12 KiB
Quickstart: Go Module VCS Build System for Yocto/BitBake
This guide covers how to create and maintain Go recipes using the go-mod-vcs system, which provides reproducible, offline Go builds by fetching dependencies directly from their git repositories.
Overview
The go-mod-vcs system replaces vendor directories with a build-time module cache constructed from git repositories. This provides:
- Reproducible builds - Every module comes from a verified git commit
- Offline builds - After
do_fetch, no network access is required - Auditable dependencies - Every dependency is traceable to a specific git commit
- Smaller recipes - No need to maintain large vendor directories in source trees
How It Works
┌─────────────────────────────────────────────────────────────────────┐
│ Your Recipe │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │
│ │ myapp_git.bb │ │ go-mod-git.inc │ │ go-mod-cache.inc │ │
│ │ (your code) │ │ (git fetches) │ │ (module metadata) │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │
└───────────────────────────────┬─────────────────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────────┐ ┌───────────────┐
│do_fetch │ │do_create_ │ │ do_compile │
│ │──────────▶│module_cache │────────▶│ │
│(git) │ │(build cache)│ │(go build) │
└─────────┘ └─────────────┘ └───────────────┘
- do_fetch - BitBake fetches all git repositories listed in
go-mod-git.inc - do_create_module_cache - The
go-mod-vcsclass builds a Go module cache from git checkouts - do_compile - Go builds offline using the pre-populated module cache
Converting an Existing Recipe
Step 1: Add Discovery Configuration
Add these lines to your recipe (before inherit go):
# go-mod-discovery configuration
GO_MOD_DISCOVERY_BUILD_TARGET = "./cmd/..." # Your build target
GO_MOD_DISCOVERY_GIT_REPO = "https://github.com/org/repo.git"
GO_MOD_DISCOVERY_GIT_REF = "${SRCREV}"
inherit go-mod-discovery
Step 2: Include the Generated Files
Add includes after your SRC_URI:
SRC_URI = "git://github.com/org/repo;branch=main;protocol=https;destsuffix=${GO_SRCURI_DESTSUFFIX}"
include go-mod-git.inc
include go-mod-cache.inc
Step 3: Create Placeholder Files
Create empty placeholder files (they'll be generated):
cd recipes-containers/myapp/
touch go-mod-git.inc go-mod-cache.inc
Step 4: Run Discovery
bitbake myapp -c discover_and_generate
This will:
- Build your project with network access to discover dependencies
- Extract module metadata from the Go module cache
- Generate
go-mod-git.incandgo-mod-cache.incfiles
Step 5: Update do_compile()
Ensure your do_compile() doesn't set conflicting environment variables. The go-mod-vcs.bbclass automatically sets:
GOMODCACHE- Points to the built module cacheGOPROXY=off- Enforces offline buildGOSUMDB=off- Disables checksum databaseGOTOOLCHAIN=local- Uses the native Go toolchain
A minimal do_compile():
do_compile() {
cd ${S}/src/import
export GOPATH="${S}/src/import/.gopath:${STAGING_DIR_TARGET}/${prefix}/local/go"
export CGO_ENABLED="1"
${GO} build -trimpath ./cmd/...
}
Step 6: Build and Test
bitbake myapp
Creating a New Recipe
Minimal Recipe Template
SUMMARY = "My Go Application"
HOMEPAGE = "https://github.com/org/myapp"
SRCREV = "abc123def456..."
SRC_URI = "git://github.com/org/myapp;branch=main;protocol=https;destsuffix=${GO_SRCURI_DESTSUFFIX}"
include go-mod-git.inc
include go-mod-cache.inc
LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://src/import/LICENSE;md5=..."
GO_IMPORT = "import"
PV = "v1.0.0+git"
# go-mod-discovery configuration
GO_MOD_DISCOVERY_BUILD_TARGET = "./cmd/..."
GO_MOD_DISCOVERY_GIT_REPO = "https://github.com/org/myapp.git"
GO_MOD_DISCOVERY_GIT_REF = "${SRCREV}"
inherit go goarch
inherit go-mod-discovery
do_compile() {
cd ${S}/src/import
export GOPATH="${S}/src/import/.gopath:${STAGING_DIR_TARGET}/${prefix}/local/go"
export CGO_ENABLED="1"
${GO} build -trimpath -o ${B}/myapp ./cmd/myapp
}
do_install() {
install -d ${D}${bindir}
install -m 755 ${B}/myapp ${D}${bindir}/
}
Generate Dependencies
# Create placeholder files
touch go-mod-git.inc go-mod-cache.inc
# Run discovery
bitbake myapp -c discover_and_generate
# Build
bitbake myapp
Updating a Recipe to a New Version
Quick Update (Same Repository)
-
Update the SRCREV in your recipe:
SRCREV = "new_commit_hash_here" -
Re-run discovery:
bitbake myapp -c discover_and_generate -
Build:
bitbake myapp
Full Workflow Example
# 1. Find the new commit/tag
git ls-remote https://github.com/org/myapp refs/tags/v2.0.0
# 2. Update SRCREV in recipe
# SRCREV = "abc123..."
# 3. Clean old discovery cache (optional, recommended for major updates)
bitbake myapp -c clean_discovery
# 4. Run discovery and generation
bitbake myapp -c discover_and_generate
# 5. Review generated files
git diff recipes-containers/myapp/go-mod-*.inc
# 6. Build and test
bitbake myapp
# 7. Commit changes
git add recipes-containers/myapp/
git commit -m "myapp: update to v2.0.0"
Discovery Tasks Reference
| Task | Purpose | Network? |
|---|---|---|
discover_modules |
Build project and download modules from proxy | Yes |
extract_modules |
Extract metadata from cache to JSON | No |
generate_modules |
Generate .inc files from metadata | No |
discover_and_generate |
All three steps in sequence | Yes |
show_upgrade_commands |
Print command lines without running | No |
clean_discovery |
Remove the discovery cache | No |
Step-by-Step vs All-in-One
All-in-one (recommended for most cases):
bitbake myapp -c discover_and_generate
Step-by-step (useful for debugging):
bitbake myapp -c discover_modules # Download modules
bitbake myapp -c extract_modules # Extract to JSON
bitbake myapp -c generate_modules # Generate .inc files
Configuration Variables
| Variable | Default | Description |
|---|---|---|
GO_MOD_DISCOVERY_BUILD_TARGET |
(required) | Go build target (e.g., ./cmd/...) |
GO_MOD_DISCOVERY_GIT_REPO |
(required for generate) | Git repository URL |
GO_MOD_DISCOVERY_GIT_REF |
${SRCREV} |
Git commit/tag |
GO_MOD_DISCOVERY_SRCDIR |
${S}/src/import |
Directory containing go.mod |
GO_MOD_DISCOVERY_BUILD_TAGS |
${TAGS} |
Go build tags |
GO_MOD_DISCOVERY_RECIPEDIR |
${FILE_DIRNAME} |
Output directory for .inc files |
Troubleshooting
"module lookup disabled by GOPROXY=off"
This error during do_compile means a module is missing from the cache.
Fix:
# Re-run discovery to find missing modules
bitbake myapp -c discover_and_generate
bitbake myapp
Discovery Cache Location
The discovery cache persists in ${TOPDIR}/go-mod-discovery/${PN}/${PV}/ and survives bitbake -c cleanall. To fully reset:
bitbake myapp -c clean_discovery
bitbake myapp -c discover_and_generate
Viewing Generated Commands
To see what commands would be run without executing them:
bitbake myapp -c show_upgrade_commands
Build Fails After SRCREV Update
If changing SRCREV causes sstate errors:
# Clean sstate for the recipe
bitbake myapp -c cleansstate
# Re-run discovery
bitbake myapp -c discover_and_generate
# Build
bitbake myapp
Multiple Source Repositories
For recipes with multiple git sources, use named SRCREVs:
SRCREV_myapp = "abc123..."
SRCREV_plugins = "def456..."
SRCREV_FORMAT = "myapp_plugins"
SRC_URI = "\
git://github.com/org/myapp;name=myapp;branch=main;protocol=https;destsuffix=${GO_SRCURI_DESTSUFFIX} \
git://github.com/org/plugins;name=plugins;branch=main;protocol=https;destsuffix=${GO_SRCURI_DESTSUFFIX}/plugins \
"
GO_MOD_DISCOVERY_GIT_REF = "${SRCREV_myapp}"
Example Recipes
Simple Recipe (rootlesskit)
SRCREV_rootless = "8059d35092db167ec53cae95fb6aa37fc577060c"
SRCREV_FORMAT = "rootless"
SRC_URI = "git://github.com/rootless-containers/rootlesskit;name=rootless;branch=master;protocol=https;destsuffix=${GO_SRCURI_DESTSUFFIX}"
include go-mod-git.inc
include go-mod-cache.inc
GO_MOD_DISCOVERY_BUILD_TARGET = "./cmd/..."
GO_MOD_DISCOVERY_GIT_REPO = "https://github.com/rootless-containers/rootlesskit.git"
GO_MOD_DISCOVERY_GIT_REF = "${SRCREV_rootless}"
inherit go goarch go-mod-discovery
Complex Recipe (k3s with build tags)
TAGS = "static_build netcgo osusergo providerless"
GO_MOD_DISCOVERY_BUILD_TARGET = "./cmd/server/main.go"
GO_MOD_DISCOVERY_GIT_REPO = "https://github.com/rancher/k3s.git"
GO_MOD_DISCOVERY_GIT_REF = "${SRCREV_k3s}"
inherit go goarch go-mod-discovery
Files Generated
go-mod-git.inc
Contains SRC_URI entries for each module's git repository:
SRC_URI += "git://github.com/spf13/cobra;protocol=https;nobranch=1;rev=e94f6d0...;name=git_41456771_1;destsuffix=vcs_cache/2d91d6bc..."
go-mod-cache.inc
Contains module metadata and inherits the build class:
inherit go-mod-vcs
GO_MODULE_CACHE_DATA = '[{"module":"github.com/spf13/cobra","version":"v1.8.1","vcs_hash":"2d91d6bc...","timestamp":"2024-06-01T10:31:11Z","subdir":""},...]'
Advanced: Manual Script Invocation
For cases where BitBake isn't available or you need more control:
# Direct generation from git (no BitBake needed)
python3 ./meta-virtualization/scripts/oe-go-mod-fetcher.py \
--git-repo https://github.com/org/myapp.git \
--git-ref abc123... \
--recipedir ./meta-virtualization/recipes-containers/myapp/
# Using existing discovery cache
python3 ./meta-virtualization/scripts/oe-go-mod-fetcher.py \
--discovered-modules ${TOPDIR}/go-mod-discovery/myapp/v1.0.0/modules.json \
--git-repo https://github.com/org/myapp.git \
--git-ref abc123... \
--recipedir ./meta-virtualization/recipes-containers/myapp/
Getting Help
- Show available commands:
bitbake myapp -c show_upgrade_commands - Architecture details: See
scripts/ARCHITECTURE.md - Report issues: Check the generated files and error messages from discovery