github.com/mem/u-root@v2.0.1-0.20181004165302-9b18b4636a33+incompatible/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 if name != "installcommand" { 63 // Add a symlink to installcommand. This means source mode can 64 // work with any init. 65 if err := af.AddRecord(cpio.Symlink( 66 path.Join(opts.BinaryDir, name), 67 path.Join("/", opts.BinaryDir, "installcommand"))); err != nil { 68 return err 69 } 70 } 71 } 72 if len(installcommand) == 0 { 73 return fmt.Errorf("must include a version of installcommand in source mode") 74 } 75 76 // Add src files of dependencies to archive. 77 for dep := range deps { 78 goListPkg(opts, dep, af) 79 } 80 81 // Add Go toolchain. 82 log.Printf("Building go toolchain...") 83 if err := buildToolchain(opts); err != nil { 84 return err 85 } 86 if err := opts.Env.Build(installcommand, filepath.Join(opts.TempDir, opts.BinaryDir, "installcommand"), golang.BuildOpts{}); err != nil { 87 return err 88 } 89 90 // Add Go toolchain and installcommand to archive. 91 return af.AddFile(opts.TempDir, "") 92 } 93 94 // buildToolchain builds the needed Go toolchain binaries: go, compile, link, 95 // asm. 96 func buildToolchain(opts Opts) error { 97 goBin := filepath.Join(opts.TempDir, "go/bin/go") 98 tcbo := golang.BuildOpts{ 99 ExtraArgs: []string{"-tags", "cmd_go_bootstrap"}, 100 } 101 if err := opts.Env.Build("cmd/go", goBin, tcbo); err != nil { 102 return err 103 } 104 105 toolDir := filepath.Join(opts.TempDir, fmt.Sprintf("go/pkg/tool/%v_%v", opts.Env.GOOS, opts.Env.GOARCH)) 106 for _, pkg := range []string{"compile", "link", "asm"} { 107 c := filepath.Join(toolDir, pkg) 108 if err := opts.Env.Build(fmt.Sprintf("cmd/%s", pkg), c, golang.BuildOpts{}); err != nil { 109 return err 110 } 111 } 112 return nil 113 } 114 115 func goListPkg(opts Opts, importPath string, out *initramfs.Files) *golang.ListPackage { 116 p, err := opts.Env.Deps(importPath) 117 if err != nil { 118 log.Printf("Can't list Go dependencies for %v; ignoring.", importPath) 119 return nil 120 } 121 122 // Add Go files in this package to archive. 123 for _, file := range append(append(p.GoFiles, p.SFiles...), p.HFiles...) { 124 relPath := filepath.Join("src", p.ImportPath, file) 125 srcFile := filepath.Join(p.Root, relPath) 126 if p.Goroot { 127 out.AddFile(srcFile, filepath.Join("go", relPath)) 128 } else { 129 out.AddFile(srcFile, relPath) 130 } 131 } 132 return p 133 }