uroot

package
Version: v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 15, 2018 License: BSD-3-Clause Imports: 24 Imported by: 123

README

Go Busybox

bb.go in this package implements a Go source-to-source transformation on pure Go code (no cgo).

This AST transformation does the following:

  • Takes a Go command's source files and rewrites them into Go package files without global side effects.
  • Writes a main.go file with a main() that calls into the appropriate Go command package based on argv[0].

This allows you to take two Go commands, such as Go implementations of sl and cowsay and compile them into one binary.

Which command is invoked is determined by argv[0] or argv[1] if argv[0] is not recognized. Let's say bb is the compiled binary; the following are equivalent invocations of sl and cowsay:

# Make a symlink sl -> bb
ln -s bb sl
./sl -l

# Make a symlink cowsay -> bb
ln -s bb cowsay
./cowsay Haha
./bb sl -l
./bb cowsay Haha

AST Transformation

Principally, the AST transformation moves all global side-effects into callable package functions. E.g. main becomes Main, each init becomes InitN, and global variable assignments are moved into their own InitN.

Then, these Main and Init functions can be registered with a global map of commands by name and used when called upon.

Let's say a command github.com/org/repo/cmds/sl contains the following main.go:

package main

import (
  "flag"
  "log"
)

var name = flag.String("name", "", "Gimme name")

func init() {
  log.Printf("init")
}

func main() {
  log.Printf("train")
}

This would be rewritten to be:

package sl // based on the directory name or bazel-rule go_binary name

import (
  "flag"
  "log"

  // This package holds the global map of commands.
  "github.com/u-root/u-root/pkg/bb"
)

// Type has to be inferred through type checking.
var name string

func Init0() {
  log.Printf("init")
}

func Init1() {
  name = flag.String("name", "", "Gimme name")
}

func Init() {
  // Order is determined by go/types.Info.InitOrder.
  Init0()
  Init1()
}

func Main() {
  log.Printf("main")
}

func init() {
  // Register `sl` as a command.
  bb.Register("sl", Init, Main)
}
Shortcomings
  • If there is already a function Main or InitN for some N, there may be a compilation error.
  • Any packages imported by commands may still have global side-effects affecting other commands. Done properly, we would have to rewrite all non-standard-library packages as well as commands. This has not been necessary to implement so far. It would likely be necessary if two different imported packages register the same flag unconditionally globally.

Generated main

The main file can be generated based on any template Go files, but the default looks something like the following:

import (
  "os"

  "github.com/u-root/u-root/pkg/bb"

  // Side-effect import registers command with bb.
  _ "github.com/org/repo/cmds/generated/sl"
)

func main() {
  bb.Run(os.Argv[0])
}

The default template will use argv[1] if argv[0] is not in the map.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var BBBuilder = Builder{
	Build:            BBBuild,
	DefaultBinaryDir: "bbin",
}
View Source
var BinaryBuilder = Builder{
	Build:            BinaryBuild,
	DefaultBinaryDir: "bin",
}
View Source
var DefaultRamfs = []cpio.Record{
	cpio.Directory("tcz", 0755),
	cpio.Directory("etc", 0755),
	cpio.Directory("dev", 0755),
	cpio.Directory("tmp", 0777),
	cpio.Directory("ubin", 0755),
	cpio.Directory("usr", 0755),
	cpio.Directory("usr/lib", 0755),
	cpio.Directory("var/log", 0777),
	cpio.Directory("lib64", 0755),
	cpio.Directory("bin", 0755),
	cpio.CharDev("dev/console", 0600, 5, 1),
	cpio.CharDev("dev/tty", 0666, 5, 0),
	cpio.CharDev("dev/null", 0666, 1, 3),
	cpio.CharDev("dev/port", 0640, 1, 4),
	cpio.CharDev("dev/urandom", 0666, 1, 9),
	cpio.StaticFile("etc/resolv.conf", nameserver, 0644),
	cpio.StaticFile("etc/localtime", gmt0, 0644),
}

DefaultRamfs are files that are contained in all u-root initramfs archives by default.

View Source
var SourceBuilder = Builder{
	Build:            SourceBuild,
	DefaultBinaryDir: "buildbin",
}

Functions

func BBBuild added in v1.0.0

func BBBuild(af ArchiveFiles, opts BuildOpts) error

BBBuild is an implementation of Build for the busybox-like u-root initramfs.

BBBuild rewrites the source files of the packages given to create one busybox-like binary containing all commands in `opts.Packages`.

func BinaryBuild added in v1.0.0

func BinaryBuild(af ArchiveFiles, opts BuildOpts) error

BinaryBuild builds all given packages as separate binaries and includes them in the archive.

func BuildBusybox added in v1.0.0

func BuildBusybox(env golang.Environ, pkgs []string, binaryPath string) error

BuildBusybox builds a busybox of the given Go packages.

pkgs is a list of Go import paths. If nil is returned, binaryPath will hold the busybox-style binary.

func CreateBBMainSource added in v1.0.0

func CreateBBMainSource(fset *token.FileSet, astp *ast.Package, pkgs []string, destDir string) error

CreateBBMainSource creates a bb Go command that imports all given pkgs.

p must be the bb template.

- For each pkg in pkgs, add

  import _ "pkg"
to astp's first file.

- Write source file out to destDir.

func CreateInitramfs

func CreateInitramfs(opts Opts) error

CreateInitramfs creates an initramfs built to `opts`' specifications.

func DefaultPackageImports added in v1.0.0

func DefaultPackageImports(env golang.Environ) ([]string, error)

DefaultPackageImports returns a list of default u-root packages to include.

func ParseAST added in v1.0.0

func ParseAST(p *build.Package) (*token.FileSet, *ast.Package, error)

ParseAST parses p's package files into an AST.

func ParseExtraFiles

func ParseExtraFiles(archive ArchiveFiles, extraFiles []string, lddDeps bool) error

ParseExtraFiles adds files from the extraFiles list to the archive, as parsed from the following formats:

- hostPath:archivePath adds the file from hostPath at the relative archivePath in the archive. - justAPath is added to the archive under justAPath.

ParseExtraFiles will also add ldd-listed dependencies if lddDeps is true.

func ResolvePackagePaths

func ResolvePackagePaths(env golang.Environ, pkgs []string) ([]string, error)

ResolvePackagePaths takes a list of Go package import paths and directories and turns them into exclusively import paths.

Currently allowed formats:

Go package imports; e.g. github.com/u-root/u-root/cmds/ls
Paths to Go package directories; e.g. $GOPATH/src/github.com/u-root/u-root/cmds/ls
Globs of package imports, e.g. github.com/u-root/u-root/cmds/*
Globs of paths to Go package directories; e.g. ./cmds/*

func RewritePackage added in v1.0.0

func RewritePackage(env golang.Environ, pkgPath, destDir, bbImportPath string, importer types.Importer) error

RewritePackage rewrites pkgPath to be bb-mode compatible, where destDir is the file system destination of the written files and bbImportPath is the Go import path of the bb package to register with.

func SourceBuild added in v1.0.0

func SourceBuild(af ArchiveFiles, opts BuildOpts) error

SourceBuild is an implementation of Build that compiles the Go toolchain (go, compile, link, asm) and an init process. It includes source files for packages listed in `opts.Packages` to build from scratch.

func WriteFile added in v1.0.0

func WriteFile(w ArchiveWriter, src, dest string) error

WriteFile takes the file at `src` on the host system and adds it to the archive `w` at path `dest`.

If `src` is a directory, its children will be added to the archive as well.

Types

type ArchiveFiles added in v1.0.0

type ArchiveFiles struct {
	// Files is a map of relative archive path -> absolute host file path.
	Files map[string]string

	// Records is a map of relative archive path -> Record to use.
	//
	// TODO: While the only archive mode is cpio, this will be a
	// cpio.Record. If or when there is another archival mode, we can add a
	// similar uroot.Record type.
	Records map[string]cpio.Record
}

ArchiveFiles are host files and records to add to the resulting initramfs.

func NewArchiveFiles added in v1.0.0

func NewArchiveFiles() ArchiveFiles

NewArchiveFiles returns a new archive files map.

func (ArchiveFiles) AddFile added in v1.0.0

func (af ArchiveFiles) AddFile(src string, dest string) error

AddFile adds a host file at `src` into the archive at `dest`.

func (ArchiveFiles) AddRecord added in v1.0.0

func (af ArchiveFiles) AddRecord(r cpio.Record) error

AddRecord adds a cpio.Record into the archive at `r.Name`.

func (ArchiveFiles) Contains added in v1.0.0

func (af ArchiveFiles) Contains(dest string) bool

Contains returns whether path `dest` is already contained in the archive.

func (ArchiveFiles) Rename added in v1.0.0

func (af ArchiveFiles) Rename(name string, newname string)

Rename renames a file in the archive.

func (ArchiveFiles) SortedKeys added in v1.0.0

func (af ArchiveFiles) SortedKeys() []string

SortedKeys returns a list of sorted paths in the archive.

func (ArchiveFiles) WriteTo added in v1.0.0

func (af ArchiveFiles) WriteTo(w ArchiveWriter) error

WriteTo writes all records and files in `af` to `w`.

type ArchiveOpts added in v1.0.0

type ArchiveOpts struct {
	// ArchiveFiles are the files to be included.
	//
	// Files in ArchiveFiles generally have priority over files in
	// DefaultRecords or BaseArchive.
	ArchiveFiles

	// DefaultRecords is a set of files to be included in the initramfs.
	DefaultRecords []cpio.Record

	// OutputFile is the file to write to.
	OutputFile ArchiveWriter

	// BaseArchive is an existing archive to add files to.
	//
	// BaseArchive may be nil.
	BaseArchive ArchiveReader

	// UseExistingInit determines whether the init from BaseArchive is used
	// or not, if BaseArchive is specified.
	//
	// If this is false, the "init" file in BaseArchive will be renamed
	// "inito" in the output archive.
	UseExistingInit bool
}

ArchiveOpts are the options for building the initramfs archive.

func (*ArchiveOpts) Write added in v1.0.0

func (opts *ArchiveOpts) Write() error

Write uses the given options to determine which files need to be written to the output file using the archive format `a` and writes them.

type ArchiveReader added in v1.0.0

type ArchiveReader cpio.RecordReader

ArchiveReader is an object that files can be read from.

type ArchiveWriter added in v1.0.0

type ArchiveWriter interface {
	cpio.RecordWriter

	// Finish finishes the archive.
	Finish() error
}

ArchiveWriter is an object that files can be written to.

type Archiver added in v1.0.0

type Archiver interface {
	// OpenWriter opens an archive writer at `path`.
	//
	// If `path` is unspecified, implementations may choose an arbitrary
	// default location, potentially based on `goos` and `goarch`.
	OpenWriter(path, goos, goarch string) (ArchiveWriter, error)

	// Reader returns an ArchiveReader wrapper using the given io.Reader.
	Reader(io.ReaderAt) ArchiveReader
}

Archiver is an archive format that builds an archive using a given set of files.

func GetArchiver added in v1.0.0

func GetArchiver(name string) (Archiver, error)

GetArchiver returns the archive mode for the named archive.

type BuildOpts added in v1.0.0

type BuildOpts struct {
	// Env is the Go environment to use to compile and link packages.
	Env golang.Environ

	// Packages are the Go package import paths to compile.
	//
	// Builders need not support resolving packages by path.
	//
	// E.g. cmd/go or github.com/u-root/u-root/cmds/ls.
	Packages []string

	// TempDir is a temporary directory where the compilation mode compiled
	// binaries can be placed.
	//
	// TempDir should contain no files.
	TempDir string

	// BinaryDir is the directory that built binaries are placed in in the
	// initramfs.
	//
	// BinaryDir must be specified.
	BinaryDir string
}

BuildOpts are arguments to the Build function.

type Builder added in v1.0.0

type Builder struct {
	// Build uses the given options to build Go packages and adds its files to be
	// included in the initramfs to the given ArchiveFiles.
	Build func(ArchiveFiles, BuildOpts) error

	// DefaultBinaryDir is the default binary directory to place built
	// binaries in.
	DefaultBinaryDir string
}

Builder uses the given options to build Go packages and adds its files to be included in the initramfs to the given ArchiveFiles.

func GetBuilder added in v1.0.0

func GetBuilder(name string) (Builder, error)

GetBuilder returns the Build function for the named build mode.

type CPIOArchiver added in v1.0.0

type CPIOArchiver struct {
	cpio.RecordFormat
}

CPIOArchiver is an implementation of Archiver for the cpio format.

func (CPIOArchiver) OpenWriter added in v1.0.0

func (ca CPIOArchiver) OpenWriter(path, goos, goarch string) (ArchiveWriter, error)

OpenWriter opens `path` as the correct file type and returns an ArchiveWriter pointing to `path`.

If `path` is empty, a default path of /tmp/initramfs.GOOS_GOARCH.cpio is used.

func (CPIOArchiver) Reader added in v1.0.0

func (ca CPIOArchiver) Reader(r io.ReaderAt) ArchiveReader

Reader implements Archiver.Reader.

type Commands

type Commands struct {
	// Builder is the build format.
	Builder Builder

	// Packages are the Go packages to add to the archive.
	//
	// Currently allowed formats:
	//   Go package imports; e.g. github.com/u-root/u-root/cmds/ls
	//   Paths to Go package directories; e.g. $GOPATH/src/github.com/u-root/u-root/cmds/ls
	//   Globs of paths to Go package directories; e.g. ./cmds/*
	Packages []string

	// BinaryDir is the directory in which the resulting binaries are
	// placed inside the initramfs.
	BinaryDir string
}

Commands specifies a list of packages to build with a specific builder.

func (Commands) TargetDir

func (c Commands) TargetDir() string

TargetDir returns the binary directory for these Commands.

type DirArchiver added in v1.0.0

type DirArchiver struct{}

DirArchiver implements Archiver for a directory.

func (DirArchiver) OpenWriter added in v1.0.0

func (da DirArchiver) OpenWriter(path, goos, goarch string) (ArchiveWriter, error)

OpenWriter implements Archiver.OpenWriter.

func (DirArchiver) Reader added in v1.0.0

func (da DirArchiver) Reader(io.ReaderAt) ArchiveReader

Reader implements Archiver.Reader.

Currently unsupported for directories.

type Opts

type Opts struct {
	// Env is the build environment (OS, arch, etc).
	Env golang.Environ

	// Commands specify packages to build using a specific builder.
	Commands []Commands

	// TempDir is a temporary directory for builders to store files in.
	TempDir string

	// Archiver is the initramfs archival format.
	//
	// Only "cpio" is currently supported.
	Archiver Archiver

	// ExtraFiles are files to add to the archive in addition to the Go
	// packages.
	//
	// Shared library dependencies will automatically also be added to the
	// archive using ldd.
	ExtraFiles []string

	// OutputFile is the archive output file.
	OutputFile ArchiveWriter

	// BaseArchive is an existing initramfs to include in the resulting
	// initramfs.
	BaseArchive ArchiveReader

	// UseExistingInit determines whether the existing init from
	// BaseArchive should be used.
	//
	// If this is false, the "init" from BaseArchive will be renamed to
	// "inito".
	UseExistingInit bool

	// InitCmd is the name of a command to link /init to.
	//
	// This can be an absolute path or the name of a command included in
	// Commands.
	//
	// If this is empty, no init symlink will be created.
	InitCmd string

	// DefaultShell is the default shell to start after init.
	//
	// This can be an absolute path or the name of a command included in
	// Commands.
	//
	// This must be specified to have a default shell.
	DefaultShell string
}

Opts are the arguments to CreateInitramfs.

type Package added in v1.0.0

type Package struct {
	// contains filtered or unexported fields
}

Package is a Go package.

It holds AST, type, file, and Go package information about a Go package.

func NewPackage added in v1.0.0

func NewPackage(name string, p *build.Package, importer types.Importer) (*Package, error)

NewPackage gathers AST, type, and token information about package p, using the given importer to resolve dependencies.

func NewPackageFromEnv added in v1.0.0

func NewPackageFromEnv(env golang.Environ, importPath string, importer types.Importer) (*Package, error)

NewPackageFromEnv finds the package identified by importPath, and gathers AST, type, and token information.

func (*Package) Rewrite added in v1.0.0

func (p *Package) Rewrite(destDir, bbImportPath string) error

Rewrite rewrites p into destDir as a bb package using bbImportPath for the bb implementation.

Directories

Path Synopsis
test
foo
Package util contains various u-root utility functions.
Package util contains various u-root utility functions.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL