targets/target.go (322 lines of code) (raw):

package targets import ( "context" "fmt" "os" "strings" buildx "github.com/Azure/moby-packaging/packages/moby-buildx" cli "github.com/Azure/moby-packaging/packages/moby-cli" compose "github.com/Azure/moby-packaging/packages/moby-compose" containerd "github.com/Azure/moby-packaging/packages/moby-containerd" shim "github.com/Azure/moby-packaging/packages/moby-containerd-shim-systemd" engine "github.com/Azure/moby-packaging/packages/moby-engine" runc "github.com/Azure/moby-packaging/packages/moby-runc" tini "github.com/Azure/moby-packaging/packages/moby-tini" "github.com/Azure/moby-packaging/pkg/apt" "github.com/Azure/moby-packaging/pkg/archive" "dagger.io/dagger" ) type GoVersionFunc = func(*archive.Spec) string func (t *Target) AptInstall(pkgs ...string) *Target { c := apt.Install(t.c, t.client.CacheVolume(t.name+"-apt-cache"), t.client.CacheVolume(t.name+"-apt-lib-cache"), pkgs...) return t.update(c) } type Target struct { c *dagger.Container name string platform dagger.Platform client *dagger.Client pkgKind string goVersion string buildPlatform dagger.Platform } func (t *Target) update(c *dagger.Container) *Target { tgt := *t tgt.c = c return &tgt } func MirrorPrefix() string { prefix, ok := os.LookupEnv("MIRROR_PREFIX") if !ok { prefix = "mcr.microsoft.com/mirror/docker/library" } return prefix } type MakeTargetFunc func(context.Context, *dagger.Client, dagger.Platform, string) (*Target, error) var targets = map[string]MakeTargetFunc{ "jammy": Jammy, "noble": Noble, "buster": Buster, "bionic": Bionic, "bullseye": Bullseye, "bookworm": Bookworm, "focal": Focal, "rhel8": Rhel8, "rhel9": Rhel9, "windows": Windows, "mariner2": Mariner2, } func GetTarget(ctx context.Context, distro string, client *dagger.Client, platform dagger.Platform, goVersion string) (*Target, error) { f, ok := targets[distro] if !ok { panic("unknown distro: " + distro) } return f(ctx, client, platform, goVersion) } var ( BaseWinPackages = []string{ "binutils-mingw-w64", "g++-mingw-w64-x86-64", "gcc", "git", "make", "pkg-config", "quilt", "zip", } BaseDebPackages = []string{ "build-essential", "cmake", "dh-make", "devscripts", "dh-apparmor", "dpkg-dev", "equivs", "fakeroot", "libbtrfs-dev", "libdevmapper-dev", "libltdl-dev", "libseccomp-dev", "quilt", } BaseBionicPackages = []string{ "bash", "build-essential", "cmake", "dh-make", "devscripts", "dh-apparmor", "dpkg-dev", "equivs", "fakeroot", "libdevmapper-dev", "libltdl-dev", "libseccomp-dev", "quilt", } BaseMarinerPackages = []string{ "bash", "binutils", "build-essential", "ca-certificates", "cmake", "device-mapper-devel", "diffutils", "dnf-utils", "file", "gcc", "git", "glibc-static", "libffi-devel", "libseccomp-devel", "libtool", "libtool-ltdl-devel", "make", "patch", "pkgconfig", "pkgconfig(systemd)", "rpm-build", "rpmdevtools", "selinux-policy-devel", "systemd-devel", "tar", "which", "yum-utils", } BaseRPMPackages = []string{ "bash", "ca-certificates", "cmake", "device-mapper-devel", "gcc", "git", "glibc-static", "libseccomp-devel", "libtool", "libtool-ltdl-devel", "make", "make", "patch", "pkgconfig", "pkgconfig(systemd)", "rpmdevtools", "selinux-policy-devel", "systemd-devel", "tar", "which", "yum-utils", } GetGoVersionForPackage = map[string]GoVersionFunc{ "moby-buildx": buildx.GoVersion, "moby-cli": cli.GoVersion, "moby-compose": compose.GoVersion, "moby-containerd": containerd.GoVersion, "moby-containerd-shim-systemd": shim.GoVersion, "moby-engine": engine.GoVersion, "moby-runc": runc.GoVersion, "moby-tini": tini.GoVersion, } ) func (t *Target) Container() *dagger.Container { return t.c } func (t *Target) WithExec(args []string, opts ...dagger.ContainerWithExecOpts) *Target { return t.update(t.c.WithExec(args, opts...)) } func (t *Target) PkgKind() string { return t.pkgKind } func (t *Target) applyPatchesCommand() []string { return []string{ "bash", "-exc", ` [ -f patches/series ] || exit 0 readarray -t patches < patches/series cd src/ for f in "${patches[@]}"; do patch -p1 < "/build/patches/$f" done `, } } // Winres is used during windows builds (as part of the project build scripts) to "manifest" binaries. // This is required for windows to properly identify the binaries. func (t *Target) Winres() *dagger.File { goRef := fmt.Sprintf("%s:%s", GoRepo, t.goVersion) return t.client.Container(). From(goRef). WithEnvVariable("GOBIN", "/build"). WithEnvVariable("CGO_ENABLED", "0"). WithEnvVariable("GO111MODULE", "on"). WithExec([]string{"go", "install", "github.com/tc-hib/go-winres@v0.3.0"}). File("/build/go-winres") } func (t *Target) goMD2Man() *dagger.File { repo := "https://github.com/cpuguy83/go-md2man.git" ref := "v2.0.2" outfile := "/build/bin/go-md2man" srcDir := t.client.Git(repo, dagger.GitOpts{KeepGitDir: true}).Commit(ref).Tree() goRef := fmt.Sprintf("%s:%s", GoRepo, t.goVersion) c := t.client.Container(). From(goRef). WithDirectory("/build", srcDir). WithWorkdir("/build"). WithEnvVariable("CGO_ENABLED", "0"). WithExec([]string{"go", "build", "-o", outfile}) return c.File(outfile) } type Packager interface { Package(*dagger.Client, *dagger.Container, *archive.Spec) *dagger.Directory } func (t *Target) Packager(projectName, distro, version string) (Packager, error) { var mappings map[string]archive.Archive switch projectName { case "moby-engine": mappings = engine.Archives case "moby-cli": mappings = cli.Archives case "moby-containerd": ls, err := containerd.Archives(version) if err != nil { return nil, err } mappings = ls case "moby-containerd-shim-systemd": mappings = shim.Archives case "moby-runc": mappings = runc.Archives case "moby-compose": mappings = compose.Archives case "moby-buildx": mappings = buildx.Archives case "moby-tini": mappings = tini.Archives default: return nil, fmt.Errorf("unsupported project: %s", projectName) } a, ok := mappings[distro] if !ok { return nil, fmt.Errorf("unsupported distro: %s", distro) } switch t.PkgKind() { case "deb": return archive.NewDebPackager(&a, MirrorPrefix()), nil case "rpm": return archive.NewRPMPackager(&a, MirrorPrefix()), nil case "win": return archive.NewWinPackager(&a, MirrorPrefix()), nil default: panic("unknown pkgKind: " + t.pkgKind) } } func (t *Target) getCommitTime(projectName string, sourceDir *dagger.Directory) string { commitTime, err := t.c. WithMountedDirectory("/build/src", sourceDir). WithWorkdir("/build/src"). WithExec([]string{"bash", "-ec", `date -u --date=$(git show -s --format=%cI HEAD) +%s > /tmp/COMMITTIME`}). File("/tmp/COMMITTIME"). Contents(context.TODO()) if err != nil { return "" } return strings.TrimSpace(commitTime) } func (t *Target) Make(project *archive.Spec, projectDir, hackCrossDir *dagger.Directory) (*dagger.Directory, error) { md2man := t.goMD2Man() source := t.getSource(project) commitTime := t.getCommitTime(project.Pkg, source) build := t.c. WithDirectory("/build", projectDir). WithDirectory("/build/hack/cross", hackCrossDir). WithDirectory("/build/src", source). WithWorkdir("/build"). WithMountedFile("/usr/bin/go-md2man", md2man). WithMountedFile("/usr/bin/go-winres", t.Winres()). WithEnvVariable("TARGET_DISTRO", project.Distro). WithEnvVariable("REVISION", project.Revision). WithEnvVariable("VERSION", project.Tag). WithEnvVariable("COMMIT", project.Commit). WithEnvVariable("SOURCE_DATE_EPOCH", commitTime). WithExec(t.applyPatchesCommand()). WithExec([]string{"/usr/bin/make", t.PkgKind()}) packager, err := t.Packager(project.Pkg, project.Distro, project.Tag) if err != nil { return nil, err } return packager.Package(t.client, build, project), nil } func WithPlatformEnvs(c *dagger.Container, build, target dagger.Platform) *dagger.Container { split := strings.SplitN(string(build), "/", 2) buildOS := split[0] buildArch := split[1] var buildVarient string if len(split) == 3 { buildVarient = split[2] } split = strings.SplitN(string(target), "/", 2) targetOS := split[0] targetArch := split[1] var targetVariant string if len(split) == 3 { targetVariant = split[2] } return c. WithEnvVariable("BUILDARCH", buildArch). WithEnvVariable("BUILDVARIANT", buildVarient). WithEnvVariable("BUILDOS", buildOS). WithEnvVariable("BUILDPLATFORM", string(build)). WithEnvVariable("TARGETARCH", targetArch). WithEnvVariable("TARGETVARIANT", targetVariant). WithEnvVariable("TARGETOS", targetOS). WithEnvVariable("TARGETPLATFORM", string(target)) } func (t *Target) WithPlatformEnvs() *Target { return t.update(WithPlatformEnvs(t.c, t.buildPlatform, t.platform)) }