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