From 49b6f2896bd43a23ce1396f37ff8102ae10f91e8 Mon Sep 17 00:00:00 2001 From: Kristian Larsson Date: Thu, 4 Jun 2026 01:48:28 +0200 Subject: [PATCH 1/3] Generate Zig metadata before final build Root build.zig and build.zig.zon were generated inside zigBuild. That meant --skip-build refreshed dependency metadata but left the root project metadata stale or missing. Generate the root metadata in the post-compile step that already handles dependency metadata, before deciding whether to run the final Zig build. Keep root stubs, pruning, and the Zig invocation inside zigBuild. --- compiler/acton/Main.hs | 32 ++++++++++++++------------------ compiler/acton/test.hs | 4 ++-- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/compiler/acton/Main.hs b/compiler/acton/Main.hs index 552880cc9..d719e9580 100644 --- a/compiler/acton/Main.hs +++ b/compiler/acton/Main.hs @@ -1993,7 +1993,8 @@ runCliPostCompile cliHooks gopts plan env = do rootModuleEntries = excludeLibrarySources rootBuildLibraries rootSelectedModuleEntries rootDepModuleOpts = M.findWithDefault M.empty rootProj depModuleOptsByProj rootDepPathOverrides = projectDepPathOverrides projMap rootProj - -- Generate build.zig(.zon) for dependencies too, to satisfy Zig builder links. + -- Generate build.zig(.zon) before the final Zig build so --skip-build keeps + -- build metadata up to date while still skipping stubs, pruning, and Zig. let projKeys = Data.Set.fromList (map (tkProj . gtKey) globalTasks) forM_ (Data.Set.toList projKeys) $ \p -> do let isRootProj = p == rootProj @@ -2008,6 +2009,11 @@ runCliPostCompile cliHooks gopts plan env = do depPathOverrides = projectDepPathOverrides projMap p genBuildZigFiles (projBuildSpec pctx) rootPins (ccDepOverrides cctx) dummyPaths depOpts depPathOverrides Nothing -> return () + let isRootSysProj = rootProj == sysAbs || sysRoot `isPrefixOf` rootProj + unless isRootSysProj $ do + when (C.verbose gopts) $ + logLine ("Generating build.zig for project " ++ rootProj) + genBuildZigFiles rootSpec rootPins (ccDepOverrides cctx) pathsRoot rootDepModuleOpts rootDepPathOverrides let runFinal action = do cchFinalStart cliHooks mtime <- action `onException` cchFinalDone cliHooks Nothing @@ -2020,10 +2026,10 @@ runCliPostCompile cliHooks gopts plan env = do then do testBinTasks <- catMaybes <$> mapM (filterMainActor env pathsRoot) preTestBinTasks unless (altOutput opts') $ - runFinal (compileBins gopts opts' pathsRoot env rootSpec rootTasks testBinTasks allowPrune' rootModuleEntries rootBuildLibraries rootDepModuleOpts rootDepPathOverrides (Just (cchProgressUI cliHooks))) + runFinal (compileBins gopts opts' pathsRoot env rootTasks testBinTasks allowPrune' rootModuleEntries rootBuildLibraries (Just (cchProgressUI cliHooks))) else do unless (altOutput opts') $ - runFinal (compileBins gopts opts' pathsRoot env rootSpec rootTasks preBinTasks allowPrune' rootModuleEntries rootBuildLibraries rootDepModuleOpts rootDepPathOverrides (Just (cchProgressUI cliHooks))) + runFinal (compileBins gopts opts' pathsRoot env rootTasks preBinTasks allowPrune' rootModuleEntries rootBuildLibraries (Just (cchProgressUI cliHooks))) -- Generate documentation index for a project build by reading module names and -- docstrings from cached .tydb headers or source headers. generateProjectDocIndex :: Source.SourceProvider -> C.GlobalOptions -> C.CompileOptions -> Paths -> [String] -> IO () @@ -2288,9 +2294,9 @@ High-level Steps ================================================================================ -} -compileBins:: C.GlobalOptions -> C.CompileOptions -> Paths -> Acton.Env.Env0 -> BuildSpec.BuildSpec -> [CompileTask] -> [BinTask] -> Bool -> [FilePath] -> [BuildLibrary] -> M.Map String String -> M.Map String FilePath -> Maybe ProgressUI -> IO TimeSpec -compileBins gopts opts paths env rootSpec tasks binTasks allowPrune rootModules buildLibraries depModuleOpts depPathOverrides mProgressUI = - zigBuild env gopts opts paths rootSpec tasks binTasks allowPrune rootModules buildLibraries depModuleOpts depPathOverrides mProgressUI +compileBins:: C.GlobalOptions -> C.CompileOptions -> Paths -> Acton.Env.Env0 -> [CompileTask] -> [BinTask] -> Bool -> [FilePath] -> [BuildLibrary] -> Maybe ProgressUI -> IO TimeSpec +compileBins gopts opts paths env tasks binTasks allowPrune rootModules buildLibraries mProgressUI = + zigBuild env gopts opts paths tasks binTasks allowPrune rootModules buildLibraries mProgressUI printDiag :: C.GlobalOptions -> C.CompileOptions -> Diagnostic String -> IO () printDiag gopts opts d = do @@ -2703,8 +2709,8 @@ defCpuFlag = ["-Dcpu=x86_64_v2+aes"] #endif -- | Run zig build for generated artifacts and prune stale outputs. -zigBuild :: Acton.Env.Env0 -> C.GlobalOptions -> C.CompileOptions -> Paths -> BuildSpec.BuildSpec -> [CompileTask] -> [BinTask] -> Bool -> [FilePath] -> [BuildLibrary] -> M.Map String String -> M.Map String FilePath -> Maybe ProgressUI -> IO TimeSpec -zigBuild env gopts opts paths rootSpec tasks binTasks allowPrune rootModules buildLibraries depModuleOpts depPathOverrides mProgressUI = do +zigBuild :: Acton.Env.Env0 -> C.GlobalOptions -> C.CompileOptions -> Paths -> [CompileTask] -> [BinTask] -> Bool -> [FilePath] -> [BuildLibrary] -> Maybe ProgressUI -> IO TimeSpec +zigBuild env gopts opts paths tasks binTasks allowPrune rootModules buildLibraries mProgressUI = do allBinTasks <- mapM (writeRootC env gopts opts paths tasks) binTasks let realBinTasks = catMaybes allBinTasks @@ -2727,16 +2733,6 @@ zigBuild env gopts opts paths rootSpec tasks binTasks allowPrune rootModules bui let local_cache_dir = joinPath [ homeDir, ".cache", "acton", "zig-local-cache" ] global_cache_dir = joinPath [ homeDir, ".cache", "acton", "zig-global-cache" ] no_threads = if isWindowsOS (C.target opts) then True else C.no_threads opts - projAbs <- normalizePathSafe (projPath paths) - sysAbs <- normalizePathSafe (sysPath paths) - depOverrides <- normalizeDepOverrides (projPath paths) (C.dep_overrides opts) - let sysRoot = addTrailingPathSeparator sysAbs - isSysProj = projAbs == sysAbs || sysRoot `isPrefixOf` projAbs - - -- Generate build.zig and build.zig.zon directly from Build.act. - iff (not isSysProj) $ do - let pins = BuildSpec.dependencies rootSpec - genBuildZigFiles rootSpec pins depOverrides paths depModuleOpts depPathOverrides let zigExe = zig paths baseArgs = ["build","--cache-dir", local_cache_dir, diff --git a/compiler/acton/test.hs b/compiler/acton/test.hs index 550e7d9a6..7dfb3f28c 100644 --- a/compiler/acton/test.hs +++ b/compiler/acton/test.hs @@ -712,7 +712,7 @@ compilerTests = , "zig_dependencies = {}" ] runBuild dir = readCreateProcessWithExitCode - (proc actonExe ["build", "--always-build"]) { cwd = Just dir, env = Just envWithHome } "" + (proc actonExe ["build", "--always-build", "--skip-build"]) { cwd = Just dir, env = Just envWithHome } "" createDirectoryIfMissing True homeDir @@ -857,7 +857,7 @@ compilerTests = , " print(\"Hello, world\")" , " env.exit(0)" ] - runActon "build" ExitSuccess False proj + runActon "build --skip-build" ExitSuccess False proj zon <- readFile (proj "build.zig.zon") let nameVal = let isNameLine l = ".name = ." `isPrefixOf` dropWhile isSpace l From dc96dddb3e00dffae15dfd2cda75f05e8fd786bf Mon Sep 17 00:00:00 2001 From: Kristian Larsson Date: Thu, 4 Jun 2026 02:03:35 +0200 Subject: [PATCH 2/3] Keep root build.zig generation quiet Root build.zig metadata is now generated before final build execution so --skip-build callers get the files they need. That path does not need a new verbose line; leaving output unchanged preserves the existing incremental build output shape. --- compiler/acton/Main.hs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/acton/Main.hs b/compiler/acton/Main.hs index d719e9580..1a737e586 100644 --- a/compiler/acton/Main.hs +++ b/compiler/acton/Main.hs @@ -2010,9 +2010,7 @@ runCliPostCompile cliHooks gopts plan env = do genBuildZigFiles (projBuildSpec pctx) rootPins (ccDepOverrides cctx) dummyPaths depOpts depPathOverrides Nothing -> return () let isRootSysProj = rootProj == sysAbs || sysRoot `isPrefixOf` rootProj - unless isRootSysProj $ do - when (C.verbose gopts) $ - logLine ("Generating build.zig for project " ++ rootProj) + unless isRootSysProj $ genBuildZigFiles rootSpec rootPins (ccDepOverrides cctx) pathsRoot rootDepModuleOpts rootDepPathOverrides let runFinal action = do cchFinalStart cliHooks From c4741bb71f0eac272a06d7ad15155448476c834b Mon Sep 17 00:00:00 2001 From: Kristian Larsson Date: Thu, 4 Jun 2026 02:47:39 +0200 Subject: [PATCH 3/3] Skip final Zig build in metadata tests The Zig dependency collision tests only inspect generated build.zig and build.zig.zon contents. They do not need to invoke the final Zig build after the metadata has been written. Run those fixtures with --always-build --skip-build so the front end still refreshes the generated metadata without spending time linking temporary Zig artifacts. --- compiler/acton/test.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/acton/test.hs b/compiler/acton/test.hs index 7dfb3f28c..96b58c5a5 100644 --- a/compiler/acton/test.hs +++ b/compiler/acton/test.hs @@ -1449,9 +1449,9 @@ actonProjTests = writeFile (zigProj "src" "root.zig") $ unlines [ "pub export fn lmdb_test() void {}" ] - (returnCode, _cmdOut, cmdErr) <- readCreateProcessWithExitCode (proc actonExe ["build"]){ cwd = Just rootProj } "" - assertEqual "acton should build when package and zig deps share a name" ExitSuccess returnCode - assertBool "acton should not report the old dependency option collision" (not ("invalid option: -Dacton_modules" `isInfixOf` cmdErr)) + (returnCode, _cmdOut, _cmdErr) <- readCreateProcessWithExitCode + (proc actonExe ["build", "--always-build", "--skip-build"]){ cwd = Just rootProj } "" + assertEqual "acton should generate metadata when package and zig deps share a name" ExitSuccess returnCode rootZon <- readFile (rootProj "build.zig.zon") rootBuildZig <- readFile (rootProj "build.zig") assertBool "root build.zig.zon should keep the package dep key" (" .lmdb = .{" `isInfixOf` rootZon) @@ -1549,9 +1549,9 @@ actonProjTests = writeWrapper depCProj "dep_c" "../zig_other" "dep_c" writeZigPkg zigCommonProj writeZigPkg zigOtherProj - (returnCode, _cmdOut, cmdErr) <- readCreateProcessWithExitCode (proc actonExe ["build"]){ cwd = Just rootProj } "" - assertEqual "acton should build with colliding transitive zig dep names" ExitSuccess returnCode - assertBool "acton should not report the old dependency option collision" (not ("invalid option: -Dacton_modules" `isInfixOf` cmdErr)) + (returnCode, _cmdOut, _cmdErr) <- readCreateProcessWithExitCode + (proc actonExe ["build", "--always-build", "--skip-build"]){ cwd = Just rootProj } "" + assertEqual "acton should generate metadata with colliding transitive zig dep names" ExitSuccess returnCode rootZon <- readFile (rootProj "build.zig.zon") rootBuildZig <- readFile (rootProj "build.zig") assertBool "root build.zig.zon should include the first local zig dep name"