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