package main import ( "flag" "fmt" "io" "log" "os" "os/exec" "os/user" "path/filepath" "strings" "text/template" ) const dockerFileContents = ` FROM debian:bookworm RUN apt-get update && apt-get install -y crossbuild-essential-arm64 bc libssl-dev bison flex git python3 python3-setuptools swig python3-dev python3-pyelftools uuid-dev libgnutls28-dev COPY gokr-build-uboot /usr/bin/gokr-build-uboot RUN mkdir -p /usr/src/atf.patches RUN mkdir -p /usr/src/uboot.patches {{- range $idx, $path := .Patches }} COPY {{ $path }} /usr/src/{{ $path }} {{- end }} RUN echo 'builduser:x:{{ .Uid }}:{{ .Gid }}:nobody:/:/bin/sh' >> /etc/passwd && \ chown -R {{ .Uid }}:{{ .Gid }} /usr/src USER builduser WORKDIR /usr/src ENTRYPOINT /usr/bin/gokr-build-uboot ` var dockerFileTmpl = template.Must(template.New("dockerfile"). Funcs(map[string]interface{}{ "basename": func(path string) string { return filepath.Base(path) }, }). Parse(dockerFileContents)) var ubootPatchFiles = []string{ "uboot.patches/boot.cmd", "uboot.patches/rk3588_ddr_lp4_2112MHz_lp5_2400MHz_v1.16.bin", } var atfPatchFiles = []string{ "atf.patches/feat-rk3588-support-rk3588.patch", "atf.patches/rk3588-enable-crypto-function.patch", "atf.patches/feat-rockchip-support-SCMI-for-clock-reset-domain.patch", "atf.patches/rockchip-add-some-pm-helpers-functions.patch", } func copyFile(dest, src string) error { out, err := os.Create(dest) if err != nil { return err } defer out.Close() in, err := os.Open(src) if err != nil { return err } defer in.Close() if _, err := io.Copy(out, in); err != nil { return err } st, err := in.Stat() if err != nil { return err } if err := out.Chmod(st.Mode()); err != nil { return err } return out.Close() } var gopath = mustGetGopath() func mustGetGopath() string { gopathb, err := exec.Command("go", "env", "GOPATH").Output() if err != nil { log.Panic(err) } return strings.TrimSpace(string(gopathb)) } func find(filename string) (string, error) { if _, err := os.Stat(filename); err == nil { return filename, nil } path := filepath.Join(gopath, "src", "github", "lordwelch", "gokrazy-cm3588-kernel", filename) if _, err := os.Stat(path); err == nil { return path, nil } return "", fmt.Errorf("could not find file %q (looked in . and %s)", filename, path) } func getContainerExecutable() (string, error) { // Probe podman first, because the docker binary might actually // be a thin podman wrapper with podman behavior. choices := []string{"podman", "docker"} for _, exe := range choices { p, err := exec.LookPath(exe) if err != nil { continue } resolved, err := filepath.EvalSymlinks(p) if err != nil { return "", err } return resolved, nil } return "", fmt.Errorf("none of %v found in $PATH", choices) } func main() { var overwriteContainerExecutable = flag.String("overwrite_container_executable", "", "E.g. docker or podman to overwrite the automatically detected container executable") flag.Parse() executable, err := getContainerExecutable() if err != nil { log.Fatal(err) } if *overwriteContainerExecutable != "" { executable = *overwriteContainerExecutable } execName := filepath.Base(executable) // We explicitly use /tmp, because Docker only allows volume mounts under // certain paths on certain platforms, see // e.g. https://docs.docker.com/docker-for-mac/osxfs/#namespaces for macOS. tmp, err := os.MkdirTemp("/tmp", "gokr-rebuild-uboot") if err != nil { log.Fatal(err) } defer os.RemoveAll(tmp) cmd := exec.Command("go", "build", "-o", tmp, "github/lordwelch/gokrazy-cm3588-kernel/cmd/gokr-build-uboot") cmd.Env = append(os.Environ(), "GOOS=linux", "CGO_ENABLED=0") cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { log.Fatalf("%v: %v", cmd.Args, err) } buildPath := filepath.Join(tmp, "gokr-build-uboot") var patchPaths []string for _, filename := range ubootPatchFiles { path, err := find(filename) if err != nil { log.Fatal(err) } patchPaths = append(patchPaths, path) } err = os.MkdirAll(filepath.Join(tmp, "uboot.patches"), 0750) if err != nil { log.Fatal(err) } // Copy all files into the temporary directory so that docker // includes them in the build context. for _, path := range patchPaths { if err = copyFile(filepath.Join(tmp, "uboot.patches", filepath.Base(path)), path); err != nil { log.Fatal(err) } } patchPaths = patchPaths[0:0] for _, filename := range atfPatchFiles { path, err := find(filename) if err != nil { log.Fatal(err) } patchPaths = append(patchPaths, path) } err = os.MkdirAll(filepath.Join(tmp, "atf.patches"), 0750) if err != nil { log.Fatal(err) } // Copy all files into the temporary directory so that docker // includes them in the build context. for _, path := range patchPaths { if err := copyFile(filepath.Join(tmp, "atf.patches", filepath.Base(path)), path); err != nil { log.Fatal(err) } } u, err := user.Current() if err != nil { log.Fatal(err) } dockerFile, err := os.Create(filepath.Join(tmp, "Dockerfile")) if err != nil { log.Fatal(err) } if err := dockerFileTmpl.Execute(dockerFile, struct { Uid string Gid string BuildPath string Patches []string }{ Uid: u.Uid, Gid: u.Gid, BuildPath: buildPath, Patches: append(atfPatchFiles, ubootPatchFiles...), }); err != nil { log.Fatal(err) } if err := dockerFile.Close(); err != nil { log.Fatal(err) } log.Printf("building %s container for uboot compilation", execName) dockerBuild := exec.Command(execName, "build", "--rm=true", "--tag=gokr-rebuild-uboot", ".") dockerBuild.Dir = tmp dockerBuild.Stdout = os.Stdout dockerBuild.Stderr = os.Stderr if err := dockerBuild.Run(); err != nil { log.Fatalf("%s build: %v (cmd: %v)", execName, err, dockerBuild.Args) } log.Printf("compiling uboot") var dockerRun *exec.Cmd if execName == "podman" { dockerRun = exec.Command(executable, "run", "--userns=keep-id", "--rm", "--volume", tmp+":/tmp/buildresult:Z", "gokr-rebuild-uboot") } else { dockerRun = exec.Command(executable, "run", "--rm", "--volume", tmp+":/tmp/buildresult:Z", "gokr-rebuild-uboot") } dockerRun.Dir = tmp dockerRun.Stdout = os.Stdout dockerRun.Stderr = os.Stderr if err := dockerRun.Run(); err != nil { log.Fatalf("%s run: %v (cmd: %v)", execName, err, dockerRun.Args) } for _, filename := range []string{ "boot.scr", "u-boot-rockchip.bin", } { if err := copyFile(filename, filepath.Join(tmp, filename)); err != nil { log.Fatal(err) } } }