#!/usr/bin/env lua -- zeta-makepkg — build Zeta binary packages from declarative .recipe files. -- -- Usage: -- zeta-makepkg [flags] Build a package from a recipe -- zeta-makepkg --index [flags] Regenerate packages/index.lua -- -- Flags: -- --output Output repository root (default: .) -- --no-index Skip index.lua update -- --keep-work Preserve build work directory -- --force Overwrite existing package -- --help Show this help -- -- Pipeline: -- LOAD recipe → FETCH source → EXTRACT → BUILD → TEST → PACKAGE → INDEX -- Lua 5.1 compat: arg[0] is the script path, not the interpreter. local here if arg and arg[0] and arg[0]:match("[/\\]") then here = arg[0]:match("^(.*)[/\\][^/\\]*$") elseif arg and arg[-1] then -- Lua 5.1 sends script path as arg[-1] when using env. here = arg[-1]:match("^(.*)[/\\][^/\\]*$") else here = "." end -- Resolve the real directory in case we were invoked via a symlink. if here then local f = io.popen("cd " .. here:gsub("'", "'\\''") .. " 2>/dev/null && pwd") if f then local real = f:read("*l") f:close() if real and real ~= "" then here = real end end else here = "." end -- Library path: works both from the repo tree (../lib/) and when installed -- via 'make install' (/usr/local/lib/zeta-toolchain/). package.path = here .. "/../lib/?.lua;" .. here .. "/../lib/zeta-toolchain/?.lua;" .. package.path local path = require("path") local log = require("log") local build = require("build") local indexer = require("indexer") --------------------------------------------------------------------------- -- Help --------------------------------------------------------------------------- local HELP = [[ zeta-makepkg — build Zeta binary packages from declarative .recipe files. Usage: zeta-makepkg [flags] zeta-makepkg --all [flags] zeta-makepkg --index [flags] Commands: Build a package from a .recipe file --all Build all .recipe files found in --index Regenerate packages/index.lua from existing package.lua files Flags: --output Output repository root (default: .) Creates packages// with tarball + package.lua --repo Base URL for generated package.lua manifests (default: https://raw.githubusercontent.com/gretagen/...) -j, --jobs Number of parallel build jobs (default: nproc) -w, --workers Number of parallel package builds for --all (default: 1) --no-index Don't update packages/index.lua after building --keep-work Don't remove the build work directory --force Overwrite existing package tarball --help Show this help Cache: Downloaded sources are cached in ~/.cache/zeta-makepkg/sources/ Build work is done in /tmp/zeta-makepkg/-/ Examples: zeta-makepkg mypkg.recipe zeta-makepkg mypkg.recipe --output ~/zeta-packages --force zeta-makepkg --all recipes/ --output ~/zeta-packages zeta-makepkg --index --output ~/zeta-packages ]] --------------------------------------------------------------------------- -- CLI parsing --------------------------------------------------------------------------- local function parse_cli(args) local opts = { output = ".", no_index = false, keep_work = false, force = false, index = false, all = nil, help = false, recipe = nil, jobs = nil, -- nil = auto-detect (nproc) workers = 1, -- parallel package builds (--all only) repo = "https://raw.githubusercontent.com/gretagen/zeta-packages/refs/heads/main", } local i = 1 while i <= #args do local a = args[i] if a == "--help" or a == "-h" then opts.help = true elseif a == "--index" then opts.index = true elseif a == "--all" then i = i + 1 if i > #args then io.stderr:write("error: --all requires a directory argument\n") return nil end opts.all = args[i] elseif a == "--output" then i = i + 1 if i > #args then io.stderr:write("error: --output requires a directory argument\n") return nil end opts.output = args[i] elseif a == "--repo" then i = i + 1 if i > #args then io.stderr:write("error: --repo requires a URL argument\n") return nil end opts.repo = args[i] elseif a == "--no-index" then opts.no_index = true elseif a == "--keep-work" then opts.keep_work = true elseif a == "--force" then opts.force = true elseif a == "--jobs" then i = i + 1 if i > #args then io.stderr:write("error: --jobs requires a number argument\n") return nil end local n = tonumber(args[i]) if not n or n < 1 then io.stderr:write(("error: invalid jobs value %q\n"):format(args[i])) return nil end opts.jobs = n elseif a:match("^%-j%d+$") then local n = tonumber(a:match("^%-j(%d+)$")) opts.jobs = n elseif a == "-j" then i = i + 1 if i > #args then io.stderr:write("error: -j requires a number argument\n") return nil end local n = tonumber(args[i]) if not n or n < 1 then io.stderr:write(("error: invalid jobs value %q\n"):format(args[i])) return nil end opts.jobs = n elseif a == "--workers" then i = i + 1 if i > #args then io.stderr:write("error: --workers requires a number argument\n") return nil end local n = tonumber(args[i]) if not n or n < 1 then io.stderr:write(("error: invalid workers value %q\n"):format(args[i])) return nil end opts.workers = n elseif a:match("^%-w%d+$") then local n = tonumber(a:match("^%-w(%d+)$")) opts.workers = n elseif a:match("^%-") then io.stderr:write(("error: unknown flag %s\n"):format(a)) return nil elseif not opts.recipe and not opts.index then opts.recipe = a else io.stderr:write(("error: unexpected argument %s\n"):format(a)) return nil end i = i + 1 end return opts end --------------------------------------------------------------------------- -- Dispatch --------------------------------------------------------------------------- local args = {} for i = 1, #arg do args[i] = arg[i] end local opts = parse_cli(args) if not opts then io.stderr:write("Run 'zeta-makepkg --help' for usage.\n") os.exit(1) end if opts.help then io.write(HELP) os.exit(0) end if opts.index then indexer.generate(path.join(opts.output, "packages")) elseif opts.all then build.all(opts.all, opts, arg[0]) elseif opts.recipe then local ok, err = pcall(build.single, opts.recipe, opts) if not ok then log.fatal(tostring(err)) end else io.stderr:write("error: no recipe specified\n") io.stderr:write("Run 'zeta-makepkg --help' for usage.\n") os.exit(1) end