github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/uroot/builder/binary.go (about)

     1  // Copyright 2015-2017 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package builder
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"path/filepath"
    11  	"sync"
    12  
    13  	gbbgolang "github.com/u-root/gobusybox/src/pkg/golang"
    14  	"github.com/mvdan/u-root-coreutils/pkg/ulog"
    15  	"github.com/mvdan/u-root-coreutils/pkg/uroot/initramfs"
    16  	"golang.org/x/tools/go/packages"
    17  )
    18  
    19  func lookupPkgs(env gbbgolang.Environ, dir string, patterns ...string) ([]*packages.Package, error) {
    20  	cfg := &packages.Config{
    21  		Mode: packages.NeedName | packages.NeedFiles,
    22  		Env:  append(os.Environ(), env.Env()...),
    23  		Dir:  dir,
    24  	}
    25  	return packages.Load(cfg, patterns...)
    26  }
    27  
    28  func dirFor(env gbbgolang.Environ, pkg string) (string, error) {
    29  	pkgs, err := lookupPkgs(env, "", pkg)
    30  	if err != nil {
    31  		return "", fmt.Errorf("failed to look up package %q: %v", pkg, err)
    32  	}
    33  
    34  	// One directory = one package in standard Go, so
    35  	// finding the first file's parent directory should
    36  	// find us the package directory.
    37  	var dir string
    38  	for _, p := range pkgs {
    39  		if len(p.GoFiles) > 0 {
    40  			dir = filepath.Dir(p.GoFiles[0])
    41  		}
    42  	}
    43  	if dir == "" {
    44  		return "", fmt.Errorf("could not find package directory for %q", pkg)
    45  	}
    46  	return dir, nil
    47  }
    48  
    49  // BinaryBuilder builds each Go command as a separate binary.
    50  //
    51  // BinaryBuilder is an implementation of Builder.
    52  type BinaryBuilder struct{}
    53  
    54  // DefaultBinaryDir implements Builder.DefaultBinaryDir.
    55  //
    56  // "bin" is the default initramfs binary directory for these binaries.
    57  func (BinaryBuilder) DefaultBinaryDir() string {
    58  	return "bin"
    59  }
    60  
    61  // Build implements Builder.Build.
    62  func (BinaryBuilder) Build(l ulog.Logger, af *initramfs.Files, opts Opts) error {
    63  	result := make(chan error, len(opts.Packages))
    64  
    65  	var wg sync.WaitGroup
    66  	for _, pkg := range opts.Packages {
    67  		wg.Add(1)
    68  		go func(p string) {
    69  			defer wg.Done()
    70  			dir, err := dirFor(opts.Env, p)
    71  			if err != nil {
    72  				result <- err
    73  				return
    74  			}
    75  			result <- opts.Env.BuildDir(
    76  				dir,
    77  				filepath.Join(opts.TempDir, opts.BinaryDir, filepath.Base(p)),
    78  				opts.BuildOpts)
    79  		}(pkg)
    80  	}
    81  
    82  	wg.Wait()
    83  	close(result)
    84  
    85  	for err := range result {
    86  		if err != nil {
    87  			return err
    88  		}
    89  	}
    90  
    91  	// Add bin directory to archive.
    92  	return af.AddFile(opts.TempDir, "")
    93  }