Giter Club home page Giter Club logo

gobusybox's Introduction

Go Busybox

PkgGoDev Build Status Slack

Contact: best bet to reach us is the #u-root-dev channel on the Open Source Firmware Slack (Sign Up Link).

Go Busybox is a set of Go tools that allow you to compile many Go commands into one binary. The resulting binary uses its invocation arguments (os.Args) to determine which command is being called.

Feature Support status
Go version Tested are 1.20-1.22
Packaging Go workspaces, Go modules, Go vendoring
GOOS any (linux is tested)
GOARCH any (amd64, arm, arm64, riscv64 are tested)
CGO Not supported

An example:

go install github.com/u-root/gobusybox/src/cmd/makebb@latest

git clone github.com/u-root/u-root
cd u-root
makebb ./cmds/core/dmesg ./cmds/core/strace

A binary named bb should appear. It can be invoked in one of two ways -- either with a symlink or using a second argument.

./bb dmesg
./bb strace echo "hi"

It is meant to be used with symlinks for convenience:

# Make a symlink dmesg -> bb
ln -s bb dmesg
# Symlink means that argv[0] is the command name.
./dmesg

# Make a symlink strace -> bb
ln -s bb strace
./strace echo "hi"

Go Busybox does this by copying all the source for these Go commands and rewriting it in a temporary directory.

Go Busybox can be used with any Go commands across multiple Go modules:

mkdir workspace
cd workspace

git clone https://github.com/hugelgupf/p9
git clone https://github.com/u-root/cpu

go work init ./p9
go work use ./cpu

makebb ./cpu/cmds/* ./p9/cmd/*

Important

makebb works any time go build or go list also work.

For multi-module compilation, use Go workspaces or read below.

Path resolution & multi-module builds

makebb and the APIs mentioned below all accept commands from file system paths, Go package paths, and globs matching path.Match thereof.

makebb with globs and exclusions

In addition to the standard syntaxes supported by go list and go build, makebb also accepts globs and exclusions.

git clone https://github.com/u-root/u-root
cd u-root

makebb ./cmds/core/ip ./cmds/core/init

# Escaping the * to show that the Go APIs know how to resolve this as well
makebb ./cmds/core/\*
makebb ./cmds/core/i\* ./cmds/boot/pxeboot

# All core commands except ip.
makebb ./cmds/core/\* -./cmds/core/ip

makebb with Go workspaces & GBB_PATH.

To compile commands from multiple modules, you may use workspaces.

mkdir workspace
cd workspace

git clone https://github.com/u-root/u-root
git clone https://github.com/u-root/cpu

go work init ./u-root
go work use ./cpu

makebb \
    ./u-root/cmds/core/init \
    ./u-root/cmds/core/elvish \
    ./cpu/cmds/cpud

# Also works for offline builds with `go work vendor` (Go 1.22 feature):
go work vendor

makebb \
    ./u-root/cmds/core/init \
    ./u-root/cmds/core/elvish \
    ./cpu/cmds/cpud

For a shortcut to specify many commands sharing common path elements (e.g. from the same repository), the GBB_PATH environment variable exists. Paths are concatenated with every colon-separated element of GBB_PATH from left to right and checked for existence.

GBB_PATH=$(pwd)/u-root:$(pwd)/cpu makebb \
    cmds/core/init \
    cmds/core/elvish \
    cmds/cpud

# matches:
#   $(pwd)/u-root/cmds/core/init
#   $(pwd)/u-root/cmds/core/elvish
#   $(pwd)/cpu/cmds/cpud

goanywhere

go install github.com/u-root/gobusybox/src/cmd/goanywhere@latest

goanywhere creates a Go workspace temporarily on the fly from the packages' modules in the given local file paths.

goanywhere then executes the command given after "--" in the workspace directory and amends the packages as args.

For example,

# -o $(pwd) is needed since goanywhere executes the command in the workspace
# directory, and by default the Go binary is created in the current wd.
goanywhere ./u-root/cmds/core/{init,gosh} -- go build -o $(pwd)
goanywhere ./u-root/cmds/core/{init,gosh} -- makebb -o $(pwd)
goanywhere ./u-root/cmds/core/{init,gosh} -- mkuimage [other args]
goanywhere ./u-root/cmds/core/{init,gosh} -- u-root [other args]

Like makebb, goanywhere supports GBB_PATH, globs and shell expansions.

GBB_PATH=$(pwd)/u-root:$(pwd)/cpu goanywhere \
    cmds/core/{init,gosh} cmds/cpud -- makebb -o $(pwd)

makebb with multiple Go modules

makebb supports Go workspaces for locally checked out sources, as shown above.

For multiple Go module command dependencies that aren't checked out locally, we apply some Go module tricks.

To depend on commands outside of ones own repository, the easiest way to depend on Go commands is the following:

TMPDIR=$(mktemp -d)
cd $TMPDIR
go mod init foobar

Create a file with some unused build tag like this to create dependencies on commands:

//go:build tools

package something

import (
        _ "github.com/u-root/u-root/cmds/core/ip"
        _ "github.com/u-root/u-root/cmds/core/init"
        _ "github.com/hugelgupf/p9/cmd/p9ufs"
)

You can generate this file for your repo with the gencmddeps tool:

go install github.com/u-root/gobusybox/src/cmd/gencmddeps@latest

gencmddeps -o deps.go -t tools -p something \
    github.com/u-root/u-root/cmds/core/{ip,init} \
    github.com/hugelgupf/p9/cmd/p9ufs

Important

gencmddeps does not support file paths or exclusions, as these rely on a go.mod already being present to resolve the Go package name.

The input to gencmddeps must be full Go package paths.

The unused build tag keeps it from being compiled, but its existence forces go mod to add these dependencies to go.mod:

go mod tidy

makebb \
  github.com/u-root/u-root/cmds/core/ip \
  github.com/u-root/u-root/cmds/core/init \
  github.com/hugelgupf/p9/cmd/p9ufs

# Also works with vendored, offline builds:
go mod vendor

makebb \
  github.com/u-root/u-root/cmds/core/ip \
  github.com/u-root/u-root/cmds/core/init \
  github.com/hugelgupf/p9/cmd/p9ufs

APIs

Besides the makebb CLI command, there is a Go API at src/pkg/bb.

Shortcomings

  • Any imported packages' init functions are run for every command.

    For example, if some command imports and uses the testing package, all commands in the busybox will have testing's flags registered as a side effect, because testing's init function runs with every command.

    While Go busybox handles every main commands' init functions, it does not handle dependencies' init functions. Done properly, it would 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, for example, two different imported packages register the same flag unconditionally globally.

How It Works

src/pkg/bb 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 (almost) without global side effects.
  • Writes a main.go file with a main() that calls into the appropriate Go command package based on argv[0] or argv[1].

This allows you to take two Go commands, such as Go implementations of dmesg and strace 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 dmesg and strace:

(cd ./src/cmd/makebb && go install)
(cd ./test/nested && makebb ./cmd/dmesg ./cmd/strace)

# Make a symlink dmesg -> bb
ln -s bb dmesg
./dmesg

# Make a symlink strace -> bb
ln -s bb strace
./strace echo "hi"
./bb dmesg
./bb strace echo "hi"

Command Transformation

Principally, the AST transformation moves all global side-effects into callable package functions. E.g. main becomes registeredMain, each init becomes initN, and global variable assignments are moved into their own initN. A registeredInit calls each initN function in the correct init order.

Then, these registeredMain and registeredInit 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 %s", *name)
}

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

This would be rewritten to be:

package sl // based on the directory name

import (
  "flag"
  "log"

  "../bb/pkg/bbmain" // generated import path
)

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

func init0() {
  log.Printf("init %s", *name)
}

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

func registeredInit() {
  // Order is determined by go/types.Info.InitOrder.
  init1()
  init0()
}

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

func init() {
  bbmain.Register("sl", registeredInit, registeredMain)
}

Generated main.go

The main.go file is generated from ./src/pkg/bb/bbmain/cmd/main.go.

package main

import (
  "os"
  "log"
  "path/filepath"

  // Side-effect import so init in sl calls bbmain.Register
  _ "github.com/org/repo/cmds/sl"

  "../bb/pkg/bbmain"
)

func main() {
  bbmain.Run(filepath.Base(os.Argv[0]))
}

Directory Structure

All files are written into a temporary directory. All dependency Go packages are also written there.

The directory structure we generate resembles a $GOPATH-based source tree, even if we are combining module-based Go commands. Regardless of whether the original commands are based on Go modules, Go workspaces, or GOPATH, we generate the same structure and compiled with GOPATH=$tmpdir GO111MODULE=off.

This means that in all cases, traditionally offline compilations remain offline (e.g. GOPATH, or vendored modules / workspaces).

/tmp/bb-$NUM/
└── src
    ├── bb.u-root.com
    │   └── bb
    │       ├── main.go               << ./src/pkg/bb/bbmain/cmd/main.go (with edits)
    │       └── pkg
    │           └── bbmain
    │               └── register.go   << ./src/pkg/bb/bbmain/register.go
    └── github.com
        └── u-root
            ├── uio
            │   ├── uio               << dependency used by both
            │   └── ulog              << dependency used by both
            ├── u-bmc
            │   ├── cmd
            │   │   ├── fan           << generated command package
            │   │   ├── login         << generated command package
            │   │   └── socreset      << generated command package
            │   └── pkg
            │       ├── acme          << dependency copied from u-bmc
            │       ├── aspeed        << dependency copied from u-bmc
            │       ├── gpiowatcher   << dependency copied from u-bmc
            │       └── mtd           << dependency copied from u-bmc
            └── u-root
                ├── cmds
                │   └── core
                │       ├── cat       << generated command package
                │       ├── ip        << generated command package
                │       └── ls        << generated command package
                └── pkg
                    ├── curl          << dependency copied from u-root
                    ├── dhclient      << dependency copied from u-root
                    ├── ip            << dependency copied from u-root
                    ├── ls            << dependency copied from u-root
                    └── uio           << dependency copied from u-root

gobusybox's People

Contributors

10ne1 avatar hugelgupf avatar leongross avatar orangecms avatar prabhuk avatar rminnich avatar rminnichcodeu avatar walterchris avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gobusybox's Issues

Return explicit error if user tries to combine vendor-based and module-based Go commands

The Go compiler does not support mixing vendor-based and module-based combination for the compilation of one binary. Hence, we need to explicitly tell users that this is not possible anymore.

Currently, we know which commands are module-based and which ones aren't, but we proceed to try to compile them together anyway. Sometimes this works when GO111MODULE=off if and only if all dependencies of the Go-module-based commands also happen to be in $GOPATH.

Better error messages for dependency conflicts

Conflicts 2 - 4 from this section in the README generate error messages from the bb implementation itself in the localModules function.

The output is still a bit... lacking:

:: ./makebb test/mod5/cmd/mod5hello test/mod6/cmd/mod6hello
2020/09/07 05:40:48 Disabling CGO for u-root...
2020/09/07 05:40:48 Build environment: GOARCH=amd64 GOOS=linux GOPATH=/usr/local/google/home/chrisko/go CGO_ENABLED=0 GO111MODULE= GOROOT=/usr/lib/google-golang PATH=/usr/lib/google-golang/bin:$PATH

2020/09/07 05:40:51 Conflicting module dependencies on github.com/u-root/gobusybox/test/mod6:
2020/09/07 05:40:51   github.com/u-root/gobusybox/test/mod5 uses version v0.0.0-20200907120712-30940fe2554f
2020/09/07 05:40:51   your request to compile github.com/u-root/gobusybox/test/mod6 from /usr/local/google/home/chrisko/go/src/github.com/u-root/gobusybox/test/mod6 uses version

2020/09/07 05:40:51 Suggestion to resolve: add `replace github.com/u-root/gobusybox/test/mod6 => ../mod6` to /usr/local/google/home/chrisko/go/src/github.com/u-root/gobusybox/test/mod5/go.mod

Improve these error messages.

u-root-proper compatible pathfinding

Currently, makebb supports file system paths as well as Go command paths. (It accepts everything that go list -json would accept.)

u-root also allows Go command paths with globs and {} selectors, like github.com/u-root/u-root/cmds/core/* or github.com/u-root/u-root/cmds/core/{ip,init,dhclient} and some others. See here.

We should support these here, too, or explicitly only support local file system paths for compilation.

Add support for combining commands from unrelated Go modules

I attempted to build a bb command for the Carvel toolchain, which is a set of independent git repositories, each with a different Go module.

I have a top-level go module where I pull in the vendor dependencies using a tools.go file:

$ cat go.mod
module gitlab.eng.vmware.com/jpeach/carvel

go 1.16

require (
	github.com/k14s/imgpkg v0.5.0
	github.com/k14s/ytt v0.31.0
	github.com/u-root/gobusybox/src v0.0.0-20200923191651-a55b339949b0 // indirect
	github.com/vmware-tanzu/carvel-vendir v0.17.0
	golang.org/x/mod v0.4.2 // indirect
	golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 // indirect
	golang.org/x/tools v0.1.0 // indirect
)
// +build tools

package tools

import (
	_ "github.com/k14s/imgpkg/cmd/imgpkg"
	_ "github.com/k14s/kbld/cmd/kbld"
	_ "github.com/k14s/ytt/cmd/ytt"

	_ "github.com/u-root/gobusybox/src/cmd/makebb"
)

When I run makebb, the source appears to be rewritten correctly, but the modfile in the bb command doesn't contains the necessary requires directives.

$ go run github.com/u-root/gobusybox/src/cmd/makebb github.com/k14s/imgpkg/cmd/imgpkg github.com/k14s/ytt/cmd/ytt
2021/03/23 07:23:51 Disabling CGO for u-root...
2021/03/23 07:23:51 Build environment: GOARCH=amd64 GOOS=darwin GOPATH=/Users/jpeach/go CGO_ENABLED=0 GO111MODULE= GOROOT=/usr/local/Cellar/go/1.16.2/libexec PATH=/usr/local/Cellar/go/1.16.2/libexec/bin:$PATH
2021/03/23 07:23:53 go build with modules failed: error building go package in "/var/folders/d9/tpzrvxbs7s98zqy4c5yq8bk40000gr/T/bb-324558004/src/bb.u-root.com/bb": main.go:14:2: module github.com/k14s/imgpkg provides package github.com/k14s/imgpkg/cmd/imgpkg and is replaced but not required; to add it:
	go get github.com/k14s/imgpkg
main.go:15:2: module github.com/k14s/ytt provides package github.com/k14s/ytt/cmd/ytt and is replaced but not required; to add it:
	go get github.com/k14s/ytt
, exit status 1
2021/03/23 07:23:53 Preserving bb generated source directory at /var/folders/d9/tpzrvxbs7s98zqy4c5yq8bk40000gr/T/bb-324558004 due to error. To debug build, `cd /var/folders/d9/tpzrvxbs7s98zqy4c5yq8bk40000gr/T/bb-324558004/src/bb.u-root.com/bb` 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.
exit status 1

The generated bb source references the packages correctly:

$ cat /var/folders/d9/tpzrvxbs7s98zqy4c5yq8bk40000gr/T/bb-324558004/src/bb.u-root.com/bb/main.go
// Copyright 2018 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package main is the busybox main.go template.
package main

import (
	"log"
	"os"
	"path/filepath"

	"bb.u-root.com/bb/pkg/bbmain"
	_ "github.com/k14s/imgpkg/cmd/imgpkg"
	_ "github.com/k14s/ytt/cmd/ytt"
	// There MUST NOT be any other dependencies here.
	//
	// It is preferred to copy minimal code necessary into this file, as
	// dependency management for this main file is... hard.
)
...

The source for each command package looks correctly rewritten and calls bbmain.Register.

But the go.mod only contains the replacements, not the requires, which are are also necessary because the bb command is consuming vendor packages.

$ cat /var/folders/d9/tpzrvxbs7s98zqy4c5yq8bk40000gr/T/bb-324558004/src/bb.u-root.com/bb/go.mod
module bb.u-root.com/bb
replace github.com/k14s/imgpkg => ../../github.com/k14s/imgpkg

replace github.com/k14s/ytt => ../../github.com/k14s/ytt

I tried working around this with some makefile hackery (i.e. running go get manually to add the requires). This seems to work.

Test Generics & workspaces

We should start testing gobusybox with Go 1.18, which is being readied for release some time this month.

New features we have to pay attention to:

  • Generics,
  • Go workspaces,
  • debug/buildinfo

Generics

One of the features of gobusybox is that global variables must be rewritten if they implicitly are initialization statements. For example,

var foo = flag.Uint64("something", 100, "haha")

must be rewritten to

var foo *string

// Some function that can be called if this command is selected by argv[0]
func Init() {
  foo = flag.Uint64(...)
}

One of the things that we must do here is derive the return type of flag.String, which is where paying attention to generics comes in. I don't know Go's generics syntax well yet, but I assume that one can do something like

func NumberFlag[T int64 | int32 | int16 | int8](name string, value T, usage string) *T {
  ..
}

var foo = NumberFlag[int64]("something", -100, "blabla")

and it has to be rewritten to

// This will always be a concrete type, and never contain type parameters.
var foo *int64

func Init() {
  foo = NumberFlag[int64]("something", -100, "blabla")
}

All this happens here in the gobusybox codebase:

for _, spec := range d.Specs {
s := spec.(*ast.ValueSpec)
if s.Values == nil {
continue
}
// For each assignment, create a new init
// function, and place it in the same file.
for i, name := range s.Names {
varInit := &ast.FuncDecl{
Name: p.nextInit(false),
Type: &ast.FuncType{
Params: &ast.FieldList{},
Results: nil,
},
Body: &ast.BlockStmt{
List: []ast.Stmt{
&ast.AssignStmt{
Lhs: []ast.Expr{name},
Tok: token.ASSIGN,
Rhs: []ast.Expr{s.Values[i]},
},
},
},
}
// Add a call to the new init func to
// this map, so they can be added to
// Init0() in the correct init order
// later.
p.initAssigns[s.Values[i]] = &ast.ExprStmt{X: &ast.CallExpr{Fun: varInit.Name}}
f.Decls = append(f.Decls, varInit)
}
// Add the type of the expression to the global
// declaration instead.
if s.Type == nil {
typ := p.Pkg.TypesInfo.Types[s.Values[0]]
s.Type = ast.NewIdent(types.TypeString(typ.Type, qualifier))
}
s.Values = nil
}

But specifically, the type declaration of the global variable is here:

s.Type = ast.NewIdent(types.TypeString(typ.Type, qualifier))

We need to verify that all this still works as expected with generics, or find out what needs to be modified. The go/types did gain support for generics in Go 1.18. See proposal. But hopefully it just works.

Workspaces

Workspaces may mess with how we figure out the main module go.mod and how we synthesize the new directory structure. To be honest, I won't know what's wrong until I try it out, so that's what I suggest: learn about go workspaces and try them out, see how they work, then put a few different modules in a workspace together and see if you can compile them using gobusybox (but I'd avoid u-root at first)

debug/buildinfo

Go binaries will as of Go 1.18 contain a bunch of info about how they were built. If you read the draft release notes and search for "buildinfo" you'll find

Additionally, the go command embeds information about the build including build and tool tags (set with -tags), compiler, assembler, and linker flags (like -gcflags), whether cgo was enabled, and if it was, the values of the cgo environment variables (like CGO_CFLAGS). Both VCS and build information may be read together with module information using go version -m file or runtime/debug.ReadBuildInfo (for the currently running binary) or the new debug/buildinfo package.

This sounds kind of awesome, but it will make binaries produced by gobusybox not reproducible. We should add a bool (or a couple, I don't know what knobs the Go compiler has) to golang.BuildOpts for users to be able to enable/disable this feature and add corresponding flags that can be used in makebb and u-root commands.

Dependency error on u-root's cmds/core/backoff

I'm attempting to migrate u-root scripts and delete the old GO111MODULE=off (we used to use GO111MODULE=off go get ... to get u-root sources), but I'm getting dependency failures. Below is a script that I would expect to work, and which I hope you can use to reproduce the problem:

#! /bin/sh

set -e

GOPATH=${HOME}/tmp/go
export GOPATH

if [ -d "${GOPATH}/pkg/mod" ] ; then chmod u+w -R "${GOPATH}/pkg/mod"; fi
rm -rf ${GOPATH}

UROOT_MOD=github.com/u-root/[email protected]

go mod download -x ${UROOT_MOD}

UROOT_SRC=$(go env GOMODCACHE)/${UROOT_MOD}
# Attempt to also download u-root deps
(cd ${UROOT_SRC} && go mod download -x)

go run ${UROOT_MOD} -build=bb -uroot-source=${UROOT_SRC} -o ${HOME}/tmp/initrd core

When running this, it downloads a bunch of modules, in particular, I do get a directory ${HOME}/tmp/go/pkg/mod/github.com/cenkalti/backoff/[email protected]/. But the u-root invocation fails, with this build error:

09:47:45 Build environment: GOARCH=amd64 GOOS=linux GOPATH=/home/nisse/tmp/go CGO_ENABLED=0 GO111MODULE= GOROOT=/usr/lib/go-1.19 PATH=/usr/lib/go-1.19/bin:$PATH
09:47:45 NOTE: building with the new gobusybox; to get the old behavior check out commit 8b790de
09:47:45 Skipping package github.com/u-root/u-root/cmds/core/bind because build constraints exclude all Go files
09:47:45 Gobusybox error: finding packages failed: could not load packages from file system: failed to add package github.com/u-root/u-root/cmds/core/backoff for errors: 3 errors occurred:
        * /home/nisse/tmp/go/pkg/mod/github.com/u-root/[email protected]/cmds/core/backoff/run.go:12:2: could not import github.com/cenkalti/backoff/v4 (invalid package name: "")
        * /home/nisse/tmp/go/pkg/mod/github.com/u-root/[email protected]/cmds/core/backoff/run.go:23:7: undeclared name: backoff
        * /home/nisse/tmp/go/pkg/mod/github.com/u-root/[email protected]/cmds/core/backoff/run.go:40:9: undeclared name: backoff

09:47:45 Build error: error building: preserving bb generated source directory at /tmp/u-root3330130979/builder1707272484 due to error:
finding packages failed: could not load packages from file system: failed to add package github.com/u-root/u-root/cmds/core/backoff for errors: 3 errors occurred:
        * /home/nisse/tmp/go/pkg/mod/github.com/u-root/[email protected]/cmds/core/backoff/run.go:12:2: could not import github.com/cenkalti/backoff/v4 (invalid package name: "")
        * /home/nisse/tmp/go/pkg/mod/github.com/u-root/[email protected]/cmds/core/backoff/run.go:23:7: undeclared name: backoff
        * /home/nisse/tmp/go/pkg/mod/github.com/u-root/[email protected]/cmds/core/backoff/run.go:40:9: undeclared name: backoff

exit status 1

Of these error messages, I find invalid package name: "" particularly confusing, and I guess that's from the go toolchain, not gobusybox or u-root? Also preserving bb generated source directory at /tmp/u-root3330130979/builder1707272484 seems wrong, there's no such directory after u-root is finished, so I can't easily look at the generated source dir.

I need some advice on what to do, in order to get u-root's build to locate the github.com/cenkalti/backoff/v4 dependency.

Test failed: test_external - 12-fancy-cmd

When running ./test.sh I reach this point.

12-fancy-cmd errors on test_external.sh.
I understand the error messages, but I dont understand why this dependency is missing.

cd ./test/12-fancy-cmd
+ ./test.sh
+ cd ../../src/cmd/makebb
+ GO111MODULE=on
+ go build .
+ MAKEBB=../../src/cmd/makebb/makebb
+ for GO111MODULE in on auto
+ GO111MODULE=on
+ ../../src/cmd/makebb/makebb -o bb-on .
2021/09/02 07:40:59 Disabling CGO for u-root...
2021/09/02 07:40:59 Build environment: GOARCH=amd64 GOOS=linux GOPATH=/home/riot/go CGO_ENABLED=0 GO111MODULE=on GOROOT=/snap/go/7736 PATH=/snap/go/7736/bin:$PATH
+ test -f ./bb-on
++ ./bb-on 12-fancy-cmd
+ CMD=12-fancy-cmd
+ test 12-fancy-cmd == 12-fancy-cmd
+ for GO111MODULE in on auto
+ GO111MODULE=auto
+ ../../src/cmd/makebb/makebb -o bb-auto .
2021/09/02 07:41:02 Disabling CGO for u-root...
2021/09/02 07:41:02 Build environment: GOARCH=amd64 GOOS=linux GOPATH=/home/riot/go CGO_ENABLED=0 GO111MODULE=auto GOROOT=/snap/go/7736 PATH=/snap/go/7736/bin:$PATH
+ test -f ./bb-auto
++ ./bb-auto 12-fancy-cmd
+ CMD=12-fancy-cmd
+ test 12-fancy-cmd == 12-fancy-cmd
+ ./test-external.sh
+ '[' -z '' ']'
+ GO=go
+ cd src/cmd/makebb
+ go generate
+ go build
+ cd ../../..
++ mktemp -d
+ TMPDIR=/tmp/tmp.CWhiXNauTa
++ mktemp -d
+ EMPTY_TMPDIR=/tmp/tmp.Suin3b4wYI
+ trap ctrl_c INT
+ cd /tmp/tmp.CWhiXNauTa
+ git clone --branch circ-dep https://github.com/hugelgupf/u-root

...

2021/09/02 07:41:52 Disabling CGO for u-root...
2021/09/02 07:41:52 Build environment: GOARCH=amd64 GOOS=linux GOPATH=/tmp/tmp.Suin3b4wYI CGO_ENABLED=0 GO111MODULE=on GOROOT=/snap/go/7736 PATH=/snap/go/7736/bin:$PATH
2021/09/02 07:41:52 Skipping directory /tmp/tmp.CWhiXNauTa/u-root/cmds/core/bind because build constraints exclude all Go files
2021/09/02 07:41:59 go build with modules failed: error building go package in "/tmp/bb-270759302/src/bb.u-root.com/bb": ../../github.com/u-root/u-root/cmds/exp/cbmem/main_linux.go:23:2: missing go.sum entry for module providing package golang.org/x/text/language (imported by github.com/u-root/u-root/cmds/exp/cbmem); to add:
	go get github.com/u-root/u-root/cmds/exp/[email protected]
../../github.com/u-root/u-root/cmds/exp/cbmem/main_linux.go:24:2: missing go.sum entry for module providing package golang.org/x/text/message (imported by github.com/u-root/u-root/cmds/exp/cbmem); to add:
	go get github.com/u-root/u-root/cmds/exp/[email protected]
../../github.com/u-root/u-root/cmds/boot/localboot/grub.go:17:2: missing go.sum entry for module providing package golang.org/x/text/transform (imported by github.com/u-root/u-root/cmds/boot/localboot); to add:
	go get github.com/u-root/u-root/cmds/boot/[email protected]
../../github.com/u-root/u-root/cmds/boot/localboot/grub.go:18:2: missing go.sum entry for module providing package golang.org/x/text/unicode/norm (imported by github.com/u-root/u-root/cmds/boot/localboot); to add:
	go get github.com/u-root/u-root/cmds/boot/[email protected]
, exit status 1
2021/09/02 07:41:59 Preserving bb generated source directory at /tmp/bb-270759302 due to error. To debug build, `cd /tmp/bb-270759302/src/bb.u-root.com/bb` 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.

Fix go_busybox's cmds field

the go_busybox rule cmds field currently requires each target to be listed individually (no aggregators like :... or :all) and requires each target to be an absolute label (no :foobar labels).

This is due to the fact that go_busybox is currently a macro that expands to many rules:

def go_busybox(name, cmds = [], **kwargs):

That's like that for 2 reasons, today:

  1. every rule can only generate one go.archive at the moment because they will all be named "%s.a" % ctx.attr.name. That is fixed with bazelbuild/rules_go#2652, but not released in rules_go yet.

  2. go_busybox is a rule that generates many GoLibrary rules (one for each cmd), and then one GoBinary that depends on all those GoLibrarys (the busybox command). The deps field of the GoSource provider is technically required to be a list of Target (which each provide GoArchive). If we consolidated all those macro-generated rules into one rule, GoSource.deps would have to depend on a list of Target or GoArchive, because there would be no targets to depend on for the individual command libraries. In practice, GoSource.deps does accept list of Target or GoArchive right now, but rules_go wants to drop that ability. We'll need to convince them not to, or ask what it's about.

See actions.archive's resolution of deps calling get_archive (the "type == struct" thing is for when a GoArchive is directly supplied).

In particular, see this comment saying

TODO(#1784): we allow deps to be a list of GoArchive since go_test and nogo work this way. We should force deps to be a list of Targets.

Refers to #1784, which I suppose I'll comment on.

Re-enable duplicate command checking

BuildBusybox should be checking whether some command names are duplicated before attempting to compile a busybox. You don't want a busybox with two dmesg commands, where you can't tell which one has been run.

Fix global naming collisions

A rewrite of a package done by gobusybox introduces new names to file-level scope of Go command files, as seen in the command transformation section of the README. Names like Main, Init, Init0, Init1, InitN... may already be used, and if used, should be avoided.

Additionally, #5 adds the possibility of adding new import statements to each rewritten file, which adds the package name to the file-level namespace. If that name is already used, the import needs an alias that isn't already used.

E.g.

package foobar

import (
  "somecrazylogger"
)

var log = somecrazylogger.Logger()

would be rewritten as

package foobar

import (
  "log"
  "somecrazylogger"
)

var log = *log.Logger

func Init() {
  log = somecrazylogger.Logger()
}

and now log is in the file-level namespace twice -- once as a variable, once as a package name.

This currently prevents compilation of https://github.com/gokrazy/gokrazy/blob/master/cmd/randomd/ (besides #5)

Command names may be invalid package names

Here's what I did:

 ~/Projects $ git clone [email protected]:orangecms/wsd-cuse
Cloning into 'wsd-cuse'...
remote: Enumerating objects: 10, done.
remote: Counting objects: 100% (10/10), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 10 (delta 0), reused 7 (delta 0), pack-reused 0
Receiving objects: 100% (10/10), done.
 ~/Projects $ cd wsd-cuse/
 ~/P/wsd-cuse $ ll
total 16K
-rw-r--r-- 1 dan dan   46 Sep 21 07:14 go.mod
-rw-r--r-- 1 dan dan 1.1K Sep 21 07:14 LICENSE
-rw-r--r-- 1 dan dan  903 Sep 21 07:14 main.go
drwxr-xr-x 3 dan dan 4.0K Sep 21 07:14 pkg/
 ~/P/wsd-cuse $ ~/Projects/gobusybox/src/cmd/makebb/makebb .
2020/09/21 07:14:45 Disabling CGO for u-root...
2020/09/21 07:14:45 Build environment: GOARCH=amd64 GOOS=linux GOPATH=/home/dan/go CGO_ENABLED=0 GO111MODULE= GOROOT=/usr/lib/go PATH=/usr/lib/go/bin:$PATH
2020/09/21 07:14:47 Preserving bb generated source directory at /tmp/bb-287669195 due to error
2020/09/21 07:14:47 rewriting command "github.com/orangecms/wsd-cuse" failed: error formatting Go file "/tmp/bb-287669195/src/github.com/orangecms/wsd-cuse/main.go": format.Node internal error (4:12: expected ';', found '-')
~/P/wsd-cuse $ ll
total 16K
-rw-r--r-- 1 dan dan   46 Sep 21 07:14 go.mod
-rw-r--r-- 1 dan dan 1.1K Sep 21 07:14 LICENSE
-rw-r--r-- 1 dan dan  903 Sep 21 07:14 main.go
drwxr-xr-x 3 dan dan 4.0K Sep 21 07:14 pkg/
 ~/P/wsd-cuse $ go build .
 ~/P/wsd-cuse $ ll
total 2.9M
-rw-r--r-- 1 dan dan   46 Sep 21 07:14 go.mod
-rw-r--r-- 1 dan dan 1.1K Sep 21 07:14 LICENSE
-rw-r--r-- 1 dan dan  903 Sep 21 07:14 main.go
drwxr-xr-x 3 dan dan 4.0K Sep 21 07:14 pkg/
-rwxr-xr-x 1 dan dan 2.8M Sep 21 07:15 wsd-cuse*

Looking at the temp directory, I see only directories, no files to look into:

 ~/P/wsd-cuse $ ll -R /tmp/bb-287669195/
/tmp/bb-287669195/:
total 0
drwxr-xr-x 4 dan dan 80 Sep 21 07:14 src/

/tmp/bb-287669195/src:
total 0
drwxr-xr-x 3 dan dan 60 Sep 21 07:14 bb.u-root.com/
drwxr-xr-x 3 dan dan 60 Sep 21 07:14 github.com/

/tmp/bb-287669195/src/bb.u-root.com:
total 0
drwxr-xr-x 2 dan dan 40 Sep 21 07:14 bb/

/tmp/bb-287669195/src/bb.u-root.com/bb:
total 0

/tmp/bb-287669195/src/github.com:
total 0
drwxr-xr-x 3 dan dan 60 Sep 21 07:14 orangecms/

/tmp/bb-287669195/src/github.com/orangecms:
total 0
drwxr-xr-x 2 dan dan 40 Sep 21 07:14 wsd-cuse/

/tmp/bb-287669195/src/github.com/orangecms/wsd-cuse:
total 0

how add go build flags to compile our modules ?

Hello,
at this time we are using : go build -ldflags "-X 'main.BuildTime=$(date)'" when we want to compile our prog.
Now we want to use gobusybox to assemble all our little program but we how reproduce the same ,
makebb -go-extra-args seems to be not working as we expected.

Regards,
Nicolas

findpkg: if we drop support for Go 1.13, can this code be removed?

If we drop support for Go 1.13, can this code be removed?

There's a comment here I wrote a long time ago which refers to a section of code that only exists because of Go 1.13: Lines 172-205

However, this could still be useful because we emit these skip notes as part of the build.

If I remember correctly, when you look up a package that doesn't fit any build criteria through packages.Load on Go >=1.14, you'll get a similar error message. Maybe we can delete the code and emit a similar error message in those cases? I think this needs some experimentation.

(E.g. I don't think we want to turn those warnings into errors, which would happen if we just delete the code.)

permission denied writing to go file

Reproducer:

#!/bin/bash

set -eu

cd `mktemp -d`

echo "running in $PWD"

go mod init temp

cat <<EOF > tools.go
//go:build tools

package tools

import (
"github.com/golangci/golangci-lint/cmd/golangci-lint"
"golang.org/x/tools/cmd/goimports"
)
EOF


for import in $@; do
  go get "github.com/golangci/golangci-lint/cmd/[email protected]"
  go get "golang.org/x/tools/cmd/[email protected]"
done
go mod tidy

makebb "github.com/golangci/golangci-lint/cmd/golangci-lint" "golang.org/x/tools/cmd/goimports"

Error:

running in /tmp/tmp.j7xyPHNdpI
go: creating new go.mod: module temp
go: finding module for package golang.org/x/tools/cmd/goimports
go: finding module for package github.com/golangci/golangci-lint/cmd/golangci-lint
go: found github.com/golangci/golangci-lint/cmd/golangci-lint in github.com/golangci/golangci-lint v1.52.2
go: found golang.org/x/tools/cmd/goimports in golang.org/x/tools v0.9.1
15:14:04 Disabling CGO for u-root...
15:14:04 Build environment: GOARCH=amd64 GOOS=linux GOPATH=/usr/local/google/home/howardjohn/go CGO_ENABLED=0 GO111MODULE=on GOROOT=/usr/local/google/home/howardjohn/sdk/go1.20 PATH=/usr/local/google/home/howardjohn/sdk/go1.20/bin:$PATH
15:14:07 collecting and putting dependencies in place failed: writing package golang.org/x/tools/go/analysis/passes/assign failed: error writing Go file to "/tmp/bb-1433136968/src/golang.org/x/tools/go/analysis/passes/assign/doc.go": open /tmp/bb-1433136968/src/golang.org/x/tools/go/analysis/passes/assign/doc.go: permission denied
15:14:07 Preserving bb generated source directory at /tmp/bb-1433136968 due to error.

For some reason the doc.go file has 444 permissions, not 644. I am not sure why this happens.

I made a small fork that just forces all files to 644 to workaround this and I got another error:

$ /tmp/tmp.pxgPd7CDTG/gobusybox/src/makebb "github.com/golangci/golangci-lint/cmd/golangci-lint" "golang.org/x/tools/cmd/goimports"
15:15:58 Disabling CGO for u-root...
15:15:58 Build environment: GOARCH=amd64 GOOS=linux GOPATH=/usr/local/google/home/howardjohn/go CGO_ENABLED=0 GO111MODULE=on GOROOT=/usr/local/google/home/howardjohn/sdk/go1.20 PATH=/usr/local/google/home/howardjohn/sdk/go1.20/bin:$PATH
15:16:02 running `go mod tidy` on the generated busybox main package failed (exit status 1): go: finding module for package golang.org/x/tools/go/packages/packagestest
go: finding module for package golang.org/x/tools/go/expect
go: finding module for package golang.org/x/tools/txtar
go: finding module for package golang.org/x/tools/go/analysis/analysistest
bb.u-root.com/bb imports
        github.com/golangci/golangci-lint/cmd/golangci-lint imports
        github.com/golangci/golangci-lint/pkg/commands imports
        github.com/golangci/golangci-lint/pkg/lint/lintersdb imports
        github.com/golangci/golangci-lint/pkg/golinters imports
        4d63.com/gocheckcompilerdirectives/checkcompilerdirectives tested by
        4d63.com/gocheckcompilerdirectives/checkcompilerdirectives.test imports
        golang.org/x/tools/go/analysis/analysistest: module golang.org/x/tools@latest found (v0.0.0-00010101000000-000000000000, replaced by ../../golang.org/x/tools), but does not contain package golang.org/x/tools/go/analysis/analysistest
bb.u-root.com/bb imports
        github.com/golangci/golangci-lint/cmd/golangci-lint imports
        github.com/golangci/golangci-lint/pkg/commands imports
        github.com/golangci/golangci-lint/pkg/lint/lintersdb imports
        github.com/golangci/golangci-lint/pkg/golinters imports
        honnef.co/go/tools/unused tested by
        honnef.co/go/tools/unused.test imports
        golang.org/x/tools/go/expect: module golang.org/x/tools@latest found (v0.0.0-00010101000000-000000000000, replaced by ../../golang.org/x/tools), but does not contain package golang.org/x/tools/go/expect
bb.u-root.com/bb imports
        github.com/golangci/golangci-lint/cmd/golangci-lint imports
        github.com/golangci/golangci-lint/pkg/commands imports
        github.com/golangci/golangci-lint/pkg/lint/lintersdb imports
        github.com/golangci/golangci-lint/pkg/golinters imports
        mvdan.cc/gofumpt/format tested by
        mvdan.cc/gofumpt/format.test imports
        golang.org/x/tools/txtar: module golang.org/x/tools@latest found (v0.0.0-00010101000000-000000000000, replaced by ../../golang.org/x/tools), but does not contain package golang.org/x/tools/txtar
bb.u-root.com/bb imports
        github.com/golangci/golangci-lint/cmd/golangci-lint imports
        github.com/golangci/golangci-lint/pkg/commands imports
        github.com/golangci/golangci-lint/pkg/lint/lintersdb imports
        github.com/golangci/golangci-lint/pkg/golinters imports
        github.com/kkHAIKE/contextcheck imports
        github.com/gostaticanalysis/analysisutil tested by
        github.com/gostaticanalysis/analysisutil.test imports
        golang.org/x/tools/go/packages/packagestest: module golang.org/x/tools@latest found (v0.0.0-00010101000000-000000000000, replaced by ../../golang.org/x/tools), but does not contain package golang.org/x/tools/go/packages/packagestest
15:16:02 Preserving bb generated source directory at /tmp/bb-3177314551 due to error.

This may be completely unrelated, but thought I would include it in case it hinted at the wider issue

findpkg: unit tests

pkg/findpkg needs some good test coverage of both Go module mode and GOPATH mode look ups of packages. Cases to consider:

  • looking up directories that don't contain any Go files
  • looking up packages in Go module mode
  • looking up packages in GOPATH mode
  • looking up Go modules outside of GOPATH in GOPATH mode
  • looking up a Go package that isn't a command
  • looking up a directory that only contains Go test files, but no Go command/package
  • looking up more than one command, but one has a go.mod and the other does not, in Go module mode
  • looking up 2 or more Go commands in 2 or more Go modules (testing the batching by module)

Due to #60 we need not consider looking up non-filesystem paths.

Rec: add directories with the desired scenario of module/package/command setups to the ./test and ./vendortest directories at the root of the repo to use in the test

Banish bash

Rewrite all the tests as Go tests. Bash is quickly becoming annoying to control and a long list of copy and pastes.

elaborate on constraints

Just playing around a bit with makebb to see what it does. Here's one point where I'm somewhat confused: For the experiment, I created a subdirectory in gobusybox' root dir, and in that, initialized a module, and go get -ued a single package, then told makebb to bb it up. Specifically, starting from said directory:

 ~/P/gobusybox/more-tests $ go mod init guiuiui
go: creating new go.mod: module guiuiui
 ~/P/gobusybox/more-tests $ go get -u gioui.org/example/kitchen
go: downloading gioui.org/example v0.0.0-20200829162755-829ee4559c5a
go: downloading gioui.org v0.0.0-20200829162755-829ee4559c5a
go: found gioui.org/example/kitchen in gioui.org/example v0.0.0-20200829162755-829ee4559c5a
go: downloading gioui.org v0.0.0-20200726090130-3b95e2918359
go: downloading golang.org/x/image v0.0.0-20200618115811-c13761719519
go: downloading golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3
...

Go doing its thing, after that, this was go.mod:

module guiuiui

go 1.15

require (
        gioui.org v0.0.0-20200829162755-829ee4559c5a // indirect
        gioui.org/example v0.0.0-20200829162755-829ee4559c5a // indirect
        golang.org/x/exp v0.0.0-20200908183739-ae8ad444f925 // indirect
        golang.org/x/image v0.0.0-20200801110659-972c09e46d76 // indirect
        golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 // indirect
        golang.org/x/text v0.3.3 // indirect
)

So I ran makebb:

~/P/gobusybox/ more-tests $ ../src/cmd/makebb/makebb gioui.org/example/kitchen
2020/09/11 20:48:19 Disabling CGO for u-root...
2020/09/11 20:48:19 Build environment: GOARCH=amd64 GOOS=linux GOPATH=/home/dan/go CGO_ENABLED=0 GO111MODULE= GOROOT=/usr/lib/go PATH=/usr/lib/go/bin:$PATH
2020/09/11 20:48:34 Preserving bb generated source directory at /tmp/bb-598536552 due to error
2020/09/11 20:48:34 go build: error building go package in "/tmp/bb-598536552/src/bb.u-root.com/bb": go: found gioui.org/example/kitchen in gioui.org/example v0.0.0-00010101000000-000000000000
package bb.u-root.com/bb
        imports gioui.org/example/kitchen
        imports gioui.org/app
        imports gioui.org/app/internal/window
        imports gioui.org/app/internal/egl
        imports gioui.org/app/internal/glimpl: build constraints exclude all Go files in /home/dan/go/pkg/mod/[email protected]/app/internal/glimpl
, exit status 1

Clueless as I was, I tried turning GO111MODULE off:

~/P/gobusybox/ more-tests $ GO111MODULE=off ../src/cmd/makebb/makebb gioui.org/example/kitchen
2020/09/11 21:00:37 Disabling CGO for u-root...
2020/09/11 21:00:37 Build environment: GOARCH=amd64 GOOS=linux GOPATH=/home/dan/go CGO_ENABLED=0 GO111MODULE=off GOROOT=/usr/lib/go PATH=/usr/lib/go/bin:$PATH
2020/09/11 21:00:37 Skipping package gioui.org/example/kitchen for errors:
-: cannot find package "gioui.org/example/kitchen" in any of:
       /usr/lib/go/src/gioui.org/example/kitchen (from $GOROOT)
       /home/dan/go/src/gioui.org/example/kitchen (from $GOPATH)
2020/09/11 21:00:37 Preserving bb generated source directory at /tmp/bb-238598199 due to error
2020/09/11 21:00:37 no commands compiled

What are those constraints, how are they set, and how can I influence them? Can makebb provide some more information on that? I have no idea, but it's fun to play with this. :)

Use cases to keep in mind

The following use cases shall be kept in mind:

  • support for cross-repository compilation (e.g. u-bmc and u-root, each with their own go.mod or their own vendor directory)
  • support for bazel-based build environments (similar to u-root/u-root#927)
  • support for vendor-based building
  • support for vendor-based only building (no go.mod file to be seen, everything vendored)
  • reproducible builds
  • backwards-compatible path finding (i think "github.com/u-root/u-root/cmds/core/*" should still work as it does in #1358)
  • build command should work from any directory anywhere, supporting local and remote builds. In Go module land, go list -json ../../foobar is not the same as (cd ../../foobar && go list -json .).

Recognize and warn about non-moduled packages outside of $GOPATH

Say, e.g., you clone github.com/u-root/u-root at a point before it has a go.mod (like right now) into a directory that isn't in your $GOPATH, because you intend to add a go.mod, but forget.

Compilation with makebb will terribly fail. We can detect this case and warn the user about it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.