github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/uroot/builder/gbb.go (about) 1 // Copyright 2015-2021 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 "errors" 9 "fmt" 10 "path" 11 "path/filepath" 12 13 "github.com/u-root/gobusybox/src/pkg/bb" 14 "github.com/mvdan/u-root-coreutils/pkg/cpio" 15 "github.com/mvdan/u-root-coreutils/pkg/ulog" 16 "github.com/mvdan/u-root-coreutils/pkg/uroot/initramfs" 17 ) 18 19 // Commands to skip building in bb mode. 20 var skip = map[string]struct{}{ 21 "bb": {}, 22 } 23 24 // GBBBuilder is an implementation of Builder that compiles many Go commands 25 // into one busybox-style binary. 26 // 27 // GBBBuilder will also include symlinks for each command to the busybox binary. 28 // 29 // GBBBuilder does all this by rewriting the source files of the packages given 30 // to create one busybox-like binary containing all commands. 31 // 32 // The compiled binary uses argv[0] to decide which Go command to run. 33 // 34 // See bb/README.md for a detailed explanation of the implementation of busybox 35 // mode. 36 type GBBBuilder struct { 37 // ShellBang means generate #! files instead of symlinks. 38 // ShellBang are more portable and just as efficient. 39 ShellBang bool 40 } 41 42 // DefaultBinaryDir implements Builder.DefaultBinaryDir. 43 // 44 // The default initramfs binary dir is bbin for busybox binaries. 45 func (GBBBuilder) DefaultBinaryDir() string { 46 return "bbin" 47 } 48 49 // Build is an implementation of Builder.Build for a busybox-like initramfs. 50 func (b GBBBuilder) Build(l ulog.Logger, af *initramfs.Files, opts Opts) error { 51 // Build the busybox binary. 52 if len(opts.TempDir) == 0 { 53 return fmt.Errorf("opts.TempDir is empty") 54 } 55 bbPath := filepath.Join(opts.TempDir, "bb") 56 57 if len(opts.BinaryDir) == 0 { 58 return fmt.Errorf("must specify binary directory") 59 } 60 61 bopts := &bb.Opts{ 62 Env: opts.Env, 63 GenSrcDir: opts.TempDir, 64 CommandPaths: opts.Packages, 65 BinaryPath: bbPath, 66 GoBuildOpts: opts.BuildOpts, 67 } 68 69 if err := bb.BuildBusybox(l, bopts); err != nil { 70 // Print the actual error. This may contain a suggestion for 71 // what to do, actually. 72 l.Printf("Gobusybox error: %v", err) 73 74 // Return some instructions for the user; this is printed last in the u-root tool. 75 // 76 // TODO: yeah, this isn't a good way to do error handling. The 77 // error should be the thing that's returned, I just wanted 78 // that to be printed first, and the instructions for what to 79 // do about it to be last. 80 var errGopath *bb.ErrGopathBuild 81 var errGomod *bb.ErrModuleBuild 82 if errors.As(err, &errGopath) { 83 return fmt.Errorf("preserving bb generated source directory at %s due to error. To reproduce build, `cd %s` and `GO111MODULE=off GOPATH=%s go build`: %v", opts.TempDir, errGopath.CmdDir, errGopath.GOPATH, err) 84 } else if errors.As(err, &errGomod) { 85 return fmt.Errorf("preserving bb generated source directory at %s due to error. To debug build, `cd %s` and use `go build` to build, or `go mod [why|tidy|graph]` to debug dependencies, or `go list -m all` to list all dependency versions:\n%v", opts.TempDir, errGomod.CmdDir, err) 86 } else { 87 return fmt.Errorf("preserving bb generated source directory at %s due to error:\n%v", opts.TempDir, err) 88 } 89 } 90 91 if err := af.AddFile(bbPath, "bbin/bb"); err != nil { 92 return err 93 } 94 95 // Add symlinks for included commands to initramfs. 96 for _, pkg := range opts.Packages { 97 if _, ok := skip[path.Base(pkg)]; ok { 98 continue 99 } 100 101 // Add a symlink /bbin/{cmd} -> /bbin/bb to our initramfs. 102 // Or add a #! file if b.ShellBang is set ... 103 if b.ShellBang { 104 b := path.Base(pkg) 105 if err := af.AddRecord(cpio.StaticFile(filepath.Join(opts.BinaryDir, b), "#!/bbin/bb #!"+b+"\n", 0o755)); err != nil { 106 return err 107 } 108 } else if err := af.AddRecord(cpio.Symlink(filepath.Join(opts.BinaryDir, path.Base(pkg)), "bb")); err != nil { 109 return err 110 } 111 } 112 return nil 113 }