summaryrefslogtreecommitdiffstats
path: root/classes/go-mod-vcs.bbclass
diff options
context:
space:
mode:
Diffstat (limited to 'classes/go-mod-vcs.bbclass')
-rw-r--r--classes/go-mod-vcs.bbclass224
1 files changed, 224 insertions, 0 deletions
diff --git a/classes/go-mod-vcs.bbclass b/classes/go-mod-vcs.bbclass
index 65211445..549fa9f5 100644
--- a/classes/go-mod-vcs.bbclass
+++ b/classes/go-mod-vcs.bbclass
@@ -1138,3 +1138,227 @@ python do_sync_go_files() {
1138} 1138}
1139 1139
1140addtask sync_go_files after do_create_module_cache before do_compile 1140addtask sync_go_files after do_create_module_cache before do_compile
1141
1142
1143do_fix_go_mod_permissions() {
1144 # Go module cache is intentionally read-only for integrity, but this breaks
1145 # BitBake's rm -rf cleanup (sstate_eventhandler_reachablestamps).
1146 # Make all files writable so workdir can be cleaned properly.
1147 #
1148 # Check multiple possible locations where Go module cache might exist
1149 for modpath in "${S}/pkg/mod" "${S}/src/import/pkg/mod"; do
1150 if [ -d "$modpath" ]; then
1151 chmod -R u+w "$modpath" 2>/dev/null || true
1152 bbnote "Fixed permissions on Go module cache: $modpath"
1153 fi
1154 done
1155 # Also check sources subdirectory (for recipes with WORKDIR/sources layout)
1156 if [ -d "${WORKDIR}/sources" ]; then
1157 find "${WORKDIR}/sources" -type d -name "mod" -path "*/pkg/mod" 2>/dev/null | while read modpath; do
1158 chmod -R u+w "$modpath" 2>/dev/null || true
1159 bbnote "Fixed permissions on Go module cache: $modpath"
1160 done
1161 fi
1162}
1163
1164# Run after sync_go_files (which is the last Go module setup task) and before compile
1165addtask fix_go_mod_permissions after do_sync_go_files before do_compile
1166
1167
1168python do_go_mod_recommend() {
1169 """
1170 Analyze VCS-fetched modules and recommend candidates for gomod:// conversion.
1171
1172 This task delegates to oe-go-mod-fetcher-hybrid.py --recommend to avoid
1173 duplicating the analysis logic. The script handles:
1174 - Module size calculation from vcs_cache
1175 - Grouping by prefix (github.com/containerd, k8s.io, etc.)
1176 - Suggesting --git prefixes for VCS-priority modules
1177 - Generating conversion command lines
1178
1179 Run with: bitbake <recipe> -c go_mod_recommend
1180 """
1181 import subprocess
1182 from pathlib import Path
1183
1184 # Find the hybrid script in meta-virtualization layer
1185 layerdir = None
1186 for layer in d.getVar('BBLAYERS').split():
1187 if 'meta-virtualization' in layer:
1188 layerdir = layer
1189 break
1190
1191 if not layerdir:
1192 bb.error("Could not find meta-virtualization layer in BBLAYERS")
1193 return
1194
1195 scriptpath = Path(layerdir) / "scripts" / "oe-go-mod-fetcher-hybrid.py"
1196 if not scriptpath.exists():
1197 bb.error(f"Hybrid script not found at {scriptpath}")
1198 return
1199
1200 # Get recipe directory and workdir
1201 recipedir = d.getVar('FILE_DIRNAME')
1202 workdir = d.getVar('WORKDIR')
1203
1204 # Build command to run the hybrid script with --recommend
1205 cmd = [
1206 'python3', str(scriptpath),
1207 '--recipedir', recipedir,
1208 '--recommend'
1209 ]
1210
1211 # Add workdir if vcs_cache exists there (for size calculations)
1212 vcs_cache = Path(workdir) / "sources" / "vcs_cache"
1213 if vcs_cache.exists():
1214 cmd.extend(['--workdir', workdir])
1215
1216 bb.note(f"Running: {' '.join(cmd)}")
1217
1218 try:
1219 result = subprocess.run(
1220 cmd,
1221 capture_output=True,
1222 text=True,
1223 timeout=300 # 5 minute timeout
1224 )
1225
1226 # Print stdout (the recommendations)
1227 if result.stdout:
1228 for line in result.stdout.splitlines():
1229 bb.plain(line)
1230
1231 # Print any errors
1232 if result.stderr:
1233 for line in result.stderr.splitlines():
1234 bb.warn(line)
1235
1236 if result.returncode != 0:
1237 bb.warn(f"Script exited with code {result.returncode}")
1238
1239 except subprocess.TimeoutExpired:
1240 bb.error("Recommendation script timed out after 5 minutes")
1241 except Exception as e:
1242 bb.error(f"Failed to run recommendation script: {e}")
1243
1244 # Always print the recipe configuration hint
1245 bb.plain("")
1246 bb.plain("=" * 80)
1247 bb.plain("RECIPE CONFIGURATION FOR SWITCHING MODES:")
1248 bb.plain("=" * 80)
1249 bb.plain("")
1250 bb.plain(" Add this to your recipe to enable switching between VCS and hybrid modes:")
1251 bb.plain("")
1252 bb.plain(' # GO_MOD_FETCH_MODE: "vcs" (all git://) or "hybrid" (gomod:// + git://)')
1253 bb.plain(' GO_MOD_FETCH_MODE ?= "vcs"')
1254 bb.plain("")
1255 bb.plain(' # VCS mode: all modules via git://')
1256 bb.plain(' include ${@ "go-mod-git.inc" if d.getVar("GO_MOD_FETCH_MODE") == "vcs" else ""}')
1257 bb.plain(' include ${@ "go-mod-cache.inc" if d.getVar("GO_MOD_FETCH_MODE") == "vcs" else ""}')
1258 bb.plain("")
1259 bb.plain(' # Hybrid mode: gomod:// for most, git:// for selected')
1260 bb.plain(' include ${@ "go-mod-hybrid-gomod.inc" if d.getVar("GO_MOD_FETCH_MODE") == "hybrid" else ""}')
1261 bb.plain(' include ${@ "go-mod-hybrid-git.inc" if d.getVar("GO_MOD_FETCH_MODE") == "hybrid" else ""}')
1262 bb.plain(' include ${@ "go-mod-hybrid-cache.inc" if d.getVar("GO_MOD_FETCH_MODE") == "hybrid" else ""}')
1263 bb.plain("")
1264 bb.plain(" Then switch modes with: GO_MOD_FETCH_MODE = \"hybrid\" in local.conf")
1265 bb.plain("")
1266}
1267
1268addtask go_mod_recommend after do_fetch
1269do_go_mod_recommend[nostamp] = "1"
1270
1271# =============================================================================
1272# Go Module Cache Permission Fix
1273# =============================================================================
1274#
1275# Go's module cache creates read-only files by design. This prevents BitBake's
1276# do_unpack cleanup (cleandirs = ${S}) from removing the previous build's
1277# module cache, causing "Permission denied" errors.
1278#
1279# Solution: Add a prefunc to do_unpack that makes the module cache writable
1280# before BitBake tries to clean the directory.
1281#
1282
1283python go_mod_fix_permissions() {
1284 """
1285 Fix Go module cache permissions before do_unpack cleanup.
1286
1287 Go creates read-only files in pkg/mod to prevent accidental modification.
1288 BitBake's cleandirs tries to rm -rf ${S} before unpacking, which fails
1289 on these read-only files. This prefunc makes them writable first.
1290 """
1291 import os
1292 import stat
1293 from pathlib import Path
1294
1295 s_dir = d.getVar('S')
1296 if not s_dir:
1297 return
1298
1299 # Check for Go module cache in various locations
1300 mod_paths = [
1301 Path(s_dir) / 'pkg' / 'mod',
1302 Path(s_dir) / 'src' / 'import' / 'pkg' / 'mod', # k3s-style layout
1303 ]
1304
1305 for mod_cache in mod_paths:
1306 if mod_cache.exists():
1307 bb.note(f"Fixing Go module cache permissions: {mod_cache}")
1308 try:
1309 # Walk the tree and add write permission
1310 for root, dirs, files in os.walk(str(mod_cache)):
1311 # Fix directory permissions first
1312 for d_name in dirs:
1313 d_path = os.path.join(root, d_name)
1314 try:
1315 current = os.stat(d_path).st_mode
1316 os.chmod(d_path, current | stat.S_IWUSR)
1317 except (OSError, PermissionError):
1318 pass
1319 # Fix file permissions
1320 for f_name in files:
1321 f_path = os.path.join(root, f_name)
1322 try:
1323 current = os.stat(f_path).st_mode
1324 os.chmod(f_path, current | stat.S_IWUSR)
1325 except (OSError, PermissionError):
1326 pass
1327 bb.note(f"Fixed permissions on {mod_cache}")
1328 except Exception as e:
1329 bb.warn(f"Could not fix permissions on {mod_cache}: {e}")
1330
1331 # Also check the WORKDIR for sources subdirectories
1332 workdir = d.getVar('WORKDIR')
1333 if workdir:
1334 sources_dir = Path(workdir) / 'sources'
1335 if sources_dir.exists():
1336 for source_subdir in sources_dir.iterdir():
1337 if source_subdir.is_dir():
1338 for mod_subpath in ['pkg/mod', 'src/import/pkg/mod']:
1339 mod_cache = source_subdir / mod_subpath
1340 if mod_cache.exists():
1341 bb.note(f"Fixing Go module cache permissions: {mod_cache}")
1342 try:
1343 for root, dirs, files in os.walk(str(mod_cache)):
1344 for d_name in dirs:
1345 d_path = os.path.join(root, d_name)
1346 try:
1347 current = os.stat(d_path).st_mode
1348 os.chmod(d_path, current | stat.S_IWUSR)
1349 except (OSError, PermissionError):
1350 pass
1351 for f_name in files:
1352 f_path = os.path.join(root, f_name)
1353 try:
1354 current = os.stat(f_path).st_mode
1355 os.chmod(f_path, current | stat.S_IWUSR)
1356 except (OSError, PermissionError):
1357 pass
1358 bb.note(f"Fixed permissions on {mod_cache}")
1359 except Exception as e:
1360 bb.warn(f"Could not fix permissions on {mod_cache}: {e}")
1361}
1362
1363# Run permission fix BEFORE do_unpack's cleandirs removes ${S}
1364do_unpack[prefuncs] += "go_mod_fix_permissions"