common

package
v2.5.0 Latest Latest
Warning

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

Go to latest
Published: Jul 22, 2026 License: GPL-3.0 Imports: 26 Imported by: 0

Documentation

Overview

Package common provides shared interfaces and base implementations for package builders.

Package common provides shared interfaces and base implementations for package builders.

Package common provides shared functionality for cross-compilation toolchains.

Index

Constants

This section is empty.

Variables

View Source
var CrossToolchainMap = func() map[string]map[string]CrossToolchain {

	basePatterns := map[string]map[string]string{
		constants.DistroDebian: {
			gccKey:        gccFmtPattern,
			gppKey:        gppFmtPattern,
			binutilsKey:   binutilsFmtPat,
			additionalKey: libc6DevFmtPat,
		},
		constants.DistroUbuntu: {
			gccKey:        gccFmtPattern,
			gppKey:        gppFmtPattern,
			binutilsKey:   binutilsFmtPat,
			additionalKey: libc6DevFmtPat,
		},
		constants.DistroFedora: {
			gccKey:        gccFmtPattern,
			gppKey:        gppCxxFmtPat,
			binutilsKey:   binutilsFmtPat,
			additionalKey: "",
		},

		constants.DistroArch: {
			gccKey:        archGccFmtPat,
			gppKey:        archGppFmtPat,
			binutilsKey:   archBinFmtPat,
			additionalKey: "",
		},
	}

	archSpecific := map[string]map[string]map[string]string{
		constants.ArchI686: {
			constants.DistroArch: {
				gccKey:      gccMultilibPkg,
				gppKey:      gccCxxMultilib,
				binutilsKey: binutilsMultilib,
			},
		},
		constants.ArchX86_64: {
			constants.DistroFedora: {
				gccKey:      gccX86FmtPat,
				gppKey:      gppX86FmtPat,
				binutilsKey: binX86FmtPat,
			},
		},
		constants.ArchAarch64: {
			constants.DistroArch: {
				gccKey:      aarch64ArchGcc,
				gppKey:      aarch64ArchGpp,
				binutilsKey: "aarch64-linux-gnu-binutils",
			},
		},

		constants.ArchS390x: {
			constants.DistroFedora: {
				gccKey:      gccS390xRH,
				gppKey:      gppS390xRH,
				binutilsKey: binS390xRH,
			},
		},
	}

	archAdditional := map[string]map[string][]string{
		constants.ArchAarch64: {
			constants.DistroDebian: {"libc6-dev-arm64-cross"},
			constants.DistroUbuntu: {"libc6-dev-arm64-cross"},
		},
		constants.ArchArmv7: {
			constants.DistroDebian: {libc6DevArmhf},
			constants.DistroUbuntu: {libc6DevArmhf},
		},
		constants.ArchArmv6: {
			constants.DistroDebian: {libc6DevArmhf},
			constants.DistroUbuntu: {libc6DevArmhf},
		},
		constants.ArchI686: {
			constants.DistroArch:   {lib32GccLibs},
			constants.DistroDebian: {libc6DevI386},
			constants.DistroUbuntu: {libc6DevI386},
		},
		constants.ArchX86_64: {
			constants.DistroDebian: {"libc6-dev-amd64-cross"},
			constants.DistroUbuntu: {"libc6-dev-amd64-cross"},
		},
		constants.ArchPpc64le: {
			constants.DistroDebian: {"libc6-dev-ppc64el-cross"},
			constants.DistroUbuntu: {"libc6-dev-ppc64el-cross"},
		},
		constants.ArchS390x: {
			constants.DistroDebian: {"libc6-dev-s390x-cross"},
			constants.DistroUbuntu: {"libc6-dev-s390x-cross"},
		},
		constants.ArchRiscv64: {
			constants.DistroDebian: {"libc6-dev-riscv64-cross"},
			constants.DistroUbuntu: {"libc6-dev-riscv64-cross"},
		},
	}

	archPatterns := map[string]string{
		constants.ArchAarch64: constants.TripletAarch64Linux,
		constants.ArchArmv7:   constants.TripletArmLinuxHf,
		constants.ArchArmv6:   constants.TripletArmLinuxHf,
		constants.ArchI686:    constants.TripletI686Linux,
		constants.ArchX86_64:  "x86-64-linux-gnu",
		constants.ArchPpc64le: constants.TripletPpc64leLinux,
		constants.ArchS390x:   constants.TripletS390xLinux,
		constants.ArchRiscv64: constants.TripletRiscv64Linux,
	}

	result := make(map[string]map[string]CrossToolchain)

	architectures := []string{
		constants.ArchAarch64, constants.ArchArmv7, constants.ArchArmv6, constants.ArchI686,
		constants.ArchX86_64, constants.ArchPpc64le, constants.ArchS390x, constants.ArchRiscv64,
	}

	distributions := []string{
		constants.DistroDebian, constants.DistroUbuntu, constants.DistroFedora,
		constants.DistroArch,
	}

	for _, arch := range architectures {
		result[arch] = make(map[string]CrossToolchain)

		for _, distro := range distributions {

			patterns, exists := basePatterns[distro]
			if !exists {
				continue
			}

			archPattern := arch
			if pattern, exists := archPatterns[arch]; exists {
				archPattern = pattern
			}

			gcc := fmt.Sprintf(patterns[gccKey], archPattern)
			gpp := fmt.Sprintf(patterns[gppKey], archPattern)
			binutils := fmt.Sprintf(patterns[binutilsKey], archPattern)

			if archOverrides, exists := archSpecific[arch]; exists {
				if distroOverrides, exists := archOverrides[distro]; exists {
					if override, exists := distroOverrides[gccKey]; exists {
						gcc = override
					}

					if override, exists := distroOverrides[gppKey]; exists {
						gpp = override
					}

					if override, exists := distroOverrides[binutilsKey]; exists {
						binutils = override
					}
				}
			}

			// Determine additional packages
			var additional []string

			if archAdd, exists := archAdditional[arch]; exists {
				if add, exists := archAdd[distro]; exists {
					additional = add
				} else if add, exists := archAdd[defaultKey]; exists {
					additional = add
				}
			}

			triple := archPatterns[arch]

			installCommands := make(map[string]string)

			switch distro {
			case constants.DistroDebian, constants.DistroUbuntu:
				installCommands[distro] = fmt.Sprintf("sudo apt-get install %s %s", gcc, gpp)
			case constants.DistroFedora:
				installCommands[distro] = fmt.Sprintf("sudo dnf install %s %s", gcc, gpp)
			case constants.DistroArch:
				installCommands[distro] = fmt.Sprintf("sudo pacman -S %s %s", gcc, gpp)
			case constants.DistroAlpine:
				installCommands[distro] = fmt.Sprintf("sudo apk add %s %s", gcc, gpp)
			}

			result[arch][distro] = CrossToolchain{
				GCCPackage:         gcc,
				GPlusPlusPackage:   gpp,
				BinutilsPackage:    binutils,
				AdditionalPackages: additional,
				Triple:             triple,
				InstallCommands:    installCommands,
			}
		}
	}

	return result
}()

CrossToolchainMap maps target architectures to toolchain packages for each distribution.

View Source
var SkipToolchainValidation bool

SkipToolchainValidation controls whether cross-compilation toolchain validation is performed. This is set by command-line flags and used by PrepareEnvironment.

Functions

func ExtractDEB added in v2.1.0

func ExtractDEB(packagePath, destDir string) error

ExtractDEB is a wrapper around archive.ExtractDEB for backward compatibility. It extracts a Debian package (.deb) to the destination directory.

func ExtractRPM added in v2.1.0

func ExtractRPM(packagePath, destDir string) error

ExtractRPM is a wrapper around archive.ExtractRPM for backward compatibility. It extracts an RPM package payload to the destination directory.

func NormalizeTargetArch added in v2.2.0

func NormalizeTargetArch(arch string) string

NormalizeTargetArch returns the canonical form of arch, mapping aliases like "arm64" → "aarch64" and "amd64" → "x86_64". Unknown values pass through unchanged so ValidateTargetArch can report them with the full known-arch list.

func ValidateTargetArch

func ValidateTargetArch(arch string) error

ValidateTargetArch returns an error if arch is not a recognised cross-compilation target. Returns nil when arch is empty (native build) or known.

func ValidateToolchain

func ValidateToolchain(targetArch, format string) error

ValidateToolchain checks if the required cross-compilation toolchain is available for the given target architecture and package format. It returns a detailed error message with installation instructions if the toolchain is missing.

APK (Alpine) format always returns an error: Alpine does not ship Linux-userspace cross-compiler packages. Use a native Alpine container or QEMU instead.

Types

type BaseBuilder

type BaseBuilder struct {
	PKGBUILD *pkgbuild.PKGBUILD
	Format   string // Package format: "apk", "deb", "rpm", "pacman"
}

BaseBuilder provides common functionality that can be embedded in concrete builders.

func NewBaseBuilder

func NewBaseBuilder(pkgBuild *pkgbuild.PKGBUILD, format string) *BaseBuilder

NewBaseBuilder creates a new base builder instance.

func (*BaseBuilder) ApplyOptions

func (bb *BaseBuilder) ApplyOptions() error

ApplyOptions runs the PKGBUILD option handlers (strip, docs, libtool, etc.) against the package directory using the process environment for STRIP/OBJCOPY. Prefer ApplyOptionsWithEnv for parallel builds.

func (*BaseBuilder) ApplyOptionsWithEnv added in v2.1.2

func (bb *BaseBuilder) ApplyOptionsWithEnv(env map[string]string) error

ApplyOptionsWithEnv is the env-overlay variant of ApplyOptions. The env map is consulted before os.Getenv for STRIP/OBJCOPY during the strip pass. Pair with BuildCrossStripEnvSlice() (parsed to a map) to scope cross-strip toolchain selection without mutating the global process environment.

func (*BaseBuilder) BuildCcacheEnvSlice added in v2.1.0

func (bb *BaseBuilder) BuildCcacheEnvSlice() []string

BuildCcacheEnvSlice returns ccache environment variables as a "KEY=VALUE" slice for safe concurrent use. Unlike SetupCcache, it does NOT mutate the process environment (no os.Setenv calls). Returns nil if ccache is not available.

CC/CXX are set to bare compiler names (gcc, g++). ccache wrapping is achieved by prepending the ccache symlink directory to PATH, matching the approach used by the cross-compilation path (BuildCrossEnvSlice). This avoids the "CC=ccache gcc" pattern which breaks some autoconf configure scripts that perform word-splitting on $CC in ways that fail with multi-word values.

func (*BaseBuilder) BuildCrossEnvSlice added in v2.1.0

func (bb *BaseBuilder) BuildCrossEnvSlice(targetArch string) ([]string, error)

BuildCrossEnvSlice returns the cross-compilation environment variables as a "KEY=VALUE" slice for safe concurrent use. Unlike SetupCrossCompilationEnvironment, it does NOT mutate the process environment (no os.Setenv calls). Returns nil if no cross-compilation is needed (targetArch == "" or == build arch).

func (*BaseBuilder) BuildCrossStripEnvSlice added in v2.1.2

func (bb *BaseBuilder) BuildCrossStripEnvSlice(targetArch string) []string

BuildCrossStripEnvSlice returns STRIP and OBJCOPY environment variables as a "KEY=VALUE" slice for safe concurrent use. Unlike SetupCrossStripEnv, it does NOT mutate the process environment (no os.Setenv calls). Returns nil if no cross-compilation is active or if the toolchain cannot be resolved.

The returned slice can be passed to ApplyOptions() to configure cross-compilation binutils for the strip/objcopy pass, making it safe to call from multiple goroutines simultaneously (parallel builds).

func (*BaseBuilder) BuildPackageName

func (bb *BaseBuilder) BuildPackageName(extension string) string

BuildPackageName constructs standardized package names for different formats. Used by: APK, DEB, Pacman Not used by: RPM (RPM does not include epoch in the filename, only in metadata)

func (*BaseBuilder) CreateFileWalker

func (bb *BaseBuilder) CreateFileWalker() *files.Walker

CreateFileWalker creates a configured file walker for the package format.

func (*BaseBuilder) CrossStripEnvMap added in v2.1.2

func (bb *BaseBuilder) CrossStripEnvMap(targetArch string) map[string]string

CrossStripEnvMap parses BuildCrossStripEnvSlice into a map[K]V usable with ApplyOptionsWithEnv. Returns nil if no cross-strip env is needed.

func (*BaseBuilder) DownloadAndExtractCrossDeps

func (bb *BaseBuilder) DownloadAndExtractCrossDeps(
	ctx context.Context,
	deps []string,
	targetArch string,
) error

DownloadAndExtractCrossDeps downloads runtime dependencies and extracts them directly to the root filesystem without registering them in the dpkg database. This avoids the circular dependency problem where arch-all meta-packages (e.g. vendor-core) depend on arch-specific packages (e.g. vendor-openldap) that conflict with the target-arch variants needed for cross-compilation.

The function partitions deps the same way as installCrossDeps: arch-all packages are downloaded unqualified, arch-specific ones are qualified with the target architecture (e.g. :arm64). All packages are extracted to "/" without dpkg, so there is no conflict checking.

**Transitive resolution**: the declared PKGBUILD dependencies are walked through aptcache.DownloadClosure so transitive runtime libraries that the declared deps themselves need are pulled in too. Without this, a PKGBUILD listing only vendor-ffmpeg fails to cross-link because libavcodec.so has DT_NEEDED entries for libvpx.so / libx264.so which come from sibling packages (vendor-libvpx, vendor-x264) the PKGBUILD did not declare.

func (*BaseBuilder) ExtractToRoot

func (bb *BaseBuilder) ExtractToRoot(packagePath string) error

ExtractToRoot extracts a package directly to the root filesystem (/). This extracts the package contents to the actual filesystem without using a sysroot directory.

func (*BaseBuilder) FormatRelease

func (bb *BaseBuilder) FormatRelease(distroSuffixMap map[string]string)

FormatRelease formats the package release string with distribution-specific suffixes. For DEB packages: always appends a distro suffix (codename or distro name) for proper repository targeting. The suffix is guarded against double-append for split packages. For RPM packages: appends RPM distro suffix and codename (only when codename is set) For other formats: returns release unchanged

func (*BaseBuilder) InstallOrExtract

func (bb *BaseBuilder) InstallOrExtract(artifactsPath, buildDir, targetArch string) error

InstallOrExtract extracts the built package to the root filesystem (/). This applies to both native and cross-compilation builds.

func (*BaseBuilder) LogCrossCompilation

func (bb *BaseBuilder) LogCrossCompilation(targetArch string)

LogCrossCompilation logs cross-compilation build initiation if target architecture differs. This consolidates duplicated cross-compilation logging across builders.

func (*BaseBuilder) LogPackageCreated

func (bb *BaseBuilder) LogPackageCreated(artifactPath string)

LogPackageCreated logs successful package creation with consistent formatting. When running under sudo, ownership of the artifact is transferred to the original invoking user so downstream consumers (CI agents, etc.) can read it without elevated privileges.

func (*BaseBuilder) Prepare

func (bb *BaseBuilder) Prepare(ctx context.Context, makeDepends []string, targetArch string) error

Prepare installs build dependencies using the appropriate package manager. This consolidates duplicated Prepare methods across all builders.

func (*BaseBuilder) PrepareBackupFilePaths

func (bb *BaseBuilder) PrepareBackupFilePaths() []string

PrepareBackupFilePaths ensures all backup file paths have a leading slash. This is required by RPM and some other package formats.

func (*BaseBuilder) PrepareEnvironment

func (bb *BaseBuilder) PrepareEnvironment(ctx context.Context, golang bool, targetArch string) error

PrepareEnvironment sets up the build environment with necessary tools. This consolidates duplicated PrepareEnvironment methods across all builders. Toolchain validation is always skipped here: PrepareEnvironment is called by "yap prepare" whose job is to *install* the cross-compiler — the toolchain cannot be present before it is installed. Validation runs later during the build stage (builder.processFunction → SetupCrossCompilationEnvironment).

func (*BaseBuilder) PrepareScriptletWithHelpers

func (bb *BaseBuilder) PrepareScriptletWithHelpers(body string) string

PrepareScriptletWithHelpers prepends the PKGBUILD helper function preamble to a scriptlet body. Returns the body unchanged if no preamble is needed.

func (*BaseBuilder) ProcessDependencies

func (bb *BaseBuilder) ProcessDependencies(depends []string) []string

ProcessDependencies processes dependency strings with version operators. This consolidates the duplicated dependency processing logic across package formats.

func (*BaseBuilder) SetTargetArchitecture

func (bb *BaseBuilder) SetTargetArchitecture(targetArch string)

SetTargetArchitecture sets the target architecture for cross-compilation and translates it. If targetArch is empty, it uses the current ArchComputed value. Architecture-independent packages (arch=any) are never overridden — they always produce arch-all packages regardless of the cross-compilation target. This consolidates the duplicated architecture handling pattern across all builders.

func (*BaseBuilder) SetupCrossStripEnv added in v2.1.0

func (bb *BaseBuilder) SetupCrossStripEnv(targetArch string)

SetupCrossStripEnv sets STRIP and OBJCOPY in the process environment so that any code path that still reads them via os.Getenv (e.g. external tooling) sees the cross-compilation binutils. Internal callers should prefer CrossStripEnvMap + ApplyOptionsWithEnv, which is parallel-safe. No-op when targetArch is empty or equals the build arch. Guarded by envMutex to serialize concurrent invocations.

func (*BaseBuilder) SetupEnvironmentDependencies

func (bb *BaseBuilder) SetupEnvironmentDependencies(golang bool) []string

SetupEnvironmentDependencies gets the build environment dependencies for the format, including install flags. Calls SetupEnvironmentDeps internally.

func (*BaseBuilder) SetupEnvironmentDeps added in v2.1.0

func (bb *BaseBuilder) SetupEnvironmentDeps(golang bool) []string

SetupEnvironmentDeps gets the build environment dependency package names (without install flags) for the format.

func (*BaseBuilder) TranslateArchitecture

func (bb *BaseBuilder) TranslateArchitecture()

TranslateArchitecture translates architecture names for the target package format.

func (*BaseBuilder) Update

func (bb *BaseBuilder) Update(ctx context.Context) error

Update updates the package manager's package database. This consolidates duplicated Update methods across all builders. For DEB, --allow-insecure-repositories is passed so that apt writes Packages index files for unsigned extra repos (e.g. --repo flags) to /var/lib/apt/lists/. Without it, apt silently skips those repos and aptcache.Reload() cannot see their packages for arch classification.

type CrossToolchain

type CrossToolchain struct {
	// GCCPackage is the name of the GCC cross-compiler package
	GCCPackage string
	// GPlusPlusPackage is the name of the G++ cross-compiler package
	GPlusPlusPackage string
	// BinutilsPackage is the name of the binutils package for the target architecture
	BinutilsPackage string
	// AdditionalPackages are any additional packages needed for the toolchain
	AdditionalPackages []string
	// Triple is the GNU triplet for the target architecture
	Triple string
	// InstallCommands provides distribution-specific installation commands
	InstallCommands map[string]string
}

CrossToolchain represents a cross-compilation toolchain for a specific target architecture.

func GetCrossToolchain

func GetCrossToolchain(arch, distro string) (CrossToolchain, error)

GetCrossToolchain retrieves the toolchain for a given architecture and distribution.

func (*CrossToolchain) GetAllPackages

func (ct *CrossToolchain) GetAllPackages() []string

GetAllPackages returns all packages needed for this toolchain.

func (*CrossToolchain) GetExecutableName

func (ct *CrossToolchain) GetExecutableName(packageName string) string

GetExecutableName converts a package name to its actual cross-compiler executable name. Naming conventions differ by distribution:

  • Debian/Ubuntu: gcc-aarch64-linux-gnu → aarch64-linux-gnu-gcc
  • Fedora: gcc-c++-aarch64-linux-gnu → aarch64-linux-gnu-g++
  • Arch: aarch64-linux-gnu-gcc → aarch64-linux-gnu-gcc (already correct)
  • Alpine: gcc-aarch64 → aarch64-alpine-linux-musl-gcc

func (*CrossToolchain) GetPackagesByType

func (ct *CrossToolchain) GetPackagesByType() map[string][]string

GetPackagesByType returns packages categorized by type.

func (*CrossToolchain) Validate

func (ct *CrossToolchain) Validate() ([]string, error)

Validate checks if the required toolchain executables are available in PATH. It resolves package names to their actual executable names before checking.

Jump to

Keyboard shortcuts

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