gopkg.in/hugelgupf/u-root.v9@v9.0.0-20180831063832-3f6f1057f09b/pkg/uroot/builder/source.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  	"log"
    10  	"path"
    11  	"path/filepath"
    12  
    13  	"github.com/u-root/u-root/pkg/cpio"
    14  	"github.com/u-root/u-root/pkg/golang"
    15  	"github.com/u-root/u-root/pkg/uroot/initramfs"
    16  )
    17  
    18  // SourceBuilder includes full source for Go commands in the initramfs.
    19  //
    20  // SourceBuilder is an implementation of Builder.
    21  //
    22  // It also includes the Go toolchain in the initramfs, and a tool called
    23  // installcommand that can compile the other commands using symlinks.
    24  //
    25  // E.g. if "ls" is an included command, "ls" will be a symlink to
    26  // "installcommand" in the initramfs, which uses argv[0] to figure out which
    27  // command to compile.
    28  type SourceBuilder struct{}
    29  
    30  // DefaultBinaryDir implements Builder.DefaultBinaryDir.
    31  //
    32  // The initramfs default binary dir is buildbin.
    33  func (SourceBuilder) DefaultBinaryDir() string {
    34  	return "buildbin"
    35  }
    36  
    37  // Build is an implementation of Builder.Build.
    38  func (SourceBuilder) Build(af initramfs.Files, opts Opts) error {
    39  	// TODO: this is a failure to collect the correct dependencies.
    40  	if err := af.AddFile(filepath.Join(opts.Env.GOROOT, "pkg/include"), "go/pkg/include"); err != nil {
    41  		return err
    42  	}
    43  
    44  	var installcommand string
    45  	log.Printf("Collecting package files and dependencies...")
    46  	deps := make(map[string]struct{})
    47  	for _, pkg := range opts.Packages {
    48  		name := path.Base(pkg)
    49  		if name == "installcommand" {
    50  			installcommand = pkg
    51  		}
    52  
    53  		// Add high-level packages' src files to archive.
    54  		p := goListPkg(opts, pkg, &af)
    55  		if p == nil {
    56  			continue
    57  		}
    58  		for _, d := range p.Deps {
    59  			deps[d] = struct{}{}
    60  		}
    61  
    62  		// Add a symlink to installcommand. This means source mode can
    63  		// work with any init.
    64  		if err := af.AddRecord(cpio.Symlink(
    65  			path.Join(opts.BinaryDir, name),
    66  			path.Join("/", opts.BinaryDir, "installcommand"))); err != nil {
    67  			return err
    68  		}
    69  	}
    70  	if len(installcommand) == 0 {
    71  		return fmt.Errorf("must include a version of installcommand in source mode")
    72  	}
    73  
    74  	// Add src files of dependencies to archive.
    75  	for dep := range deps {
    76  		goListPkg(opts, dep, &af)
    77  	}
    78  
    79  	// Add Go toolchain.
    80  	log.Printf("Building go toolchain...")
    81  	if err := buildToolchain(opts); err != nil {
    82  		return err
    83  	}
    84  	if err := opts.Env.Build(installcommand, filepath.Join(opts.TempDir, opts.BinaryDir, "installcommand"), golang.BuildOpts{}); err != nil {
    85  		return err
    86  	}
    87  
    88  	// Add Go toolchain and installcommand to archive.
    89  	return af.AddFile(opts.TempDir, "")
    90  }
    91  
    92  // buildToolchain builds the needed Go toolchain binaries: go, compile, link,
    93  // asm.
    94  func buildToolchain(opts Opts) error {
    95  	goBin := filepath.Join(opts.TempDir, "go/bin/go")
    96  	tcbo := golang.BuildOpts{
    97  		ExtraArgs: []string{"-tags", "cmd_go_bootstrap"},
    98  	}
    99  	if err := opts.Env.Build("cmd/go", goBin, tcbo); err != nil {
   100  		return err
   101  	}
   102  
   103  	toolDir := filepath.Join(opts.TempDir, fmt.Sprintf("go/pkg/tool/%v_%v", opts.Env.GOOS, opts.Env.GOARCH))
   104  	for _, pkg := range []string{"compile", "link", "asm"} {
   105  		c := filepath.Join(toolDir, pkg)
   106  		if err := opts.Env.Build(fmt.Sprintf("cmd/%s", pkg), c, golang.BuildOpts{}); err != nil {
   107  			return err
   108  		}
   109  	}
   110  	return nil
   111  }
   112  
   113  func goListPkg(opts Opts, importPath string, out *initramfs.Files) *golang.ListPackage {
   114  	p, err := opts.Env.Deps(importPath)
   115  	if err != nil {
   116  		log.Printf("Can't list Go dependencies for %v; ignoring.", importPath)
   117  		return nil
   118  	}
   119  
   120  	// Add Go files in this package to archive.
   121  	for _, file := range append(append(p.GoFiles, p.SFiles...), p.HFiles...) {
   122  		relPath := filepath.Join("src", p.ImportPath, file)
   123  		srcFile := filepath.Join(p.Root, relPath)
   124  		if p.Goroot {
   125  			out.AddFile(srcFile, filepath.Join("go", relPath))
   126  		} else {
   127  			out.AddFile(srcFile, relPath)
   128  		}
   129  	}
   130  	return p
   131  }