summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBruce Ashfield <bruce.ashfield@gmail.com>2025-12-05 14:26:55 +0000
committerBruce Ashfield <bruce.ashfield@gmail.com>2025-12-08 20:57:44 -0500
commitae9fa39e6f52e71052ba37c54a7e0b3aea2e7ee3 (patch)
tree1396b35a3734d45fbdc87ff28c8a69b815d536de
parent02f6761794d3a4480d10564aee3db16c83855f11 (diff)
downloadmeta-virtualization-ae9fa39e6f52e71052ba37c54a7e0b3aea2e7ee3.tar.gz
go-mod-discovery: split into more tasks
To make it easier to run individual components of the go module discovery and generation process, we add invidual tasks as well as a combined task: AVAILABLE TASKS: bitbake <recipe> -c discover_modules Build project and download modules from proxy.golang.org This populates the discovery cache but does NOT extract or generate bitbake <recipe> -c extract_modules Extract module metadata from discovery cache to modules.json Requires: discover_modules to have been run first bitbake <recipe> -c generate_modules Generate go-mod-git.inc and go-mod-cache.inc from modules.json Requires: extract_modules to have been run first bitbake <recipe> -c discover_and_generate Run all three steps: discover -> extract -> generate This is the "do everything" convenience task bitbake <recipe> -c show_upgrade_commands Show copy-pasteable command lines without running anything bitbake <recipe> -c clean_discovery Remove the persistent discovery cache Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
-rw-r--r--classes/go-mod-discovery.bbclass521
1 files changed, 307 insertions, 214 deletions
diff --git a/classes/go-mod-discovery.bbclass b/classes/go-mod-discovery.bbclass
index 0d703d0a..05265c20 100644
--- a/classes/go-mod-discovery.bbclass
+++ b/classes/go-mod-discovery.bbclass
@@ -6,15 +6,31 @@
6 6
7# go-mod-discovery.bbclass 7# go-mod-discovery.bbclass
8# 8#
9# Provides a do_discover_modules task for Go projects that downloads complete 9# Provides tasks for Go module discovery and recipe generation.
10# module metadata from proxy.golang.org for use with the bootstrap strategy.
11# 10#
12# USAGE: 11# AVAILABLE TASKS:
13# 1. Add to recipe: inherit go-mod-discovery 12#
14# 2. Set required variables (see CONFIGURATION below) 13# bitbake <recipe> -c discover_modules
15# 3. Run discovery: bitbake <recipe> -c discover_modules 14# Build project and download modules from proxy.golang.org
16# (This automatically: downloads modules, extracts metadata, regenerates recipe) 15# This populates the discovery cache but does NOT extract or generate
17# 4. Build normally: bitbake <recipe> 16#
17# bitbake <recipe> -c extract_modules
18# Extract module metadata from discovery cache to modules.json
19# Requires: discover_modules to have been run first
20#
21# bitbake <recipe> -c generate_modules
22# Generate go-mod-git.inc and go-mod-cache.inc from modules.json
23# Requires: extract_modules to have been run first
24#
25# bitbake <recipe> -c discover_and_generate
26# Run all three steps: discover -> extract -> generate
27# This is the "do everything" convenience task
28#
29# bitbake <recipe> -c show_upgrade_commands
30# Show copy-pasteable command lines without running anything
31#
32# bitbake <recipe> -c clean_discovery
33# Remove the persistent discovery cache
18# 34#
19# CONFIGURATION: 35# CONFIGURATION:
20# 36#
@@ -48,15 +64,9 @@
48# GO_MOD_DISCOVERY_MODULES_JSON - Output path for extracted module metadata 64# GO_MOD_DISCOVERY_MODULES_JSON - Output path for extracted module metadata
49# Default: "${GO_MOD_DISCOVERY_DIR}/modules.json" 65# Default: "${GO_MOD_DISCOVERY_DIR}/modules.json"
50# 66#
51# GO_MOD_DISCOVERY_SKIP_EXTRACT - Set to "1" to skip automatic extraction
52# Default: "0" (extraction runs automatically)
53#
54# GO_MOD_DISCOVERY_SKIP_GENERATE - Set to "1" to skip automatic recipe generation
55# Default: "0" (generation runs automatically)
56#
57# GO_MOD_DISCOVERY_GIT_REPO - Git repository URL for recipe generation 67# GO_MOD_DISCOVERY_GIT_REPO - Git repository URL for recipe generation
58# Example: "https://github.com/rancher/k3s.git" 68# Example: "https://github.com/rancher/k3s.git"
59# Required for automatic generation 69# Required for generate_modules task
60# 70#
61# GO_MOD_DISCOVERY_GIT_REF - Git ref (commit/tag) for recipe generation 71# GO_MOD_DISCOVERY_GIT_REF - Git ref (commit/tag) for recipe generation
62# Default: "${SRCREV}" (uses recipe's SRCREV) 72# Default: "${SRCREV}" (uses recipe's SRCREV)
@@ -64,32 +74,22 @@
64# GO_MOD_DISCOVERY_RECIPEDIR - Output directory for generated .inc files 74# GO_MOD_DISCOVERY_RECIPEDIR - Output directory for generated .inc files
65# Default: "${FILE_DIRNAME}" (recipe's directory) 75# Default: "${FILE_DIRNAME}" (recipe's directory)
66# 76#
67# MINIMAL EXAMPLE (manual generation - no GIT_REPO set): 77# WORKFLOW EXAMPLES:
68#
69# TAGS = "netcgo osusergo"
70# GO_MOD_DISCOVERY_BUILD_TARGET = "./cmd/myapp"
71# inherit go-mod-discovery
72# # Run: bitbake myapp -c discover_modules
73# # Then manually: oe-go-mod-fetcher.py --discovered-modules ... --git-repo ...
74# 78#
75# FULL AUTOMATIC EXAMPLE (all-in-one discovery + generation): 79# Full automatic (one command does everything):
80# bitbake myapp -c discover_and_generate
76# 81#
77# TAGS = "netcgo osusergo" 82# Step by step (useful for debugging or rerunning individual steps):
78# GO_MOD_DISCOVERY_BUILD_TARGET = "./cmd/myapp" 83# bitbake myapp -c discover_modules # Download modules
79# GO_MOD_DISCOVERY_GIT_REPO = "https://github.com/example/myapp.git" 84# bitbake myapp -c extract_modules # Extract metadata
80# inherit go-mod-discovery 85# bitbake myapp -c generate_modules # Generate .inc files
81# # Run: bitbake myapp -c discover_modules
82# # Recipe files are automatically regenerated!
83# 86#
84# See: meta-virtualization/scripts/BOOTSTRAP-STRATEGY.md (Approach B) 87# Skip BitBake, use scripts directly (see show_upgrade_commands):
85# 88# bitbake myapp -c show_upgrade_commands
86# This task is NOT part of the normal build - it must be explicitly invoked
87# via bitbake <recipe> -c discover_modules
88# 89#
89# PERSISTENT CACHE: The discovery cache is stored in ${TOPDIR}/go-mod-discovery/${PN}/${PV}/ 90# PERSISTENT CACHE: The discovery cache is stored in ${TOPDIR}/go-mod-discovery/${PN}/${PV}/
90# instead of ${WORKDIR}. This ensures the cache survives `bitbake <recipe> -c cleanall` 91# This ensures the cache survives `bitbake <recipe> -c cleanall`.
91# since TOPDIR is the build directory root (e.g., /path/to/build/). 92# To clean: bitbake <recipe> -c clean_discovery
92# To clean the discovery cache, run: rm -rf ${TOPDIR}/go-mod-discovery/${PN}/${PV}/
93 93
94# Required variable (must be set by recipe) 94# Required variable (must be set by recipe)
95GO_MOD_DISCOVERY_BUILD_TARGET ?= "" 95GO_MOD_DISCOVERY_BUILD_TARGET ?= ""
@@ -107,13 +107,7 @@ GO_MOD_DISCOVERY_DIR ?= "${TOPDIR}/go-mod-discovery/${PN}/${PV}"
107# Output JSON file for discovered modules (used by oe-go-mod-fetcher.py --discovered-modules) 107# Output JSON file for discovered modules (used by oe-go-mod-fetcher.py --discovered-modules)
108GO_MOD_DISCOVERY_MODULES_JSON ?= "${GO_MOD_DISCOVERY_DIR}/modules.json" 108GO_MOD_DISCOVERY_MODULES_JSON ?= "${GO_MOD_DISCOVERY_DIR}/modules.json"
109 109
110# Set to "1" to skip automatic extraction (only download modules, don't extract metadata) 110# Git repository URL for recipe generation (required for generate_modules)
111GO_MOD_DISCOVERY_SKIP_EXTRACT ?= "0"
112
113# Set to "1" to skip automatic recipe regeneration (only discover and extract)
114GO_MOD_DISCOVERY_SKIP_GENERATE ?= "0"
115
116# Git repository URL for recipe generation (required if SKIP_GENERATE != "1")
117# Example: "https://github.com/rancher/k3s.git" 111# Example: "https://github.com/rancher/k3s.git"
118GO_MOD_DISCOVERY_GIT_REPO ?= "" 112GO_MOD_DISCOVERY_GIT_REPO ?= ""
119 113
@@ -126,7 +120,14 @@ GO_MOD_DISCOVERY_RECIPEDIR ?= "${FILE_DIRNAME}"
126# Empty default for TAGS if not set by recipe (avoids undefined variable errors) 120# Empty default for TAGS if not set by recipe (avoids undefined variable errors)
127TAGS ?= "" 121TAGS ?= ""
128 122
129# Shell task that mirrors do_compile but with network access and discovery GOMODCACHE 123# =============================================================================
124# TASK 1: do_discover_modules - Build and download modules
125# =============================================================================
126# This task builds the project with network access to discover and download
127# all required Go modules from proxy.golang.org into a persistent cache.
128#
129# Usage: bitbake <recipe> -c discover_modules
130#
130do_discover_modules() { 131do_discover_modules() {
131 # Validate required variable 132 # Validate required variable
132 if [ -z "${GO_MOD_DISCOVERY_BUILD_TARGET}" ]; then 133 if [ -z "${GO_MOD_DISCOVERY_BUILD_TARGET}" ]; then
@@ -144,10 +145,9 @@ Hint: Set GO_MOD_DISCOVERY_SRCDIR to the directory containing go.mod"
144 fi 145 fi
145 146
146 # Use PERSISTENT cache location outside WORKDIR to survive cleanall 147 # Use PERSISTENT cache location outside WORKDIR to survive cleanall
147 # This is stored in ${TOPDIR}/go-mod-discovery/${PN}/${PV}/ so it persists
148 DISCOVERY_CACHE="${GO_MOD_DISCOVERY_DIR}/cache" 148 DISCOVERY_CACHE="${GO_MOD_DISCOVERY_DIR}/cache"
149 149
150 # Create required directories first 150 # Create required directories
151 mkdir -p "${DISCOVERY_CACHE}" 151 mkdir -p "${DISCOVERY_CACHE}"
152 mkdir -p "${WORKDIR}/go-tmp" 152 mkdir -p "${WORKDIR}/go-tmp"
153 mkdir -p "$(dirname "${GO_MOD_DISCOVERY_OUTPUT}")" 153 mkdir -p "$(dirname "${GO_MOD_DISCOVERY_OUTPUT}")"
@@ -159,21 +159,18 @@ Hint: Set GO_MOD_DISCOVERY_SRCDIR to the directory containing go.mod"
159 export GOPROXY="https://proxy.golang.org,direct" 159 export GOPROXY="https://proxy.golang.org,direct"
160 export GOSUMDB="sum.golang.org" 160 export GOSUMDB="sum.golang.org"
161 161
162 # Standard Go environment - use recipe-provided GOPATH or default 162 # Standard Go environment
163 export GOPATH="${GO_MOD_DISCOVERY_GOPATH}:${STAGING_DIR_TARGET}/${prefix}/local/go" 163 export GOPATH="${GO_MOD_DISCOVERY_GOPATH}:${STAGING_DIR_TARGET}/${prefix}/local/go"
164 export CGO_ENABLED="1" 164 export CGO_ENABLED="1"
165 export GOTOOLCHAIN="local" 165 export GOTOOLCHAIN="local"
166
167 # Use system temp directory for Go's work files
168 export GOTMPDIR="${WORKDIR}/go-tmp" 166 export GOTMPDIR="${WORKDIR}/go-tmp"
169 167
170 # Disable excessive debug output from BitBake environment 168 # Disable excessive debug output
171 unset GODEBUG 169 unset GODEBUG
172 170
173 # Build tags from recipe configuration 171 # Build tags from recipe configuration
174 TAGS="${GO_MOD_DISCOVERY_BUILD_TAGS}" 172 TAGS="${GO_MOD_DISCOVERY_BUILD_TAGS}"
175 173
176 # Change to source directory
177 cd "${GO_MOD_DISCOVERY_SRCDIR}" 174 cd "${GO_MOD_DISCOVERY_SRCDIR}"
178 175
179 echo "======================================================================" 176 echo "======================================================================"
@@ -187,21 +184,11 @@ Hint: Set GO_MOD_DISCOVERY_SRCDIR to the directory containing go.mod"
187 echo "LDFLAGS: ${GO_MOD_DISCOVERY_LDFLAGS}" 184 echo "LDFLAGS: ${GO_MOD_DISCOVERY_LDFLAGS}"
188 echo "" 185 echo ""
189 186
190 # Use native go binary (not cross-compiler) 187 # Use native go binary
191 GO_NATIVE="${STAGING_DIR_NATIVE}${bindir_native}/go" 188 GO_NATIVE="${STAGING_DIR_NATIVE}${bindir_native}/go"
192 189
193 # NOTE: Do NOT run go mod tidy during discovery - it can upgrade versions in go.mod
194 # without adding checksums to go.sum, causing version mismatches.
195 # The source's go.mod/go.sum should already be correct for the commit.
196 # echo "Running: go mod tidy"
197 # ${GO_NATIVE} mod tidy
198 # ${GO_NATIVE} mod download # If tidy is re-enabled, this ensures go.sum gets all checksums
199
200 echo ""
201 echo "Running: go build (to discover all modules)..." 190 echo "Running: go build (to discover all modules)..."
202 191
203 # Build to discover ALL modules that would be used at compile time
204 # This is better than 'go mod download' because it handles build tags correctly
205 BUILD_CMD="${GO_NATIVE} build -v -trimpath" 192 BUILD_CMD="${GO_NATIVE} build -v -trimpath"
206 if [ -n "${TAGS}" ]; then 193 if [ -n "${TAGS}" ]; then
207 BUILD_CMD="${BUILD_CMD} -tags \"${TAGS}\"" 194 BUILD_CMD="${BUILD_CMD} -tags \"${TAGS}\""
@@ -214,68 +201,39 @@ Hint: Set GO_MOD_DISCOVERY_SRCDIR to the directory containing go.mod"
214 201
215 echo "" 202 echo ""
216 echo "Fetching ALL modules referenced in go.sum..." 203 echo "Fetching ALL modules referenced in go.sum..."
217 # go build downloads .zip files but not always .info files
218 # We need .info files for VCS metadata (Origin.URL, Origin.Hash)
219 # Extract unique module@version pairs from go.sum and download each
220 # go.sum format: "module version/go.mod hash" or "module version hash"
221 #
222 # IMPORTANT: We must download ALL versions, including /go.mod-only entries!
223 # When GOPROXY=off during compile, Go may need these for dependency resolution.
224 # Strip the /go.mod suffix to get the base version, then download it.
225 awk '{gsub(/\/go\.mod$/, "", $2); print $1 "@" $2}' go.sum | sort -u | while read modver; do 204 awk '{gsub(/\/go\.mod$/, "", $2); print $1 "@" $2}' go.sum | sort -u | while read modver; do
226 ${GO_NATIVE} mod download "$modver" 2>/dev/null || true 205 ${GO_NATIVE} mod download "$modver" 2>/dev/null || true
227 done 206 done
228 207
229 # Download ALL modules in the complete dependency graph.
230 # The go.sum loop above only gets direct dependencies. Replace directives
231 # can introduce transitive deps that aren't in go.sum but are needed at
232 # compile time when GOPROXY=off. `go mod download all` resolves and
233 # downloads the entire module graph, including transitive dependencies.
234 echo "" 208 echo ""
235 echo "Downloading complete module graph (including transitive deps)..." 209 echo "Downloading complete module graph (including transitive deps)..."
236 ${GO_NATIVE} mod download all 2>&1 || echo "Warning: some modules may have failed to download" 210 ${GO_NATIVE} mod download all 2>&1 || echo "Warning: some modules may have failed to download"
237 211
238 # Additionally scan for any modules that go build downloaded but don't have .info
239 # This ensures we capture everything that was fetched dynamically
240 echo "" 212 echo ""
241 echo "Ensuring .info files for all cached modules..." 213 echo "Ensuring .info files for all cached modules..."
242 find "${GOMODCACHE}/cache/download" -name "*.zip" 2>/dev/null | while read zipfile; do 214 find "${GOMODCACHE}/cache/download" -name "*.zip" 2>/dev/null | while read zipfile; do
243 # Extract module@version from path like: .../module/@v/version.zip
244 version=$(basename "$zipfile" .zip) 215 version=$(basename "$zipfile" .zip)
245 moddir=$(dirname "$zipfile") 216 moddir=$(dirname "$zipfile")
246 infofile="${moddir}/${version}.info" 217 infofile="${moddir}/${version}.info"
247 if [ ! -f "$infofile" ]; then 218 if [ ! -f "$infofile" ]; then
248 # Reconstruct module path from directory structure
249 # cache/download/github.com/foo/bar/@v/v1.0.0.zip -> github.com/foo/bar@v1.0.0
250 modpath=$(echo "$moddir" | sed "s|${GOMODCACHE}/cache/download/||" | sed 's|/@v$||') 219 modpath=$(echo "$moddir" | sed "s|${GOMODCACHE}/cache/download/||" | sed 's|/@v$||')
251 echo " Fetching .info for: ${modpath}@${version}" 220 echo " Fetching .info for: ${modpath}@${version}"
252 ${GO_NATIVE} mod download "${modpath}@${version}" 2>/dev/null || true 221 ${GO_NATIVE} mod download "${modpath}@${version}" 2>/dev/null || true
253 fi 222 fi
254 done 223 done
255 224
256 # Download transitive deps of REPLACED modules.
257 # Replace directives can point to older versions whose deps aren't in the MVS
258 # graph. At compile time with GOPROXY=off, Go validates the replaced version's
259 # go.mod. We parse replace directives and download each replacement version,
260 # which fetches all its transitive dependencies.
261 echo "" 225 echo ""
262 echo "Downloading dependencies of replaced modules..." 226 echo "Downloading dependencies of replaced modules..."
263
264 # Extract replace directives: "old_module => new_module new_version"
265 awk '/^replace \($/,/^\)$/ {if ($0 !~ /^replace|^\)/) print}' go.mod | \ 227 awk '/^replace \($/,/^\)$/ {if ($0 !~ /^replace|^\)/) print}' go.mod | \
266 grep "=>" | while read line; do 228 grep "=>" | while read line; do
267 # Parse: github.com/foo/bar => github.com/baz/qux v1.2.3
268 new_module=$(echo "$line" | awk '{print $(NF-1)}') 229 new_module=$(echo "$line" | awk '{print $(NF-1)}')
269 new_version=$(echo "$line" | awk '{print $NF}') 230 new_version=$(echo "$line" | awk '{print $NF}')
270
271 if [ -n "$new_module" ] && [ -n "$new_version" ] && [ "$new_version" != "=>" ]; then 231 if [ -n "$new_module" ] && [ -n "$new_version" ] && [ "$new_version" != "=>" ]; then
272 echo " Replace target: ${new_module}@${new_version}" 232 echo " Replace target: ${new_module}@${new_version}"
273 # Download this specific version - Go will fetch all its dependencies
274 ${GO_NATIVE} mod download "${new_module}@${new_version}" 2>/dev/null || true 233 ${GO_NATIVE} mod download "${new_module}@${new_version}" 2>/dev/null || true
275 fi 234 fi
276 done 235 done
277 236
278 # Count modules discovered
279 MODULE_COUNT=$(find "${GOMODCACHE}/cache/download" -name "*.info" 2>/dev/null | wc -l) 237 MODULE_COUNT=$(find "${GOMODCACHE}/cache/download" -name "*.info" 2>/dev/null | wc -l)
280 238
281 echo "" 239 echo ""
@@ -284,148 +242,190 @@ Hint: Set GO_MOD_DISCOVERY_SRCDIR to the directory containing go.mod"
284 echo "======================================================================" 242 echo "======================================================================"
285 echo "Modules discovered: ${MODULE_COUNT}" 243 echo "Modules discovered: ${MODULE_COUNT}"
286 echo "Cache location: ${GOMODCACHE}" 244 echo "Cache location: ${GOMODCACHE}"
245 echo ""
246 echo "Next steps:"
247 echo " bitbake ${PN} -c extract_modules # Extract metadata to JSON"
248 echo " bitbake ${PN} -c generate_modules # Generate .inc files"
249 echo ""
250 echo "Or run all at once:"
251 echo " bitbake ${PN} -c discover_and_generate"
252 echo ""
253}
287 254
288 # Extract module metadata automatically (unless skipped) 255addtask discover_modules after do_patch
289 if [ "${GO_MOD_DISCOVERY_SKIP_EXTRACT}" != "1" ]; then 256do_discover_modules[depends] = "${PN}:do_prepare_recipe_sysroot"
290 echo "" 257do_discover_modules[network] = "1"
291 echo "Extracting module metadata..." 258do_discover_modules[nostamp] = "1"
259do_discover_modules[vardeps] += "GO_MOD_DISCOVERY_DIR GO_MOD_DISCOVERY_SRCDIR \
260 GO_MOD_DISCOVERY_BUILD_TARGET GO_MOD_DISCOVERY_BUILD_TAGS \
261 GO_MOD_DISCOVERY_LDFLAGS GO_MOD_DISCOVERY_GOPATH GO_MOD_DISCOVERY_OUTPUT"
292 262
293 # Find the extraction script relative to this class file 263# =============================================================================
294 EXTRACT_SCRIPT="${COREBASE}/../meta-virtualization/scripts/extract-discovered-modules.py" 264# TASK 2: do_extract_modules - Extract metadata from cache
295 if [ ! -f "${EXTRACT_SCRIPT}" ]; then 265# =============================================================================
296 # Try alternate location 266# This task extracts module metadata from the discovery cache into a JSON file.
297 EXTRACT_SCRIPT="$(dirname "${COREBASE}")/meta-virtualization/scripts/extract-discovered-modules.py" 267# The JSON file can then be used with oe-go-mod-fetcher.py --discovered-modules.
298 fi 268#
299 if [ ! -f "${EXTRACT_SCRIPT}" ]; then 269# Usage: bitbake <recipe> -c extract_modules
300 # Last resort - search in layer path 270#
301 for layer in ${BBLAYERS}; do 271do_extract_modules() {
302 if [ -f "${layer}/scripts/extract-discovered-modules.py" ]; then 272 DISCOVERY_CACHE="${GO_MOD_DISCOVERY_DIR}/cache"
303 EXTRACT_SCRIPT="${layer}/scripts/extract-discovered-modules.py"
304 break
305 fi
306 done
307 fi
308 273
309 if [ -f "${EXTRACT_SCRIPT}" ]; then 274 if [ ! -d "${DISCOVERY_CACHE}/cache/download" ]; then
310 python3 "${EXTRACT_SCRIPT}" \ 275 bbfatal "Discovery cache not found: ${DISCOVERY_CACHE}
311 --gomodcache "${GOMODCACHE}" \ 276Run 'bitbake ${PN} -c discover_modules' first to populate the cache."
312 --output "${GO_MOD_DISCOVERY_MODULES_JSON}"
313 EXTRACT_RC=$?
314 if [ $EXTRACT_RC -eq 0 ]; then
315 echo ""
316 echo "✓ Module metadata extracted to: ${GO_MOD_DISCOVERY_MODULES_JSON}"
317 else
318 bbwarn "Module extraction failed (exit code $EXTRACT_RC)"
319 bbwarn "You can run manually: python3 ${EXTRACT_SCRIPT} --gomodcache ${GOMODCACHE} --output ${GO_MOD_DISCOVERY_MODULES_JSON}"
320 EXTRACT_RC=1 # Mark as failed for generation check
321 fi
322 else
323 bbwarn "Could not find extract-discovered-modules.py script"
324 bbwarn "Run manually: extract-discovered-modules.py --gomodcache ${GOMODCACHE} --output ${GO_MOD_DISCOVERY_MODULES_JSON}"
325 EXTRACT_RC=1 # Mark as failed for generation check
326 fi
327 else
328 echo ""
329 echo "Skipping automatic extraction (GO_MOD_DISCOVERY_SKIP_EXTRACT=1)"
330 EXTRACT_RC=1 # Skip generation too if extraction skipped
331 fi 277 fi
332 278
333 # Step 3: Generate recipe .inc files (unless skipped or extraction failed) 279 echo "======================================================================"
334 if [ "${GO_MOD_DISCOVERY_SKIP_GENERATE}" != "1" ] && [ "${EXTRACT_RC:-0}" = "0" ]; then 280 echo "EXTRACTING MODULE METADATA: ${PN} ${PV}"
335 # Validate required git repo 281 echo "======================================================================"
336 if [ -z "${GO_MOD_DISCOVERY_GIT_REPO}" ]; then 282 echo "Cache: ${DISCOVERY_CACHE}"
337 bbwarn "GO_MOD_DISCOVERY_GIT_REPO not set - skipping recipe generation" 283 echo "Output: ${GO_MOD_DISCOVERY_MODULES_JSON}"
338 bbwarn "Set GO_MOD_DISCOVERY_GIT_REPO in your recipe to enable automatic generation" 284 echo ""
339 echo "" 285
340 echo "NEXT STEP: Regenerate recipe manually:" 286 # Find the extraction script
341 echo "" 287 EXTRACT_SCRIPT=""
342 echo " ./meta-virtualization/scripts/oe-go-mod-fetcher.py \\" 288 for layer in ${BBLAYERS}; do
343 echo " --discovered-modules ${GO_MOD_DISCOVERY_MODULES_JSON} \\" 289 if [ -f "${layer}/scripts/extract-discovered-modules.py" ]; then
344 echo " --git-repo <your-git-repo-url> \\" 290 EXTRACT_SCRIPT="${layer}/scripts/extract-discovered-modules.py"
345 echo " --git-ref ${GO_MOD_DISCOVERY_GIT_REF} \\" 291 break
346 echo " --recipedir ${GO_MOD_DISCOVERY_RECIPEDIR}"
347 else
348 echo ""
349 echo "Generating recipe .inc files..."
350
351 # Find the fetcher script (same search as extraction script)
352 FETCHER_SCRIPT="${COREBASE}/../meta-virtualization/scripts/oe-go-mod-fetcher.py"
353 if [ ! -f "${FETCHER_SCRIPT}" ]; then
354 FETCHER_SCRIPT="$(dirname "${COREBASE}")/meta-virtualization/scripts/oe-go-mod-fetcher.py"
355 fi
356 if [ ! -f "${FETCHER_SCRIPT}" ]; then
357 for layer in ${BBLAYERS}; do
358 if [ -f "${layer}/scripts/oe-go-mod-fetcher.py" ]; then
359 FETCHER_SCRIPT="${layer}/scripts/oe-go-mod-fetcher.py"
360 break
361 fi
362 done
363 fi
364
365 if [ -f "${FETCHER_SCRIPT}" ]; then
366 python3 "${FETCHER_SCRIPT}" \
367 --discovered-modules "${GO_MOD_DISCOVERY_MODULES_JSON}" \
368 --git-repo "${GO_MOD_DISCOVERY_GIT_REPO}" \
369 --git-ref "${GO_MOD_DISCOVERY_GIT_REF}" \
370 --recipedir "${GO_MOD_DISCOVERY_RECIPEDIR}"
371 GENERATE_RC=$?
372 if [ $GENERATE_RC -eq 0 ]; then
373 echo ""
374 echo "✓ Recipe files regenerated in: ${GO_MOD_DISCOVERY_RECIPEDIR}"
375 else
376 bbwarn "Recipe generation failed (exit code $GENERATE_RC)"
377 bbwarn "Check the output above for errors"
378 fi
379 else
380 bbwarn "Could not find oe-go-mod-fetcher.py script"
381 bbwarn "Run manually: oe-go-mod-fetcher.py --discovered-modules ${GO_MOD_DISCOVERY_MODULES_JSON} --git-repo ${GO_MOD_DISCOVERY_GIT_REPO} --git-ref ${GO_MOD_DISCOVERY_GIT_REF} --recipedir ${GO_MOD_DISCOVERY_RECIPEDIR}"
382 fi
383 fi 292 fi
384 elif [ "${GO_MOD_DISCOVERY_SKIP_GENERATE}" = "1" ]; then 293 done
294
295 if [ -z "${EXTRACT_SCRIPT}" ]; then
296 bbfatal "Could not find extract-discovered-modules.py in any layer"
297 fi
298
299 python3 "${EXTRACT_SCRIPT}" \
300 --gomodcache "${DISCOVERY_CACHE}" \
301 --output "${GO_MOD_DISCOVERY_MODULES_JSON}"
302
303 if [ $? -eq 0 ]; then
304 MODULE_COUNT=$(python3 -c "import json; print(len(json.load(open('${GO_MOD_DISCOVERY_MODULES_JSON}'))['modules']))" 2>/dev/null || echo "?")
385 echo "" 305 echo ""
386 echo "Skipping automatic generation (GO_MOD_DISCOVERY_SKIP_GENERATE=1)" 306 echo "======================================================================"
307 echo "EXTRACTION COMPLETE"
308 echo "======================================================================"
309 echo "Modules extracted: ${MODULE_COUNT}"
310 echo "Output file: ${GO_MOD_DISCOVERY_MODULES_JSON}"
387 echo "" 311 echo ""
388 echo "NEXT STEP: Regenerate recipe manually:" 312 echo "Next step:"
313 echo " bitbake ${PN} -c generate_modules"
389 echo "" 314 echo ""
390 echo " ./meta-virtualization/scripts/oe-go-mod-fetcher.py \\" 315 else
391 echo " --discovered-modules ${GO_MOD_DISCOVERY_MODULES_JSON} \\" 316 bbfatal "Module extraction failed"
392 echo " --git-repo <your-git-repo-url> \\" 317 fi
393 echo " --git-ref <your-git-ref> \\" 318}
394 echo " --recipedir ${GO_MOD_DISCOVERY_RECIPEDIR}" 319
320addtask extract_modules
321do_extract_modules[nostamp] = "1"
322do_extract_modules[vardeps] += "GO_MOD_DISCOVERY_DIR GO_MOD_DISCOVERY_MODULES_JSON"
323
324# =============================================================================
325# TASK 3: do_generate_modules - Generate .inc files
326# =============================================================================
327# This task generates go-mod-git.inc and go-mod-cache.inc from the extracted
328# modules.json file.
329#
330# Usage: bitbake <recipe> -c generate_modules
331#
332do_generate_modules() {
333 if [ ! -f "${GO_MOD_DISCOVERY_MODULES_JSON}" ]; then
334 bbfatal "Modules JSON not found: ${GO_MOD_DISCOVERY_MODULES_JSON}
335Run 'bitbake ${PN} -c extract_modules' first to create the modules file."
336 fi
337
338 if [ -z "${GO_MOD_DISCOVERY_GIT_REPO}" ]; then
339 bbfatal "GO_MOD_DISCOVERY_GIT_REPO must be set for recipe generation.
340Add to your recipe: GO_MOD_DISCOVERY_GIT_REPO = \"https://github.com/...\"
341Or run 'bitbake ${PN} -c show_upgrade_commands' to see manual options."
395 fi 342 fi
396 343
397 echo ""
398 echo "NOTE: Cache is stored OUTSIDE WORKDIR in a persistent location."
399 echo " This cache survives 'bitbake ${PN} -c cleanall'!"
400 echo " To clean: rm -rf ${GO_MOD_DISCOVERY_DIR}"
401 echo ""
402 echo "======================================================================" 344 echo "======================================================================"
403} 345 echo "GENERATING RECIPE FILES: ${PN} ${PV}"
346 echo "======================================================================"
347 echo "Modules JSON: ${GO_MOD_DISCOVERY_MODULES_JSON}"
348 echo "Git repo: ${GO_MOD_DISCOVERY_GIT_REPO}"
349 echo "Git ref: ${GO_MOD_DISCOVERY_GIT_REF}"
350 echo "Recipe dir: ${GO_MOD_DISCOVERY_RECIPEDIR}"
351 echo ""
404 352
405# Make this task manually runnable (not part of default build) 353 # Find the fetcher script
406# Run after unpack and patch so source is available 354 FETCHER_SCRIPT=""
407addtask discover_modules after do_patch 355 for layer in ${BBLAYERS}; do
356 if [ -f "${layer}/scripts/oe-go-mod-fetcher.py" ]; then
357 FETCHER_SCRIPT="${layer}/scripts/oe-go-mod-fetcher.py"
358 break
359 fi
360 done
408 361
409# Task dependencies - need source unpacked and full toolchain available 362 if [ -z "${FETCHER_SCRIPT}" ]; then
410# Depend on do_prepare_recipe_sysroot to get cross-compiler for CGO 363 bbfatal "Could not find oe-go-mod-fetcher.py in any layer"
411do_discover_modules[depends] = "${PN}:do_prepare_recipe_sysroot" 364 fi
412 365
413# Enable network access for this task ONLY 366 python3 "${FETCHER_SCRIPT}" \
414do_discover_modules[network] = "1" 367 --discovered-modules "${GO_MOD_DISCOVERY_MODULES_JSON}" \
368 --git-repo "${GO_MOD_DISCOVERY_GIT_REPO}" \
369 --git-ref "${GO_MOD_DISCOVERY_GIT_REF}" \
370 --recipedir "${GO_MOD_DISCOVERY_RECIPEDIR}"
415 371
416# Don't create stamp file - allow running multiple times 372 if [ $? -eq 0 ]; then
417do_discover_modules[nostamp] = "1" 373 echo ""
374 echo "======================================================================"
375 echo "GENERATION COMPLETE"
376 echo "======================================================================"
377 echo "Files generated in: ${GO_MOD_DISCOVERY_RECIPEDIR}"
378 echo " - go-mod-git.inc"
379 echo " - go-mod-cache.inc"
380 echo ""
381 echo "You can now build the recipe:"
382 echo " bitbake ${PN}"
383 echo ""
384 else
385 bbfatal "Recipe generation failed"
386 fi
387}
418 388
419# Track all configuration variables for proper task hashing 389addtask generate_modules
420do_discover_modules[vardeps] += "GO_MOD_DISCOVERY_DIR GO_MOD_DISCOVERY_SRCDIR \ 390do_generate_modules[nostamp] = "1"
421 GO_MOD_DISCOVERY_BUILD_TARGET GO_MOD_DISCOVERY_BUILD_TAGS \ 391do_generate_modules[vardeps] += "GO_MOD_DISCOVERY_MODULES_JSON GO_MOD_DISCOVERY_GIT_REPO \
422 GO_MOD_DISCOVERY_LDFLAGS GO_MOD_DISCOVERY_GOPATH GO_MOD_DISCOVERY_OUTPUT \
423 GO_MOD_DISCOVERY_MODULES_JSON GO_MOD_DISCOVERY_SKIP_EXTRACT \
424 GO_MOD_DISCOVERY_SKIP_GENERATE GO_MOD_DISCOVERY_GIT_REPO \
425 GO_MOD_DISCOVERY_GIT_REF GO_MOD_DISCOVERY_RECIPEDIR" 392 GO_MOD_DISCOVERY_GIT_REF GO_MOD_DISCOVERY_RECIPEDIR"
426 393
427# Task to clean the persistent discovery cache 394# =============================================================================
428# Usage: bitbake <recipe> -c clean_discovery 395# TASK 4: do_discover_and_generate - All-in-one convenience task
396# =============================================================================
397# This task runs discover_modules, extract_modules, and generate_modules
398# in sequence. It's the "do everything" option.
399#
400# Usage: bitbake <recipe> -c discover_and_generate
401#
402do_discover_and_generate() {
403 echo "======================================================================"
404 echo "FULL DISCOVERY AND GENERATION: ${PN} ${PV}"
405 echo "======================================================================"
406 echo ""
407 echo "This task will run:"
408 echo " 1. discover_modules - Build and download modules"
409 echo " 2. extract_modules - Extract metadata to JSON"
410 echo " 3. generate_modules - Generate .inc files"
411 echo ""
412}
413
414# Chain the tasks together using task dependencies
415python do_discover_and_generate_setdeps() {
416 # This runs discover -> extract -> generate in sequence
417 pass
418}
419
420addtask discover_and_generate after do_patch
421do_discover_and_generate[depends] = "${PN}:do_prepare_recipe_sysroot"
422do_discover_and_generate[network] = "1"
423do_discover_and_generate[nostamp] = "1"
424do_discover_and_generate[postfuncs] = "do_discover_modules do_extract_modules do_generate_modules"
425
426# =============================================================================
427# TASK: do_clean_discovery - Clean the persistent cache
428# =============================================================================
429do_clean_discovery() { 429do_clean_discovery() {
430 if [ -d "${GO_MOD_DISCOVERY_DIR}" ]; then 430 if [ -d "${GO_MOD_DISCOVERY_DIR}" ]; then
431 echo "Removing discovery cache: ${GO_MOD_DISCOVERY_DIR}" 431 echo "Removing discovery cache: ${GO_MOD_DISCOVERY_DIR}"
@@ -439,3 +439,96 @@ do_clean_discovery() {
439addtask clean_discovery 439addtask clean_discovery
440do_clean_discovery[nostamp] = "1" 440do_clean_discovery[nostamp] = "1"
441do_clean_discovery[vardeps] += "GO_MOD_DISCOVERY_DIR" 441do_clean_discovery[vardeps] += "GO_MOD_DISCOVERY_DIR"
442
443# =============================================================================
444# TASK: do_show_upgrade_commands - Show command lines without running
445# =============================================================================
446python do_show_upgrade_commands() {
447 import os
448
449 pn = d.getVar('PN')
450 pv = d.getVar('PV')
451 git_repo = d.getVar('GO_MOD_DISCOVERY_GIT_REPO') or '<GIT_REPO_URL>'
452 git_ref = d.getVar('GO_MOD_DISCOVERY_GIT_REF') or d.getVar('SRCREV') or '<GIT_REF>'
453 recipedir = d.getVar('GO_MOD_DISCOVERY_RECIPEDIR') or d.getVar('FILE_DIRNAME')
454 discovery_dir = d.getVar('GO_MOD_DISCOVERY_DIR')
455 modules_json = d.getVar('GO_MOD_DISCOVERY_MODULES_JSON')
456
457 # Find script locations
458 fetcher_script = None
459 extract_script = None
460 for layer in d.getVar('BBLAYERS').split():
461 candidate = os.path.join(layer, 'scripts', 'oe-go-mod-fetcher.py')
462 if os.path.exists(candidate):
463 fetcher_script = candidate
464 candidate = os.path.join(layer, 'scripts', 'extract-discovered-modules.py')
465 if os.path.exists(candidate):
466 extract_script = candidate
467
468 fetcher_script = fetcher_script or './meta-virtualization/scripts/oe-go-mod-fetcher.py'
469 extract_script = extract_script or './meta-virtualization/scripts/extract-discovered-modules.py'
470
471 bb.plain("")
472 bb.plain("=" * 70)
473 bb.plain(f"UPGRADE COMMANDS FOR: {pn} {pv}")
474 bb.plain("=" * 70)
475 bb.plain("")
476 bb.plain("Option 1: Generate from git repository (no BitBake required)")
477 bb.plain("-" * 70)
478 bb.plain("")
479 bb.plain("Run from your build directory:")
480 bb.plain("")
481 bb.plain(f" {fetcher_script} \\")
482 bb.plain(f" --git-repo {git_repo} \\")
483 bb.plain(f" --git-ref {git_ref} \\")
484 bb.plain(f" --recipedir {recipedir}")
485 bb.plain("")
486 bb.plain("")
487 bb.plain("Option 2: BitBake discovery (step by step)")
488 bb.plain("-" * 70)
489 bb.plain("")
490 bb.plain(f" bitbake {pn} -c discover_modules # Download modules (needs network)")
491 bb.plain(f" bitbake {pn} -c extract_modules # Extract metadata to JSON")
492 bb.plain(f" bitbake {pn} -c generate_modules # Generate .inc files")
493 bb.plain("")
494 bb.plain("")
495 bb.plain("Option 3: BitBake discovery (all-in-one)")
496 bb.plain("-" * 70)
497 bb.plain("")
498 bb.plain(f" bitbake {pn} -c discover_and_generate")
499 bb.plain("")
500 bb.plain("")
501 bb.plain("Option 4: Use existing discovery cache")
502 bb.plain("-" * 70)
503 bb.plain("")
504 bb.plain(f"Discovery cache: {discovery_dir}")
505 bb.plain("")
506 bb.plain("Extract modules from cache:")
507 bb.plain("")
508 bb.plain(f" {extract_script} \\")
509 bb.plain(f" --gomodcache {discovery_dir}/cache \\")
510 bb.plain(f" --output {modules_json}")
511 bb.plain("")
512 bb.plain("Then generate .inc files:")
513 bb.plain("")
514 bb.plain(f" {fetcher_script} \\")
515 bb.plain(f" --discovered-modules {modules_json} \\")
516 bb.plain(f" --git-repo {git_repo} \\")
517 bb.plain(f" --git-ref {git_ref} \\")
518 bb.plain(f" --recipedir {recipedir}")
519 bb.plain("")
520 bb.plain("")
521 bb.plain("Generated files:")
522 bb.plain("-" * 70)
523 bb.plain("")
524 bb.plain(" go-mod-git.inc - SRC_URI entries for fetching module git repos")
525 bb.plain(" go-mod-cache.inc - Module path mappings for cache creation")
526 bb.plain("")
527 bb.plain("=" * 70)
528 bb.plain("")
529}
530
531addtask show_upgrade_commands
532do_show_upgrade_commands[nostamp] = "1"
533do_show_upgrade_commands[vardeps] += "GO_MOD_DISCOVERY_GIT_REPO GO_MOD_DISCOVERY_GIT_REF \
534 GO_MOD_DISCOVERY_RECIPEDIR GO_MOD_DISCOVERY_DIR GO_MOD_DISCOVERY_MODULES_JSON"