github.com/zaolin/u-root@v0.0.0-20200428085104-64aaafd46c6d/u-root.go (about)

     1  // Copyright 2015-2018 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 main
     6  
     7  import (
     8  	"flag"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"log"
    12  	"os"
    13  	"runtime"
    14  	"strings"
    15  
    16  	"github.com/u-root/u-root/pkg/golang"
    17  	"github.com/u-root/u-root/pkg/uroot"
    18  	"github.com/u-root/u-root/pkg/uroot/builder"
    19  	"github.com/u-root/u-root/pkg/uroot/initramfs"
    20  )
    21  
    22  // multiFlag is used for flags that support multiple invocations, e.g. -files
    23  type multiFlag []string
    24  
    25  func (m *multiFlag) String() string {
    26  	return fmt.Sprint(*m)
    27  }
    28  
    29  func (m *multiFlag) Set(value string) error {
    30  	*m = append(*m, value)
    31  	return nil
    32  }
    33  
    34  // Flags for u-root builder.
    35  var (
    36  	build, format, tmpDir, base, outputPath *string
    37  	uinitCmd, initCmd                       *string
    38  	defaultShell                            *string
    39  	useExistingInit                         *bool
    40  	fourbins                                *bool
    41  	noCommands                              *bool
    42  	extraFiles                              multiFlag
    43  )
    44  
    45  func init() {
    46  	fourbins = flag.Bool("fourbins", false, "build installcommand on boot, no ahead of time, so we have only four binares")
    47  	build = flag.String("build", "bb", "u-root build format (e.g. bb or source).")
    48  	format = flag.String("format", "cpio", "Archival format.")
    49  
    50  	tmpDir = flag.String("tmpdir", "", "Temporary directory to put binaries in.")
    51  
    52  	base = flag.String("base", "", "Base archive to add files to. By default, this is a couple of directories like /bin, /etc, etc. u-root has a default internally supplied set of files; use base=/dev/null if you don't want any base files.")
    53  	useExistingInit = flag.Bool("useinit", false, "Use existing init from base archive (only if --base was specified).")
    54  	outputPath = flag.String("o", "", "Path to output initramfs file.")
    55  
    56  	initCmd = flag.String("initcmd", "init", "Symlink target for /init. Can be an absolute path or a u-root command name. Use initcmd=\"\" if you don't want the symlink.")
    57  	uinitCmd = flag.String("uinitcmd", "", "Symlink target for /bin/uinit. Can be an absolute path or a u-root command name. Use uinitcmd=\"\" if you don't want the symlink.")
    58  	defaultShell = flag.String("defaultsh", "elvish", "Default shell. Can be an absolute path or a u-root command name. Use defaultsh=\"\" if you don't want the symlink.")
    59  
    60  	noCommands = flag.Bool("nocmd", false, "Build no Go commands; initramfs only")
    61  
    62  	flag.Var(&extraFiles, "files", "Additional files, directories, and binaries (with their ldd dependencies) to add to archive. Can be speficified multiple times.")
    63  }
    64  
    65  func main() {
    66  	flag.Parse()
    67  
    68  	// Main is in a separate functions so defers run on return.
    69  	if err := Main(); err != nil {
    70  		log.Fatal(err)
    71  	}
    72  	log.Printf("Successfully wrote initramfs.")
    73  }
    74  
    75  var recommendedVersions = []string{
    76  	"go1.12",
    77  	"go1.13",
    78  	"go1.14",
    79  }
    80  
    81  func isRecommendedVersion(v string) bool {
    82  	for _, r := range recommendedVersions {
    83  		if strings.HasPrefix(v, r) {
    84  			return true
    85  		}
    86  	}
    87  	return false
    88  }
    89  
    90  // Main is a separate function so defers are run on return, which they wouldn't
    91  // on exit.
    92  func Main() error {
    93  	env := golang.Default()
    94  	if *fourbins && env.GOROOT == "" {
    95  		log.Fatalf("You have to set GOROOT for fourbins to work")
    96  	}
    97  	if env.CgoEnabled {
    98  		log.Printf("Disabling CGO for u-root...")
    99  		env.CgoEnabled = false
   100  	}
   101  	log.Printf("Build environment: %s", env)
   102  	if env.GOOS != "linux" {
   103  		log.Printf("GOOS is not linux. Did you mean to set GOOS=linux?")
   104  	}
   105  
   106  	v, err := env.Version()
   107  	if err != nil {
   108  		log.Printf("Could not get environment's Go version, using runtime's version: %v", err)
   109  		v = runtime.Version()
   110  	}
   111  	if !isRecommendedVersion(v) {
   112  		log.Printf(`WARNING: You are not using one of the recommended Go versions (have = %s, recommended = %v).
   113  			Some packages may not compile.
   114  			Go to https://golang.org/doc/install to find out how to install a newer version of Go,
   115  			or use https://godoc.org/golang.org/dl/%s to install an additional version of Go.`,
   116  			v, recommendedVersions, recommendedVersions[0])
   117  	}
   118  
   119  	archiver, err := initramfs.GetArchiver(*format)
   120  	if err != nil {
   121  		return err
   122  	}
   123  
   124  	logger := log.New(os.Stderr, "", log.LstdFlags)
   125  	// Open the target initramfs file.
   126  	w, err := archiver.OpenWriter(logger, *outputPath, env.GOOS, env.GOARCH)
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	var baseFile initramfs.Reader
   132  	if *base != "" {
   133  		bf, err := os.Open(*base)
   134  		if err != nil {
   135  			return err
   136  		}
   137  		defer bf.Close()
   138  		baseFile = archiver.Reader(bf)
   139  	} else {
   140  		baseFile = uroot.DefaultRamfs.Reader()
   141  	}
   142  
   143  	tempDir := *tmpDir
   144  	if tempDir == "" {
   145  		var err error
   146  		tempDir, err = ioutil.TempDir("", "u-root")
   147  		if err != nil {
   148  			return err
   149  		}
   150  		defer os.RemoveAll(tempDir)
   151  	} else if _, err := os.Stat(tempDir); os.IsNotExist(err) {
   152  		if err := os.MkdirAll(tempDir, 0755); err != nil {
   153  			return fmt.Errorf("temporary directory %q did not exist; tried to mkdir but failed: %v", tempDir, err)
   154  		}
   155  	}
   156  
   157  	var (
   158  		c           []uroot.Commands
   159  		initCommand = *initCmd
   160  	)
   161  	if !*noCommands {
   162  		var b builder.Builder
   163  		switch *build {
   164  		case "bb":
   165  			b = builder.BBBuilder{}
   166  		case "binary":
   167  			b = builder.BinaryBuilder{}
   168  		case "source":
   169  			b = builder.SourceBuilder{
   170  				FourBins: *fourbins,
   171  			}
   172  		default:
   173  			return fmt.Errorf("could not find builder %q", *build)
   174  		}
   175  
   176  		// Resolve globs into package imports.
   177  		//
   178  		// Currently allowed formats:
   179  		//   Go package imports; e.g. github.com/u-root/u-root/cmds/ls (must be in $GOPATH)
   180  		//   Paths to Go package directories; e.g. $GOPATH/src/github.com/u-root/u-root/cmds/*
   181  		var pkgs []string
   182  		for _, a := range flag.Args() {
   183  			p, ok := templates[a]
   184  			if !ok {
   185  				pkgs = append(pkgs, a)
   186  				continue
   187  			}
   188  			pkgs = append(pkgs, p...)
   189  		}
   190  		if len(pkgs) == 0 {
   191  			pkgs = []string{"github.com/u-root/u-root/cmds/core/*"}
   192  		}
   193  
   194  		if *fourbins && *build == "source" {
   195  			initCommand = "/go/bin/go"
   196  		}
   197  
   198  		// The command-line tool only allows specifying one build mode
   199  		// right now.
   200  		c = append(c, uroot.Commands{
   201  			Builder:  b,
   202  			Packages: pkgs,
   203  		})
   204  	}
   205  
   206  	opts := uroot.Opts{
   207  		Env:             env,
   208  		Commands:        c,
   209  		TempDir:         tempDir,
   210  		ExtraFiles:      extraFiles,
   211  		OutputFile:      w,
   212  		BaseArchive:     baseFile,
   213  		UseExistingInit: *useExistingInit,
   214  		InitCmd:         initCommand,
   215  		UinitCmd:        *uinitCmd,
   216  		DefaultShell:    *defaultShell,
   217  	}
   218  	return uroot.CreateInitramfs(logger, opts)
   219  }