From 824f412174b56594a7799f277f3dfde88d1cbae4 Mon Sep 17 00:00:00 2001 From: Nicolas Mailhot Date: Oct 14 2020 14:44:01 +0000 Subject: [PATCH 1/2] Major rework of lua routines and forge macros Main features: – clean up lua function naming – add multiple small additional helpers to help keep lua code short and easy to check (this is what a non-barebones rpm lua namespace should look like) – add a plugable %auto_ framework. That includes two entry points in the preamble, %auto_init and %auto_pkg. This is necessary to separate cleanly the variable initialisation phase from their use in spec sections (including %package sections). Name: is treated as just another %package section without any specific handling, to KISS and save packager sanity (of course, Name: is not another section from the rpm parser POW, but that’s just one of rpm’s numerous legacy warts). – add a buildsys subsystem to manage SRPM inheritance from subpackages – add a doc subsystem to simplify creation of -doc subpackages that will hold bulky documents like pdf files – clean up and namespace properly forge control variables – make forge macros process patches too (like %autosetup, except in a safe way that does not break in presence of multiple archives). – patch processing is done via %apply_patch, and inherits all its quirks, limitations and lack of documentation – make forge macros suffix-independant: 0 no longer has any special meaning, and variables are not zaliased by default. You can write a whole multi-source and multi-patch spec without dealing with a single source or patch suffix. While the switch to uniform naming and the factorisation of common patterns in small helpers makes the code simpler and more maintainable long term, it does result in a huge number of changed lines (likewise, when reusing wordwrap in new functions required moving the wordwrap block in common.lua). The auto_call framework allows consolidating *all* macro sets that use this code into: ```rpm %global source_name … %global source_summary … %global forge_url0 … %global forge_commit0 … %global forge_url1 … %global forge_tag1 … %global go_module33 … %global go_description33 … %global font_family22 … %global font_conf22 … %auto_init %auto_pkg %sourcelist %auto_sources %patchlist %auto_patches %prep %auto_prep %generate_buildrequires %auto_generate_buildrequires %build %auto_build %install %auto_install %check %auto_check %auto_files %changelog ``` This change is backwards compatible: the old forge syntax works as before (for now, it will eventually be removed). However mixing old and new syntax in a single spec file is not supported. Packagers need to convert spec files fully or not at all. The lua side of forge macros is now cleanly split into SRPM and post-SRPM stage. Macros that call directly into the lua forge functions need adjusting their imports (to my knowledge this is only done by the Go macros that I will fix once this is merged and pushed to koji). --- diff --git a/auto.lua b/auto.lua new file mode 100644 index 0000000..7f76156 --- /dev/null +++ b/auto.lua @@ -0,0 +1,239 @@ +-- Lua code used by macros.auto* + +local fedora = require "fedora.common" + +-- Executes f(k) for k in range, returning a table of ks for which +-- f(k) = value. Used to compute a pre_range for pfloop for example +local function fmatch(f, range, value) + local K = {} + for _, k in ipairs(range) do + if (f(k) == value) then + table.insert(K, k) + end + end + return K +end + +-- Executes the function in a loop +-- – the loop is controled by (a string): +-- — if the value given to floop is nil, loop over the whole +-- . Use fedora.readflag() to process rpm flag arguments safely. +-- — otherwise execute for the specified value only, if +-- is part of +-- – is passed then as arguments. +-- – index values are processed before the rest of . +local function pfloop(f, index, pre_range, range, otherargs) + local only = {} + local first = {} + local last = {} + for _, i in ipairs(range) do + if (index == nil) then + local pre = false + for _, p in ipairs(pre_range) do + if (i == p) then + pre = true + break + end + end + if pre then + table.insert(first, i) + else + table.insert(last, i) + end + elseif (i == index) then + table.insert(only, i) + break + end + end + for _, R in ipairs({only, first, last}) do + for _, i in ipairs(R) do + f(i, table.unpack(otherargs)) + end + end +end + +-- Executes the function in a loop +-- – the loop is controled by (a string): +-- — if the value given to floop is nil, loop over the whole +-- . Use fedora.readflag() to process rpm flag arguments safely. +-- — otherwise execute for the specified value only, if +-- is part of +-- – is passed then as arguments. +-- – index values are processed before the rest of . +local function floop(f, index, range, otherargs) + pfloop(f, index, {}, range, otherargs) +end + +-- Executes the function in a loop, in a way compatible with package +-- header generation requirements +-- – the loop is controled by (a string): +-- — if the value given to floop is nil, loop over the whole +-- . Use fedora.readflag() to process rpm flag arguments safely. +-- — otherwise execute for the specified value only, if +-- is part of +-- – is passed then as arguments. +-- – () must return the %{name} of the SRPM header that would +-- be generated if (, ) is called before something +-- else generates the SRPM header. +-- – if %{source_name} is set, () results will be compared to +-- %{source_name} for all valid in the loop. values for +-- which that comparison matches will be scheduled first +local function pkg_floop(pkg, index, otherargs, name, range) + local source_range = {} + local source_name = fedora.read("source_name") + if source_name then + source_range = fmatch(name, range, source_name) + end + pfloop(pkg, index, source_range, range, otherargs) +end + +-- A small helper to return () for all values in , since +-- pkg_floop() will usually be used in conjunction with pkg_exec() +local function names(name, range) + local N = {} + for _, i in ipairs(range) do + table.insert(N, name(i)) + end + return N +end + +local function registered(call) + local macros = {} + local auto_dir = fedora.read("_rpmautodir") + if auto_dir then + for filename in posix.files(auto_dir) do + if filename:match('%.auto$') then + local f = io.open(auto_dir .. "/" .. filename, "r") + for l in f:lines() do + local macro = l:match('^%%?' .. call ..'%s+%%?(%S+)') + if macro then + table.insert(macros, macro) + end + end + end + end + end + return macros +end + +-- Executes all the macros in , passing -v as verbosity flag if +-- verbose. Each % macro will be executed after the macros listed in the +-- %{_after} variable (when those are also registered for the call). +-- Absent other constrains, % macros for which %{_last} is set to +-- anything else than false will be executed last +local function execute(macros, verbose) + local found = {} + local first = {} + local last = {} + local seen = {} + local plan = {} + local v = "" + if verbose then + v = " -v" + end + for _, macro in ipairs(macros) do + found[macro] = true + if fedora.readbool(macro .. "_last") then + table.insert(last, macro) + else + table.insert(first, macro) + end + end + local function insert(macro) + if seen[macro] then + return + end + seen[macro] = true + local deps = fedora.read(macro .. "_after") + if deps then + for dep in deps:gmatch("%S+") do + if found[dep] then + insert(dep) + end + end + end + table.insert(plan, macro) + end + for _, M in ipairs({first, last}) do + for _, macro in ipairs(M) do + insert(macro) + end + end + for _, macro in ipairs(plan) do + fedora.echo("%%" .. macro .. v) + print([[ +#]] .. macro .. v .. +rpm.expand([[ + +%]] .. macro .. v .. [[ +]])) + end +end + +-- Executes all the macros registered for call, passing -v as verbosity flag if +-- verbose. Each % macro will be executed after the macros listed in the +-- %{_after} variable (when those are also registered for the call). +local function exec(call, verbose) + execute(registered(call), verbose) +end + +-- Executes all the automated macros in the macros space-separated list of +-- s, passing -v as verbosity flag if verbose. +-- This variant is intended to be used for package header generation purposes, +-- when those headers may include the SRPM header. To that effect, each % +-- macro must have a %_source_names counterpart that returns the list of +-- SRPM names that would be generated (if any), allowing to schedule +-- those that match %{source_name} first. +local function pkg_exec(call, verbose) + local source_name = fedora.read("source_name") + local macros = registered(call) + local first = {} + local last = {} + local v = "" + if verbose then + v = " -v" + end + for _, macro in ipairs(macros) do + local inserted = false + if source_name then + fedora.echo("%%" .. macro .. "_source_names") + local names = fedora.expand("%" .. macro .. "_source_names" .. v) + for name in names:gmatch("%S+") do + if name == source_name then + inserted = true + table.insert(first, macro) + break + end + end + end + if not inserted then + table.insert(last, macro) + end + end + if source_name and next(first) == nil then + table.insert(first, "buildsys_pkg") + end + local macros = {} + for _, M in ipairs({first, last}) do + for _, macro in ipairs(M) do + table.insert(macros, macro) + end + end + execute(macros, verbose) +end + +-- A small helper to add function id to spec and console output, and keep track +-- of what is running when rpm barfs without providing any useful info +local function id(identifier) + fedora.echo(" " .. identifier) + print("#" .. identifier .. "\n") +end + +return { + floop = floop, + pkg_floop = pkg_floop, + names = names, + exec = exec, + pkg_exec = pkg_exec, + id = id, +} diff --git a/buildsys-srpm.lua b/buildsys-srpm.lua new file mode 100644 index 0000000..ac1b22c --- /dev/null +++ b/buildsys-srpm.lua @@ -0,0 +1,146 @@ +-- buildsys macros routines, safe to use at SRPM build stage + +local fedora = require "fedora.common" + +local namespace = "buildsys" +local rads = { + ["start"] = {"name"}, + ["evr"] = {"epoch", "version", "release", "post_release"}, + ["end"] = {"summary", "license", "url", "tags", "description"}, +} + +local function radicals() + local R = {} + for k, v in pairs(rads) do + R[k] = v + end + local all = {} + for k, v in pairs(R) do + all = fedora.mergelists({all, v}) + end + R["all"] = all + return R +end + +local function srpm_pkg_start(verbose) + print(fedora.expand([[ +Name: %{source_name} +]])) + -- Name: is declared, %{_sourcedir} can be used + local sn = "source" + local myrads = radicals() + fedora.mset(myrads["evr"], namespace, '', { + fedora.mread(myrads["evr"], sn, '', false), + }, verbose, false) + print(fedora.expand([[ +%{?buildsys_epoch:Epoch: %{buildsys_epoch}} +Version: %{buildsys_version} +Release: %{buildsys_release}%{?dist}%{?buildsys_post_release:.%{buildsys_post_release}}]])) + -- Make sure the environment is ready for eventual pkg_end() execution + -- pkg_end is not srpm specific and does not operate from the source namespace + local cn = "__current_" .. namespace + fedora.mset(myrads["end"], cn, '', { + fedora.mread(myrads["end"], sn, '', false), + }, verbose, true) + fedora.set("__" .. namespace .. "_srpm_done", true, verbose) +end + +local function pkg_end() + local cn = "__current_" .. namespace + local itags = {} + for t, m in pairs({["Epoch"] = "epoch", ["Version"] = "version"}) do + local v = fedora.read(cn .. '_' .. m) + if v and (v ~= fedora.read('source_' .. m)) then + table.insert(itags, t .. ': ' .. v) + end + end + print(rpm.expand([[ + +Summary: %{__current_buildsys_summary} +]] .. table.concat(itags, '\n') .. [[ +%{?__current_buildsys_license:License: %{__current_buildsys_license}} +%{?__current_buildsys_url:URL: %{__current_buildsys_url}} +%{?__current_buildsys_tags} +%description -n %{__current_buildsys_name} +%wordwrap -v __current_buildsys_description +]])) +end + +-- %new_package core +local function new_package(pkg_name, name_suffix, verbose) + local sn = "source" + local cn = "__current_" .. namespace + local source_name = fedora.read(sn .. "_name") + -- Safety net when the wrapper is used in conjunction with traditional syntax + if fedora.read("name") and (not source_name) then + warning([[ +Something already set a package name. However, %%{source_name} is not set. +Please set %%{source_name} to the SRPM name to ensure reliable processing. +]]) + if name_suffix then + print(fedora.expand("%package " .. name_suffix)) + else + print(fedora.expand("%package -n " .. pkg_name)) + end + return + end + -- New processing + -- We can not rely on cn .. "_name" because new_package (unlike pkg) + -- does not require users to clear past environement between calls + if not (pkg_name or name_suffix or source_name) then + fedora.error( + "You need to set %%{source_name} or provide explicit package naming!") + end + local first = not fedora.read("__" .. namespace .. "_srpm_done") + if name_suffix then + pkg_name = "%{source_name}-" .. name_suffix + print(fedora.expand("%package " .. name_suffix)) + else + if first and not source_name then + source_name = pkg_name + fedora.safeset(sn .. "_name", source_name, verbose) + end + if not pkg_name then + pkg_name = source_name + end + if (pkg_name == source_name) then + local myrads = radicals() + -- Collect bits that may have been declared in the __current_ + -- namespace. srpm_pkg_start will take the source namespace as only + -- trusted reference + fedora.mset(myrads["all"], sn, "", { + -- cn is a temporary reusable ref, all mreads must resolve + fedora.mread({"epoch", "version"}, cn, '', true), + fedora.mread(myrads["end"], cn, '', true), + }, verbose, false) + srpm_pkg_start() + else + print(fedora.expand("%package -n " .. pkg_name)) + end + end + _ = fedora.suffix({[namespace .. "_name"] = pkg_name}, true, verbose) + local cn = "__current_" .. namespace + fedora.set(cn .. "_name", pkg_name, verbose) +end + +local function pkg(verbose) + local cn = "__current_" .. namespace + local pkg_name = fedora.read(cn .. "_name") + new_package(pkg_name, nil, verbose) + pkg_end() +end + +local function reset(verbose) + local cn = "__current_" .. namespace + local myrads = radicals() + fedora.unset("__buildsys_srpm_done", verbose) + fedora.mset(myrads["all"], cn, '', {{}}, verbose, true) +end + +return { + namespace = namespace, + radicals = radicals()['all'], + new_package = new_package, + pkg = pkg, + reset = reset, +} diff --git a/common.lua b/common.lua index db53f39..8357da0 100644 --- a/common.lua +++ b/common.lua @@ -1,17 +1,77 @@ -- Convenience Lua functions that can be used within rpm macros --- Reads an rpm variable. Unlike a basic rpm.expand("{?foo}"), returns nil if --- the variable is unset, which is convenient in lua tests and enables --- differentiating unset variables from variables set to "" -local function read(rpmvar) +-- Reads an rpm variable reference. Returns "%{" .. rpmvar.. "}" if rpmvar is +-- set to something, and nil otherwise. This is convenient in lua tests and to +-- distinguish between an unset variable and a variable set to something that +-- expands to "" at this point of the spec file. +local function ref(rpmvar) if not rpmvar or (rpm.expand("%{" .. rpmvar .. "}") == "%{" .. rpmvar .. "}") then return nil else - return rpm.expand("%{?" .. rpmvar .. "}") + return "%{" .. rpmvar .. "}" end end +-- Expands till there is no expansion left to do +local function expand(run) + if not run then + return nil + end + run = run:gsub("%%%%", "\029") + local expanded = rpm.expand(run) + while run ~= expanded do + run = expanded + expanded = rpm.expand(run) + end + expanded = expanded:gsub("\029", "%%%%") + return expanded +end + +-- Reads an rpm variable (reads the variable reference, and evaluates it). +-- Unlike a basic rpm.expand("{?" .. rpmvar .. "}"), returns nil if the +-- variable is unset, which is convenient in lua tests. +local function read(rpmvar) + local ref = ref(rpmvar) + if ref then + return expand(ref) + else + return ref + end +end + +-- Sometimes it is convenient to distinguish between an unset variable and a +-- variable explicitly set to false. Unlike read, readbool returns a tristate, +-- true, false or nil +local function readbool(rpmvar) + local value = read(rpmvar) + if value then + if (value:lower() == "false") then + return false + else + return true + end + else + return value + end +end + +-- Builds a list of un-commented and non-empty lines from a multiline rpm +-- variable. This allows using multiline expands for private %sourcelist-like +-- containers. +local function readlines(rpmvar) + local lines = read(rpmvar) + local L = {} + if lines then + for line in string.gmatch(lines,'[^\r\n]+') do + if string.match(line, '^[^#]') then + table.insert(L, line) + end + end + end + return L +end + -- Returns true if the macro that called this function had flag set -- – for example, hasflag("z") would give the following results: -- %foo -z bar → true @@ -40,114 +100,420 @@ local function readflag(flag) end end --- Sets a spec variable; echoes the result if verbose -local function explicitset(rpmvar, value, verbose) - local value = value - if (value == nil) or (value == "") then - value = "%{nil}" +-- %wordwrap core +local function wordwrap(text) + text = expand(text .. "\n") + text = text:gsub("%%%%", "\029") + text = text:gsub("\t", " ") + text = text:gsub("\r", "\n") + text = text:gsub(" +\n", "\n") + text = text:gsub("\n+\n", "\n\n") + text = text:gsub("^\n", "") + text = text:gsub("\n( *)[-*—][  ]+", "\n%1– ") + output = "" + for line in text:gmatch("[^\n]*\n") do + local pos = 0 + local advance = "" + for word in line:gmatch("%s*[^%s]*\n?") do + local wl, bad = utf8.len(word) + if not wl then + -- Can not use warning in wordwrap since warning uses wordwrap + rpm.expand([[ +%{warn:Invalid UTF-8 sequence detected in:} +%{warn:]] .. word .. [[} +%{warn:It may produce unexpected results.} +]]) + wl = bad + end + if (pos == 0) then + advance, n = word:gsub("^(%s*– ).*", "%1") + if (n == 0) then + advance = word:gsub("^(%s*).*", "%1") + end + advance = advance:gsub("– ", " ") + pos = pos + wl + elseif (pos + wl < 81) or + ((pos + wl == 81) and word:match("\n$")) then + pos = pos + wl + else + word = advance .. word:gsub("^%s*", "") + output = output .. "\n" + pos = utf8.len(word) + end + output = output .. word + if pos > 80 then + pos = 0 + if not word:match("\n$") then + output = output .. "\n" + end + end + end end - rpm.define(rpmvar .. " " .. value) - if verbose then - rpm.expand("%{warn:Setting %%{" .. rpmvar .. "} = " .. value .. "}") + output = output:gsub("^\n*", "") + output = output:gsub("\n*$", "\n") + -- Escape %’s – we want macros that were preserved from wordwrap expansion to + -- be preserved at the next stage too + output = output:gsub("\029", "%%%%") + return output +end + +-- Reformats text and outputs it using a built-in rpm verb such as echo, warn +-- or error +local function message(verb, text) + rpm.expand("%{" .. verb .. ":" .. wordwrap(text):gsub("\n*$", "") .. "}") +end + +-- Writes some text to stdout +local function echo(text) + message("echo", text) +end + +-- Writes the list of z-suffixed rpmvars to the console, if set +local function echovars(rpmvars, z) + for _, rpmvar in ipairs(rpmvars) do + local suffixed = rpmvar .. z + local header = string.sub(" " .. suffixed .. ": ",1,24) + local v = ref(suffixed) + if v then + echo(header .. v) + end end end --- Unsets a spec variable if it is defined; echoes the result if verbose -local function explicitunset(rpmvar, verbose) - if (rpm.expand("%{" .. rpmvar .. "}") ~= "%{" .. rpmvar .. "}") then - rpm.define(rpmvar .. " %{nil}") - if verbose then - rpm.expand("%{warn:Unsetting %%{" .. rpmvar .. "}}") +-- Writes some text as a warning +local function warning(text) + message("warn", text) +end + +-- Writes some text as an error +local function err(text) + message("error", text) +end + +-- Returns a table of = , for all s in , +-- with  = reference of _ if +-- _ is set. Radicals for which +-- _ is not set are omitted in results. +local function mread(radicals, namespace, suffix, resolve) + local R = {} + for _, radical in ipairs(radicals) do + local name = radical + if namespace and (namespace ~= "") then + name = namespace .. "_" .. name + end + local ref = ref(name .. suffix) + if ref then + if resolve then + ref = expand(ref) + end + R[radical] = ref + end + end + return R +end + +-- Sets a spec variable; echoes the result if verbose +local function set(rpmvar, value, verbose) + if rpmvar then + -- Empty the stack state + while read(rpmvar) do + rpm.undefine(rpmvar) + end + if (value == nil) then + if ref(rpmvar) then + if verbose then + -- Already did it, white lie + warning("Unsetting " .. rpmvar) + end + end + else + if (value == false) then + value = "false" + elseif (value == true) then + value = "true" + end + if verbose then + warning("Setting " .. rpmvar .. " = " .. value) + end + rpm.define(rpmvar .. " %{expand:" .. value .. "}") end end end +-- Unsets a spec variable if it is defined; echoes the result if verbose +local function unset(rpmvar, verbose) + set(rpmvar, nil, verbose) +end + -- Sets a spec variable, if not already set; echoes the result if verbose local function safeset(rpmvar, value, verbose) - if (rpm.expand("%{" .. rpmvar .. "}") == "%{" .. rpmvar .. "}") then - explicitset(rpmvar,value,verbose) + if not ref(rpmvar) then + set(rpmvar, value, verbose) end end --- Aliases a list of rpm variables to the same variables suffixed with 0 (and --- vice versa); echoes the result if verbose -local function zalias(rpmvars, verbose) - for _, sfx in ipairs({{"","0"},{"0",""}}) do - for _, rpmvar in ipairs(rpmvars) do - local toalias = "%{?" .. rpmvar .. sfx[1] .. "}" - if (rpm.expand(toalias) ~= "") then - safeset(rpmvar .. sfx[2], toalias, verbose) +-- Sets the _ variable for all s in +-- , to a taken in the list of = +-- tables. When exists in multiple tables, only its first is +-- taken into account. When does not exist in any table, no +-- _ is set. +-- is usually constructed with multiple mread calls, taking +-- domain-specific fallback needs into consideration. +-- If set via set (and unset variables with no matches), otherwise set +-- via safeset. +-- mset is a complex function; its specializations like alias, bialias, zalias +-- and set_current are sufficient for many needs. +local function mset(radicals, namespace, suffix, stack, verbose, force) + for _, radical in ipairs(radicals) do + local value = nil + local found = false + for _, values in ipairs(stack) do + for k, v in pairs(values) do + if k == radical then + value = v + found = true + break + end + end + if found then + break end end + local name = radical + if namespace and (namespace ~= "") then + name = namespace .. "_" .. name + end + if force then + set( name .. suffix, value, verbose) + else + safeset(name .. suffix, value, verbose) + end end end --- Takes a list of rpm variable roots and a suffix and alias current to --- if it resolves to something not empty -local function setcurrent(rpmvars, suffix, verbose) - for _, rpmvar in ipairs(rpmvars) do - if (rpm.expand("%{?" .. rpmvar .. suffix .. "}") ~= "") then - explicitset( "current" .. rpmvar, "%{" .. rpmvar .. suffix .. "}", verbose) - else - explicitunset("current" .. rpmvar, verbose) +-- Sets the _ variable for all s in +-- , to a reference to _, +-- if _ is set. +-- inherit_from is recursive: the next is read in +-- _ +local function inherit_from (radicals, namespace, suffix, from_namespace, + from_suffix, from_key, verbose) + local seen = {} + local stack = {} + local function icopy(z) + if not z or seen[z] then + return + end + seen[z] = true + table.insert(stack, mread(radicals, from_namespace, z, false)) + if from_key and (from_key ~= "") then + local new_key = from_key .. z + if from_namespace and (from_namespace ~= "") then + new_key = from_namespace .. "_" .. new_key + end + icopy(read(new_key)) end end + icopy(from_suffix) + mset(radicals, namespace, suffix, stack, verbose, false, false) end --- Echo the list of rpm variables, with suffix, if set -local function echovars(rpmvars, suffix) - for _, rpmvar in ipairs(rpmvars) do - rpmvar = rpmvar .. suffix - local header = string.sub(" " .. rpmvar .. ": ",1,21) - rpm.expand("%{?" .. rpmvar .. ":%{echo:" .. header .. "%{?" .. rpmvar .. "}}}") +-- Sets the _ variable for all s in +-- , to a reference to _, +-- if _ is set. +-- is read in _ +-- inherit is recursive, and will also process at the next level +local function inherit(radicals, namespace, suffix, key, verbose) + local k = key .. suffix + if namespace and (namespace ~= "") then + k = namespace .. "_" .. k end + local from_suffix = read(k) + inherit_from(radicals, namespace, suffix, namespace, from_suffix, key, + verbose) end --- Returns an array, indexed by suffix, containing the non-empy values of --- , with suffix an integer string or the empty string -local function getsuffixed(rpmvar) - local suffixes = {} - zalias({rpmvar}) - for suffix=0,9999 do - local value = rpm.expand("%{?" .. rpmvar .. suffix .. "}") - if (value ~= "") then - suffixes[tostring(suffix)] = value +-- for each radical in radicals, safeset +-- to %{} if +-- is set to something +local function alias(radicals, from_namespace, from_suffix, + to_namespace, to_suffix, verbose) + mset(radicals, to_namespace, to_suffix, + {mread(radicals, from_namespace, from_suffix, false)}, + verbose, false) +end + +-- Alias back and forth +local function bialias(radicals, namespace1, suffix1, + namespace2, suffix2, verbose) + alias(radicals, namespace1, suffix1, namespace2, suffix2, verbose) + alias(radicals, namespace2, suffix2, namespace1, suffix1, verbose) +end + +-- Aliases a list of rpm variables to the same variables suffixed with 0 (and +-- vice versa); echoes the result if verbose +local function zalias(rpmvars, verbose) + bialias(rpmvars, "", "", "", "0", verbose) +end + +-- Takes a list of rpm variable roots and a suffix and alias __current_ +-- to if it resolves to something not empty +local function set_current(rpmvars, suffix, verbose) + mset(rpmvars, "__current", "", + {mread(rpmvars, "", suffix, false)}, verbose, true) +end + +-- Sets the usual verbosity variables in a generic way +local function set_verbose(verbose) + if verbose then + set( "__current_verbose", "-v", verbose) + unset("__current_quiet", verbose) + unset("__current_shell_quiet", verbose) + unset("__current_shell_verbose", verbose) + else + unset("__current_verbose", verbose) + set( "__current_quiet", "-q", verbose) + set( "__current_shell_quiet", "set +x", verbose) + set( "__current_shell_verbose", "set -x", verbose) + end +end + +-- Returns an array of _ +local function qualify(radicals, namespace) + local R = {} + if (namespace ~= "") then + namespace = namespace .. "_" + end + for _, radical in ipairs(radicals) do + table.insert(R, namespace .. radical) + end + return R +end + +-- Merges two lists, removing duplicates and ordering the result +-- The result is a list containing an ordered set of values +local function mergelists(list_of_lists) + local S = {} + local L = {} + for _, l in ipairs(list_of_lists) do + for _, v in ipairs(l) do + S[v] = true + end + end + for k, _ in pairs(S) do + table.insert(L, k) + end + table.sort(L) + return L +end + +-- Returns a list of suffixes, for which is set to something +local function suffixes(rpmvar) + local S = {} + for z=0,9999 do + if ref(rpmvar .. z) then + table.insert(S, tostring(z)) end end -- rpm convention is to alias no suffix to zero suffix -- only add no suffix if zero suffix is different - local value = rpm.expand("%{?" .. rpmvar .. "}") - if (value ~= "") and (value ~= suffixes["0"]) then - suffixes[""] = value + local value = read(rpmvar) + if value and not (value == read(rpmvar .. "0")) then + table.insert(S, "") end - return suffixes + return S end -- Returns the list of suffixes, including the empty string, for which --- is set to a non empty value -local function getsuffixes(rpmvar) - suffixes = {} - for suffix in pairs(getsuffixed(rpmvar)) do - table.insert(suffixes,suffix) +-- is set to a non empty value, for any rpmvar in rpmvars +local function all_suffixes(rpmvars) + local SL = {} + local S = {} + for _, rpmvar in ipairs(rpmvars) do + table.insert(SL, suffixes(rpmvar)) end - table.sort(suffixes, + S = mergelists(SL) + table.sort(S, function(a,b) return (tonumber(a) or 0) < (tonumber(b) or 0) end) - return suffixes + return S end --- Returns the suffix for which has a non-empty value that --- matches best the beginning of the value string -local function getbestsuffix(rpmvar, value) - local best = nil - local currentmatch = "" - for suffix, setvalue in pairs(getsuffixed(rpmvar)) do - if (string.len(setvalue) > string.len(currentmatch)) and - (string.find(value, "^" .. setvalue)) then - currentmatch = setvalue - best = suffix +local function suffix_new(set, verbose) + local s = 0 + local used = false + repeat + z = tostring(s) + used = false + for k, _ in pairs(set) do + used = used or ref(k .. z) end + s = s + 1 + until (not used) + for k, v in pairs(set) do + safeset(k .. z, v, verbose) end - return best + return z +end + +local function suffix_reuse(set, verbose) + for s = 0, 9999 do + z = tostring(s) + local matches = true + for k, v in pairs(set) do + matches = matches and (read(k .. z) == v) + end + if matches then + return z + end + end + return suffix_new(set, verbose) +end + +-- Takes a set[key] = table and returns a suffix for which all the +-- rpm variables have the corresponding set. +-- If reuse attempts to find an existing suffix that matches, otherwise, +-- takes an available suffix and creates the variable set from scratch +local function suffix(set, reuse, verbose) + if reuse then + return suffix_reuse(set, verbose) + else + return suffix_new(set, verbose) + end +end + +-- Returns the suffix/number of a source/patch filename +-- We could do it with source_num/patch_num but those are dependant on a +-- specific rpm version and are not available for all the other variables we +-- use suffixes() with +local function sourcedir_suffix(filename, radical) + if not filename then + return nil + end + filename = expand(filename) + if not filename then + return nil + end + filename = filename:match("[^/]+$") + if not filename then + return nil + end + filename = expand("%{_sourcedir}/" .. filename) + for _, z in ipairs(suffixes(radical)) do + if expand('%{' .. radical .. z .. "}") == filename then + return z + end + end + return nil +end + +-- Returns the source suffix/number of a filename +local function source_suffix(filename) + return sourcedir_suffix(filename, "SOURCE") +end + +-- Returns the patch suffix/number of a filename +local function patch_suffix(filename) + return sourcedir_suffix(filename, "PATCH") end -- %writevars core @@ -159,136 +525,93 @@ local function writevars(macrofile, rpmvars) end end --- https://github.com/rpm-software-management/rpm/issues/566 --- Reformat a text intended to be used used in a package description, removing --- rpm macro generation artefacts. --- – remove leading and ending empty lines --- – trim intermediary empty lines to a single line --- – fold on spaces --- Should really be a %%{wordwrap:…} verb -local function wordwrap(text) - text = rpm.expand(text .. "\n") - text = string.gsub(text, "\t", " ") - text = string.gsub(text, "\r", "\n") - text = string.gsub(text, " +\n", "\n") - text = string.gsub(text, "\n+\n", "\n\n") - text = string.gsub(text, "^\n", "") - text = string.gsub(text, "\n( *)[-*—][  ]+", "\n%1– ") - output = "" - for line in string.gmatch(text, "[^\n]*\n") do - local pos = 0 - local advance = "" - for word in string.gmatch(line, "%s*[^%s]*\n?") do - local wl, bad = utf8.len(word) - if not wl then - print("%{warn:Invalid UTF-8 sequence detected in:}" .. - "%{warn:" .. word .. "}" .. - "%{warn:It may produce unexpected results.}") - wl = bad - end - if (pos == 0) then - advance, n = string.gsub(word, "^(%s*– ).*", "%1") - if (n == 0) then - advance = string.gsub(word, "^(%s*).*", "%1") - end - advance = string.gsub(advance, "– ", " ") - pos = pos + wl - elseif (pos + wl < 81) or - ((pos + wl == 81) and string.match(word, "\n$")) then - pos = pos + wl - else - word = advance .. string.gsub(word, "^%s*", "") - output = output .. "\n" - pos = utf8.len(word) - end - output = output .. word - if pos > 80 then - pos = 0 - if not string.match(word, "\n$") then - output = output .. "\n" - end - end + + +-- deprecated graveyard +-- those can be removed as soon as a port of fonts-rpm-macros and go-rpm-macros +-- to the new API lands in koji + +local function setcurrent(rpmvars, suffix, verbose) + warning("setcurrent is deprecated, replace it with set_current") + for _, rpmvar in ipairs(rpmvars) do + local suffixed = rpmvar .. suffix + if ref(suffixed) then + set( "current" .. rpmvar, "%{?" .. suffixed .. "}", verbose) + else + unset("current" .. rpmvar, verbose) end end - output = string.gsub(output, "\n*$", "\n") - return output end --- Because rpmbuild will fail if a subpackage is declared before the source --- package itself, provide a source package declaration shell as fallback. -local function srcpkg(verbose) - if verbose then - rpm.expand([[ -%{echo:Creating a header for the SRPM from %%{source_name}, %%{source_summary} and} -%{echo:%%{source_description}. If that is not the intended result, please declare the} -%{echo:SRPM header and set %%{source_name} in your spec file before calling a macro} -%{echo:that creates other package headers.} -]]) - end - print(rpm.expand([[ -Name: %{source_name} -Summary: %{source_summary} -%description -%wordwrap -v source_description -]])) - explicitset("currentname", "%{source_name}", verbose) -end - --- %new_package core -local function new_package(source_name, pkg_name, name_suffix, first, verbose) - -- Safety net when the wrapper is used in conjunction with traditional syntax - if (not first) and (not source_name) then - rpm.expand([[ -%{warn:Something already set a package name. However, %%{source_name} is not set.} -%{warn:Please set %%{source_name} to the SRPM name to ensure reliable processing.} -]]) - if name_suffix then - print(rpm.expand("%package " .. name_suffix)) - else - print(rpm.expand("%package -n " .. pkg_name)) +local function getsuffixed(rpmvar) + local S = {} + zalias({rpmvar}, false) + for z=0,9999 do + local value = read(rpmvar .. z) + if value then + S[tostring(z)] = value end - return end - -- New processing - if not (pkg_name or name_suffix or source_name) then - rpm.expand([[ -%{error:You need to set %%{source_name} or provide explicit package naming!} -]]) + -- rpm convention is to alias no suffix to zero suffix + -- only add no suffix if zero suffix is different + local value = read(rpmvar) + if value and (value ~= S["0"]) then + S[""] = value end - if name_suffix then - print(rpm.expand("%package " .. name_suffix)) - explicitset("currentname", "%{source_name}-" .. name_suffix, verbose) - else - if not source_name then - source_name = pkg_name - end - if (pkg_name == source_name) then - safeset("source_name", source_name, verbose) - print(rpm.expand("Name: %{source_name}")) - else - if source_name and first then - srcpkg(verbose) - end - print(rpm.expand("%package -n " .. pkg_name)) + return S +end + +local function getbestsuffix(rpmvar, value) + local best = nil + local currentmatch = "" + for z, setvalue in pairs(getsuffixed(rpmvar)) do + if ((not best) or (string.len(setvalue) < string.len(currentmatch))) and + (string.find(setvalue, "^" .. value)) then + currentmatch = setvalue + best = z end - explicitset("currentname", pkg_name, verbose) end + return best end return { - read = read, - hasflag = hasflag, - readflag = readflag, - explicitset = explicitset, - explicitunset = explicitunset, - safeset = safeset, - zalias = zalias, - setcurrent = setcurrent, - echovars = echovars, - getsuffixed = getsuffixed, - getsuffixes = getsuffixes, - getbestsuffix = getbestsuffix, - writevars = writevars, - wordwrap = wordwrap, - new_package = new_package, + ref = ref, + expand = expand, + read = read, + readbool = readbool, + readlines = readlines, + hasflag = hasflag, + readflag = readflag, + wordwrap = wordwrap, + echo = echo, + echovars = echovars, + warning = warning, + error = err, + mread = mread, + set = set, + unset = unset, + safeset = safeset, + mset = mset, + inherit_from = inherit_from, + inherit = inherit, + alias = alias, + bialias = bialias, + zalias = zalias, + set_current = set_current, + set_verbose = set_verbose, + qualify = qualify, + mergelists = mergelists, + suffixes = suffixes, + all_suffixes = all_suffixes, + suffix = suffix, + source_suffix = source_suffix, + patch_suffix = patch_suffix, + writevars = writevars, + -- deprecated aliases + explicitset = set, + explicitunset = unset, + getsuffixes = suffixes, + setcurrent = setcurrent, + getsuffixed = getsuffixed, + getbestsuffix = getbestsuffix, } diff --git a/doc-rpm.auto b/doc-rpm.auto new file mode 100644 index 0000000..bcedb85 --- /dev/null +++ b/doc-rpm.auto @@ -0,0 +1,2 @@ +auto_install doc_install +auto_files doc_files diff --git a/doc-rpm.lua b/doc-rpm.lua new file mode 100644 index 0000000..b800c3b --- /dev/null +++ b/doc-rpm.lua @@ -0,0 +1,27 @@ +local fedora = require "fedora.common" +local auto = require "fedora.auto" +local doc = require "fedora.srpm.doc" + +local namespace = "doc" + +local function install(suffix, verbose) + auto.id(namespace .. '.install("' .. suffix .. '")') + doc.env(suffix, verbose) + print(rpm.expand([[ +%__]] .. namespace .. [[_install +]])) +end + +local function files(suffix, verbose) + auto.id(namespace .. '.files("' .. suffix .. '")') + doc.env(suffix, verbose) + print(rpm.expand([[ +%__]] .. namespace .. [[_files +]])) +end + +return { + floop = doc.floop, + install = install, + files = files, +} diff --git a/doc-srpm.auto b/doc-srpm.auto new file mode 100644 index 0000000..b06df47 --- /dev/null +++ b/doc-srpm.auto @@ -0,0 +1,2 @@ +auto_init doc_init +auto_pkg doc_pkg diff --git a/doc-srpm.lua b/doc-srpm.lua new file mode 100644 index 0000000..724ce23 --- /dev/null +++ b/doc-srpm.lua @@ -0,0 +1,103 @@ + +local fedora = require "fedora.common" +local auto = require "fedora.auto" +local buildsys = require "fedora.srpm.buildsys" + +local namespace = "doc" + +local rads = { + ["key"] = {"docs"}, + ["read"] = {"epoch", "version", "license", "licenses", + "licenses_exclude", "docs", "docs_exclude", "tags"}, + ["computed"] = {"name", "summary", "requires", "description", "list"} +} + +local function suffixes() + return fedora.all_suffixes(fedora.qualify(rads["key"], namespace)) +end + +local function floop(f, index, otherargs, verbose) + local range = suffixes(verbose) + auto.floop(f, index, range, otherargs) +end + +local function radicals() + local R = {} + for k, v in pairs(rads) do + R[k] = v + end + local all = {} + for k, v in pairs(R) do + all = fedora.mergelists({all, v}) + end + R["all"] = all + return R +end + +local function init(suffix, verbose, informative) + auto.id(namespace .. '.init("' .. suffix .. '")') + local ismain = (suffix == "") or (suffix == "0") + local myrads = radicals() + local requires = [[%{lua: + local fedora = require "fedora.common" + local namespace = "]] .. namespace .. [[" + local suffix = "]] .. suffix .. [[" + local requires = {} + local pkgs = fedora.read(namespace .. "_packages" .. suffix) + if pkgs then + for pkg in pkgs:gmatch("[^%s,;]+") do + table.insert(requires, "Enhances: " .. pkg) + end + else + for _, z in ipairs(fedora.suffixes("buildsys_name")) do + if not fedora.read("buildsys_name" .. z):match("-doc$") then + table.insert(requires, + "Enhances: %{buildsys_name" .. z .. "}") + end + end + end + print(table.concat(requires, "\\n")) + }]] + fedora.mset(myrads["all"], namespace, suffix, {{ + ["name"] = "%{source_name}-doc", + ["summary"] = "Optional documentation files %{source_name}", + ["requires"] = requires, + ["description"] = [[ +%{?source_description} + +This package provides optional documentation files shipped with %{source_name}. +]], + ["list"] = "%{_builddir}/%{?buildsubdir}/" .. + "%{" .. namespace .. "_name" .. suffix .. "}.lst", + }}, verbose, false) + if informative then + fedora.echo("Variables read or set by %%" .. namespace .. "_init") + fedora.echovars(fedora.qualify(myrads["all"], namespace), suffix) + end +end + +local function env(suffix, verbose) + local myrads = radicals(suffix, verbose) + fedora.set_current(fedora.qualify(myrads["all"], namespace), suffix, verbose) +end + +local function pkg(suffix, verbose) + auto.id(namespace .. '.pkg("' .. suffix .. '")') + fedora.mset(buildsys.radicals, "__current_buildsys", "", { + { ["tags"] = [[ +BuildArch: noarch +%{?]] .. namespace .. [[_requires]] .. suffix .. [[} +%{?]] .. namespace .. [[_tags]] .. suffix .. [[}]] }, + fedora.mread({"name", "summary", "epoch", "version", "license", + "description"}, namespace, suffix, false), + }, verbose, true) + buildsys.pkg(verbose) +end + +return { + suffixes = suffixes, + floop = floop, + init = init, + env = env, + pkg = pkg, +} diff --git a/forge-rpm.auto b/forge-rpm.auto new file mode 100644 index 0000000..7974297 --- /dev/null +++ b/forge-rpm.auto @@ -0,0 +1 @@ +auto_prep forge_prep diff --git a/forge-rpm.lua b/forge-rpm.lua new file mode 100644 index 0000000..ca536d2 --- /dev/null +++ b/forge-rpm.lua @@ -0,0 +1,44 @@ +-- Lua code used by macros.forge and derivatives; RPM stage + +local fedora = require "fedora.common" +local auto = require "fedora.auto" +local forge = require "fedora.srpm.forge" + +-- This is constrained by the design of %apply_patch +local function apply_patches(suffix, verbose) + local quiet = "" + if not verbose then + quiet = "-q " + end + for _, patch in ipairs(fedora.readlines("forge_patchlist" .. suffix)) do + local ps = fedora.patch_suffix(patch) + if ps then + for _, o in ipairs({"patch_mode", "patch_level"}) do + fedora.safeset(o .. ps, "%{forge_" .. o .. suffix .. "}") + end + -- %apply_patch does not have the notion of a disposable local currentfoo-like + -- control variable, you need to override the main one + fedora.set("__scm", "%{patch_mode" .. ps .. "}") + -- %apply_patch requires specifying the patch to apply in 3 different forms! + -- As a filename, as a full path, and as a patch suffix + print(rpm.expand( + "%apply_patch " .. quiet .. "%{patch_level" .. ps .. "} " .. + "-m " .. patch .. " %{_sourcedir}/" .. patch .. " " .. ps .. "\n")) + end + end +end + +-- %forge_prep core +local function prep(suffix, verbose) + auto.id('forge.prep("' .. suffix .. '")') + forge.env(suffix, verbose) + print(rpm.expand([[ +%setup %{?__current_quiet} %{__current_forge_setup_arguments} +]])) + apply_patches(suffix, verbose) +end + +return { + floop = forge.floop, + prep = prep, +} diff --git a/forge-srpm.auto b/forge-srpm.auto new file mode 100644 index 0000000..8ce6f98 --- /dev/null +++ b/forge-srpm.auto @@ -0,0 +1,3 @@ +auto_init forge_init +auto_sources forge_sources +auto_patches forge_patches diff --git a/forge-srpm.lua b/forge-srpm.lua new file mode 100644 index 0000000..d669b96 --- /dev/null +++ b/forge-srpm.lua @@ -0,0 +1,438 @@ +-- Lua code used by macros.forge-srpm and derivatives + +local fedora = require "fedora.common" +local auto = require "fedora.auto" + +-- Computes the suffix of a version string, removing vprefix if it matches +-- For example with vprefix 1.2.3: 1.2.3.rc2 → .rc2 but 1.2.30 → 1.2.30 not 0 +local function version_suffix(vstring, vprefix) + if (vstring:sub(1, #vprefix) == vprefix) and + (not string.match(vstring:sub(#vprefix + 1), "^%.?%d")) then + return vstring:sub(#vprefix + 1) + else + return vstring + end +end + +-- Check if an identified url is sane +local function check_forge_url(url, id, silent) + local checkedurl = nil + local checkedid = nil + local urlpatterns = { + ["gitlab"] = { + ["pattern"] = 'https://[^/]+/[^/]+/[^/#?]+', + ["description"] = 'https://(…[-.])gitlab[-.]…/owner/repo'}, + ["pagure"] = { + ["pattern"] = 'https://[^/]+/[^/#?]+', + ["description"] = 'https://pagure.io/repo'}, + ["pagure_ns"] = { + ["pattern"] = 'https://[^/]+/[^/]+/[^/#?]+', + ["description"] = 'https://pagure.io/namespace/repo'}, + ["pagure_fork"] = { + ["pattern"] = 'https://[^/]+/fork/[^/]+/[^/#?]+', + ["description"] = 'https://pagure.io/fork/owner/repo'}, + ["pagure_ns_fork"] = { + ["pattern"] = 'https://[^/]+/fork/[^/]+/[^/]+/[^/#?]+', + ["description"] = 'https://pagure.io/fork/owner/namespace/repo'}, + ["gitea.com"] = { + ["pattern"] = 'https://[^/]+/[^/]+/[^/#?]+', + ["description"] = 'https://gitea.com/owner/repo'}, + ["github"] = { + ["pattern"] = 'https://[^/]+/[^/]+/[^/#?]+', + ["description"] = 'https://(…[-.])github[-.]…/owner/repo'}, + ["code.googlesource.com"] = { + ["pattern"] = 'https://code.googlesource.com/[^#?]*[^/#?]+', + ["description"] = 'https://code.googlesource.com/…/repo'}, + ["bitbucket.org"] = { + ["pattern"] = 'https://[^/]+/[^/]+/[^/#?]+', + ["description"] = 'https://bitbucket.org/owner/repo'}} + if (urlpatterns[id] ~= nil) then + checkedurl = url:match(urlpatterns[id]["pattern"]) + if (checkedurl == nil) then + if not silent then + fedora.error(id .. " URLs must match " .. + urlpatterns[id]["description"] .. "!") + end + else + checkedid = id + end + end + return checkedurl, checkedid +end + +-- Check if an url matches a known forge +local function idforge(url, silent) + local forge_url = nil + local forge = nil + if (url ~= "") then + forge = url:match("^[^:]+://([^/]+)/") + if not forge then + if not silent then + fedora.error([[ +URLs must include a protocol such as https:// and a path starting with /. Read: +]] .. url) + end + else + if (forge == "pagure.io") then + if url:match("[^:]+://pagure.io/fork/[^/]+/[^/]+/[^/]+") then + forge = "pagure_ns_fork" + elseif url:match("[^:]+://pagure.io/fork/[^/]+/[^/]+") then + forge = "pagure_fork" + elseif url:match("[^:]+://pagure.io/[^/]+/[^/]+") then + forge = "pagure_ns" + elseif url:match("[^:]+://pagure.io/[^/]+") then + forge = "pagure" + end + elseif forge:match("^gitlab[%.-]") or forge:match("[%.-]gitlab[%.]") then + forge = "gitlab" + elseif forge:match("^github[%.-]") or forge:match("[%.-]github[%.]") then + forge = "github" + end + forge_url, forge = check_forge_url(url, forge, silent) + end + end + return forge_url, forge +end + +-- rpm variable radicals +local rads = { + -- Elements that will be pushed SRPM-level + ["source"] = {"version", "url"}, + ["forge"] = { + -- key variables that may control a declaration block + ["key"] = {"url"}, + -- variables, used to compute a SCM reference + ["ref"] = {"tag", "commit", "branch", "version", "ref"}, + -- other computed or recomputed variables + ["computed"] = {"tag", "source", "source_suffix", "setup_arguments", + "patch_mode", "patch_level", "extract_dir", + "time", "date", "file_ref"},}, +} + +-- Return all the suffixes for which one of the forge key variables is set +local function suffixes() + return fedora.all_suffixes(fedora.qualify(rads["forge"]["key"], "forge")) +end + +-- Executes the f function in a loop +-- – the loop is controled by index (a string): +-- — if the index value given to floop is nil, loop over the whole +-- range. Use readflag() to process rpm flag arguments safely. +-- — otherwise execute f for the specified index value only. +-- – f is passed index then otherargs as arguments. +-- – the index range is controlled by suffixes() +local function floop(f, index, otherargs) + auto.floop(f, index, suffixes(), otherargs) +end + +-- forge variable derivation patterns +local function rules(suffix) + local R = { + ["default"] = { + ["scm"] = "git", + ["archive_extension"] = "tar.bz2", + ["repo"] = '%{lua:print(rpm.expand("%{forge_url' .. suffix .. '}"):match("^[^:]+://[^/]+/[^/]+/([^/?#]+)"))}', + ["archive_name"] = "%{forge_repo" .. suffix .. "}-%{forge_ref" .. suffix .. "}", + ["top_dir"] = "%{forge_archive_name" .. suffix .. "}" }, + ["gitlab"] = { + ["source"] = "%{forge_url" .. suffix .. "}/-/archive/%{forge_ref" .. suffix .. "}/%{forge_archive_name" .. suffix .. "}.%{forge_archive_extension" .. suffix .. "}" }, + ["pagure"] = { + ["archive_extension"] = "tar.gz", + ["repo"] = '%{lua:print(rpm.expand("%{forge_url' .. suffix .. '}"):match("^[^:]+://[^/]+/([^/?#]+)"))}', + ["source"] = "%{forge_url" .. suffix .. "}/archive/%{forge_ref" .. suffix .. "}/%{forge_archive_name" .. suffix .. "}.%{forge_archive_extension" .. suffix .. "}" }, + ["pagure_ns"] = { + ["archive_extension"] = "tar.gz", + ["namespace"] = '%{lua:print(rpm.expand("%{forge_url' .. suffix .. '}"):match("^[^:]+://[^/]+/([^/]+)/[^/?#]+"))}', + ["repo"] = '%{lua:print(rpm.expand("%{forge_url' .. suffix .. '}"):match("^[^:]+://[^/]+/[^/]+/([^/?#]+)"))}', + ["archive_name"] = "%{forge_namespace" .. suffix .. "}-%{forge_repo" .. suffix .. "}-%{forge_ref" .. suffix .. "}", + ["source"] = "%{forge_url" .. suffix .. "}/archive/%{forge_ref" .. suffix .. "}/%{forge_archive_name" .. suffix .. "}.%{forge_archive_extension" .. suffix .. "}" }, + ["pagure_fork"] = { + ["archive_extension"] = "tar.gz", + ["owner"] = '%{lua:print(rpm.expand("%{forge_url' .. suffix .. '}"):match("https://[^/]+/fork/([^/]+)/[^/?#]+"))}', + ["repo"] = '%{lua:print(rpm.expand("%{forge_url' .. suffix .. '}"):match("https://[^/]+/fork/[^/]+/([^/?#]+)"))}', + ["archive_name"] = "%{forge_owner" .. suffix .. "}-%{forge_repo" .. suffix .. "}-%{forge_ref" .. suffix .. "}", + ["source"] = "%{forge_url" .. suffix .. "}/archive/%{forge_ref" .. suffix .. "}/%{forge_archive_name" .. suffix .. "}.%{forge_archive_extension" .. suffix .. "}" }, + ["pagure_ns_fork"] = { + ["owner"] = '%{lua:print(rpm.expand("%{forge_url' .. suffix .. '}"):match("https://[^/]+/fork/([^/]+)/[^/]+/[^/?#]+"))}', + ["namespace"] = '%{lua:print(rpm.expand("%{forge_url' .. suffix .. '}"):match("https://[^/]+/fork/[^/]+/([^/]+)/[^/?#]+")}', + ["repo"] = '%{lua:print(rpm.expand("%{forge_url' .. suffix .. '}"):match("https://[^/]+/fork/[^/]+/[^/]+/([^/?#]+)")}', + ["archive_name"] = "%{forge_owner" .. suffix .. "}-%{forge_namespace" .. suffix .. "}-%{forge_repo" .. suffix .. "}-%{forge_ref" .. suffix .. "}", + ["source"] = "%{forge_url" .. suffix .. "}/archive/%{forge_ref" .. suffix .. "}/%{forge_archive_name" .. suffix .. "}.%{forge_archive_extension" .. suffix .. "}" }, + ["gitea.com"] = { + ["archive_extension"] = "tar.gz", + ["archive_name"] = "%{forge_file_ref" .. suffix .. "}", + ["source"] = "%{forge_url" .. suffix .. "}/archive/%{forge_ref" .. suffix .. "}.%{forge_archive_extension" .. suffix .. "}", + ["top_dir"] = "%{forge_repo}" }, + ["github"] = { + ["archive_extension"] = "tar.gz", + ["archive_name"] = "%{forge_repo" .. suffix .. "}-%{forge_file_ref" .. suffix .. "}", + ["source"] = "%{forge_url" .. suffix .. "}/archive/%{forge_ref" .. suffix .. "}/%{forge_archive_name" .. suffix .. "}.%{forge_archive_extension" .. suffix .. "}" }, + ["code.googlesource.com"] = { + ["archive_extension"] = "tar.gz", + ["repo"] = '%{lua:print(rpm.expand("%{forge_url' .. suffix .. '}"):match("^[^:]+://.+/([^/?#]+)"))}', + ["source"] = "%{forge_url" .. suffix .. "}/+archive/%{forge_ref" .. suffix .. "}.%{forge_archive_extension" .. suffix .. "}", + ["top_dir"] = "" }, + ["bitbucket.org"] = { + ["short_commit"] = '%{lua:print(string.sub(rpm.expand("%{forge_commit' .. suffix .. '}"), 1, 12))}', + ["owner"] = '%{lua:print(rpm.expand("%{forge_url' .. suffix .. '}"):match("^[^:]+://[^/]+/([^/?#]+)"))}', + ["archive_name"] = "%{forge_owner" .. suffix .. "}-%{forge_repo" .. suffix .. "}-%{forge_short_commit" .. suffix .. "}", + ["source"] = "%{forge_url" .. suffix .. "}/get/%{forge_ref" .. suffix .. "}.%{forge_archive_extension" .. suffix .. "}" } } + return R +end + +-- Returns forge radicals +local function radicals(suffix) + local R = {} + for k, v in pairs(rads["forge"]) do + R[k] = v + end + R["rules"] = {} + for _, v in pairs(rules(suffix)) do + local l = {} + for k, _ in pairs(v) do + table.insert(l, k) + end + R["rules"] = fedora.mergelists({R["rules"], l}) + end + R["computed"] = fedora.mergelists({R["computed"], R["key"], R["rules"]}) + local all = {} + for k, v in pairs(R) do + all = fedora.mergelists({all, v}) + end + R["all"] = all + return R +end + +-- %forge_init core +local function init(suffix, verbose, informative, silent) + auto.id('forge.init("' .. suffix .. '")') + local myrads = radicals(suffix) + print([[ +# forge_init ]] .. suffix .. [[ +]]) + local spec = {} + for _, v in ipairs(fedora.qualify(myrads["all"], "forge")) do + spec[v] = fedora.read(v .. suffix) + end + -- Compute the reference of the object to fetch + if not (spec["forge_tag"] or spec["forge_commit"] or spec["forge_branch"] or + spec["forge_version"]) then + fedora.error([[ +You need to set one of: + – %%{forge_tag]] .. suffix .. [[} + – %%{forge_commit]] .. suffix .. [[} +with your: +%%{forge_url]] .. suffix .. [[} %{?forge_url]] .. suffix .. [[} +declaration. +]]) + end + local keep_version = "true" + local keep_file_ref = "true" + local forge_url = spec["forge_url"] + local forge + forge_url, forge = idforge(forge_url, silent) + if forge then + if (forge == "github") or (forge == "code.googlesource.com") then + keep_version = "false" + elseif (forge == "bitbucket.org") and not spec["forge_commit"] then + fedora.error("All BitBucket URLs require commit value knowledge: " .. + "you need to set %{forge_commit}!") + end + if (forge == "github") then + keep_file_ref = "false" + end + end + local ref = [[%{lua: + local fedora = require "fedora.common" + local suffix = "]] .. suffix .. [[" + local keep_version = "]] .. keep_version .. [[" + local r = "tag" + for _, k in ipairs({"tag", "commit", "branch", "version"}) do + if fedora.read("forge_" .. k .. suffix) then + r = k + break + end + end + if (r == "version") and + not (keep_version == "true") then + r = "v%{forge_" .. r .. suffix .. "}" + else + r = "%{forge_" .. r .. suffix .. "}" + end + print(r) + }]] + local file_ref = [[%{lua: + local fedora = require "fedora.common" + local ref = [=[]] .. ref .. [[]=] + local suffix = "]] .. suffix .. [[" + local keep_file_ref = "]] .. keep_file_ref .. [[" + local f = fedora.expand(ref) + local c = fedora.read("forge_commit" .. suffix) + if (not (c and (c == f))) and + f:match("^v[%d]") and + not (keep_file_ref == "true") then + f = f:gsub("^v", "") + end + f = f:gsub("/", "-") + print(f) + }]] + fedora.mset(myrads["all"], "forge", suffix, + { { ["url"] = forge_url, + ["ref"] = ref, + ["file_ref"] = file_ref }, + rules(suffix)[forge], rules(suffix)["default"] }, verbose) + -- Update lua copy with the result + for _, v in ipairs(fedora.qualify(myrads["rules"], "forge")) do + spec[v] = fedora.read(v .. suffix) + end + -- Source URL processing + local source = "%{forge_source" .. suffix .. "}" + local archive_file = "%{forge_archive_name" .. suffix .. + "}.%{forge_archive_extension" .. suffix .. "}" + if (fedora.expand(source):match("/([^/]+)$") + ~= fedora.expand(archive_file)) then + source = source .. "#/" .. archive_file + end + -- Setup processing + local source_suffix = [[%{lua: + local fedora = require "fedora.common" + print(fedora.source_suffix("%{forge_source]] .. suffix .. [[}")) + }]] + local extract_dir + local setup_arguments = "-n %{forge_extract_dir" .. suffix .. "}" + if not spec["forge_top_dir"] then + extract_dir = "%{forge_archive_name" .. suffix .. "}" + setup_arguments = "-T -D -a " .. + "%{forge_source_suffix" .. suffix .. "} " .. + "-c " .. setup_arguments + else + extract_dir = "%{forge_top_dir" .. suffix .. "}" + setup_arguments = "-T -D -b " .. + "%{forge_source_suffix" .. suffix .. "} " .. + setup_arguments + end + local time + if spec["forge_date"] then + time = [[%{lua: + local fedora = require "fedora.common" + local suffix = "]] .. suffix .. [[" + local date = fedora.read("forge_date" .. suffix) + if date and (date ~= "") then + rpm.expand("%(date -u --iso-8601=seconds -d " .. date .. ")") + end + }]] + else + time = "%([ -r %{_sourcedir}/" .. archive_file .. " ] && " .. + "date -u --iso-8601=seconds -r %{_sourcedir}/" .. + archive_file .. ")" + end + local date = '%{?forge_time' .. suffix .. ':' .. + '%([[ -n "%{forge_time' .. suffix .. '}" ]] && ' .. + 'date -u +%Y%m%d -d %{forge_time' .. suffix .. '})}' + fedora.mset(myrads["all"], "forge", suffix, {{ + ["source"] = source, + ["source_suffix"] = source_suffix, + ["extract_dir"] = extract_dir, + ["setup_arguments"] = setup_arguments, + -- Due to the design of %apply_patch we will need to set %{__scm} from + -- %{forge_patch_modeX} later. Therefore, expand the fallback to avoid + -- loops and inter-block side effects + ["patch_mode"] = fedora.read("__scm"), + ["patch_level"] = "-p 1", + ["date"] = date, + ["time"] = time, + }}, verbose) + -- set srpm values if not set yet + fedora.alias(rads["source"], "forge", suffix, "source", "", verbose) + -- dist processing + local distprefix = [[%{lua: + local fedora = require "fedora.common" + local ref = [=[]] .. ref .. [[]=] + local suffix = "]] .. suffix .. [[" + local d = fedora.expand(ref) + local c = fedora.read("forge_commit" .. suffix) + if c and (c == d) then + d = d:sub(1, 7) + else + local forge = require "fedora.srpm.forge" + local v = fedora.read("source_version") + if v then + if not v:match("%%") then + v = v:lower():gsub("[%p%s]+", ".") + end + if not d:match("%%") then + d = d:lower():gsub("[%p%s]+", ".") + end + for _, p in ipairs({'', 'v', 'v.', 'version', 'version.', + 'tags.v', 'tags.v.'}) do + d = forge.version_suffix(d, p .. v) + end + end + end + if (d ~= "") then + d = ".%{?forge_scm" .. suffix .. "}" .. + "%{?forge_date" .. suffix .. "}" .. d + d = fedora.expand(d) + if not d:match("%%") then + d = d:lower():gsub("[%p%s]+", ".") + end + print(d) + end + }]] + distprefix_suffix = fedora.suffix({ + ["distprefix"] = distprefix}, true, verbose) + -- Final spec variable summary if the macro was called with -i + if informative then + fedora.echo("Variables read or set by %%forge_init") + fedora.echovars(fedora.qualify(myrads["all"], "forge"), suffix) + if distprefix_suffix then + fedora.echovars({"distprefix"}, distprefix_suffix) + end + fedora.echo([[ +Note: unless explicitly set, snapshot timestamp will be computed once +%%{_sourcedir}/]] .. archive_file .. [[ + +is available. +]]) + end +end + +-- post-init info that does not depend on a particular forge suffix +local function info_generic(informative) + if informative then + fedora.echo("Other variables, that may have been set by %%forge_init") + fedora.echovars(fedora.qualify(rads["source"], "source"), "") + end +end + +-- %forge_sources core +local function source(suffix) + auto.id('forge.source("' .. suffix .. '")') + print(rpm.expand([[ +%{?forge_source]] .. suffix .. [[} +]])) +end + +-- %forge_patches core +local function patchlist(suffix) + auto.id('forge.patchlist("' .. suffix .. '")') + print(rpm.expand([[ +%{?forge_patchlist]] .. suffix .. "}")) +end + +local function env(suffix, verbose) + local myrads = radicals(suffix) + fedora.set_current(fedora.qualify(myrads["all"], "forge"), suffix, + verbose) + fedora.set_verbose(verbose) +end + +return { + version_suffix = version_suffix, + floop = floop, + init = init, + info_generic = info_generic, + env = env, + source = source, + patchlist = patchlist, +} diff --git a/forge.lua b/forge.lua deleted file mode 100644 index f396395..0000000 --- a/forge.lua +++ /dev/null @@ -1,312 +0,0 @@ --- Lua code used by macros.forge and derivatives - --- Computes the suffix of a version string, removing vprefix if it matches --- For example with vprefix 1.2.3: 1.2.3.rc2 → .rc2 but 1.2.30 → 1.2.30 not 0 -local function getversionsuffix(vstring,vprefix) - if (string.sub(vstring, 1, #vprefix) == vprefix) and - (not string.match(string.sub(vstring, #vprefix + 1), "^%.?%d")) then - return string.sub(vstring, #vprefix + 1) - else - return vstring - end -end - --- Check if an identified url is sane -local function checkforgeurl(url, id, silent) - local checkedurl = nil - local checkedid = nil - local urlpatterns = { - gitlab = { - pattern = 'https://[^/]+/[^/]+/[^/#?]+', - description = 'https://(…[-.])gitlab[-.]…/owner/repo'}, - pagure = { - pattern = 'https://[^/]+/[^/#?]+', - description = 'https://pagure.io/repo'}, - pagure_ns = { - pattern = 'https://[^/]+/[^/]+/[^/#?]+', - description = 'https://pagure.io/namespace/repo'}, - pagure_fork = { - pattern = 'https://[^/]+/fork/[^/]+/[^/#?]+', - description = 'https://pagure.io/fork/owner/repo'}, - pagure_ns_fork = { - pattern = 'https://[^/]+/fork/[^/]+/[^/]+/[^/#?]+', - description = 'https://pagure.io/fork/owner/namespace/repo'}, - ["gitea.com"] = { - pattern = 'https://[^/]+/[^/]+/[^/#?]+', - description = 'https://gitea.com/owner/repo'}, - github = { - pattern = 'https://[^/]+/[^/]+/[^/#?]+', - description = 'https://(…[-.])github[-.]…/owner/repo'}, - ["code.googlesource.com"] = { - pattern = 'https://code.googlesource.com/[^#?]*[^/#?]+', - description = 'https://code.googlesource.com/…/repo'}, - ["bitbucket.org"] = { - pattern = 'https://[^/]+/[^/]+/[^/#?]+', - description = 'https://bitbucket.org/owner/repo'}} - if (urlpatterns[id] ~= nil) then - checkedurl = string.match(url,urlpatterns[id]["pattern"]) - if (checkedurl == nil) then - if not silent then - rpm.expand("%{error:" .. id .. " URLs must match " .. urlpatterns[id]["description"] .. " !}") - end - else - checkedid = id - end - end - return checkedurl, checkedid -end - --- Check if an url matches a known forge -local function idforge(url, silent) - local forgeurl = nil - local forge = nil - if (url ~= "") then - forge = string.match(url, "^[^:]+://([^/]+)/") - if (forge == nil) then - if not silent then - rpm.expand("%{error:URLs must include a protocol such as https:// and a path starting with / !}") - end - else - if (forge == "pagure.io") then - if string.match(url, "[^:]+://pagure.io/fork/[^/]+/[^/]+/[^/]+") then - forge = "pagure_ns_fork" - elseif string.match(url, "[^:]+://pagure.io/fork/[^/]+/[^/]+") then - forge = "pagure_fork" - elseif string.match(url, "[^:]+://pagure.io/[^/]+/[^/]+") then - forge = "pagure_ns" - elseif string.match(url, "[^:]+://pagure.io/[^/]+") then - forge = "pagure" - end - elseif (string.match(forge, "^gitlab[%.-]") or string.match(forge, "[%.-]gitlab[%.]")) then - forge = "gitlab" - elseif (string.match(forge, "^github[%.-]") or string.match(forge, "[%.-]github[%.]")) then - forge = "github" - end - forgeurl, forge = checkforgeurl(url, forge, silent) - end - end - return forgeurl, forge -end - --- The forgemeta macro main processing function --- See the documentation in the macros.forge file for argument description --- Also called directly by gometa -local function meta(suffix, verbose, informative, silent) - local fedora = require "fedora.common" - local ismain = (suffix == "") or (suffix == "0") - if ismain then - fedora.zalias({"forgeurl", "forgesource", "forgesetupargs", - "archivename", "archiveext", "archiveurl", - "topdir", "extractdir", "repo", "owner", "namespace", - "scm", "tag", "commit", "shortcommit", "branch", "version", - "date", "distprefix"}, verbose) - end - local variables = { - default = { - scm = "git", - archiveext = "tar.bz2", - repo = '%{lua:print(string.match(rpm.expand("%{forgeurl' .. suffix .. '}"), "^[^:]+://[^/]+/[^/]+/([^/?#]+)"))}', - archivename = "%{repo" .. suffix .. "}-%{ref" .. suffix .. "}", - topdir = "%{archivename" .. suffix .. "}" }, - gitlab = { - archiveurl = "%{forgeurl" .. suffix .. "}/-/archive/%{ref" .. suffix .. "}/%{archivename" .. suffix .. "}.%{archiveext" .. suffix .. "}" }, - pagure = { - archiveext = "tar.gz", - repo = '%{lua:print(string.match(rpm.expand("%{forgeurl' .. suffix .. '}"), "^[^:]+://[^/]+/([^/?#]+)"))}', - archiveurl = "%{forgeurl" .. suffix .. "}/archive/%{ref" .. suffix .. "}/%{archivename" .. suffix .. "}.%{archiveext" .. suffix .. "}" }, - pagure_ns = { - archiveext = "tar.gz", - namespace = '%{lua:print(string.match(rpm.expand("%{forgeurl' .. suffix .. '}"), "^[^:]+://[^/]+/([^/]+)/[^/?#]+"))}', - repo = '%{lua:print(string.match(rpm.expand("%{forgeurl' .. suffix .. '}"), "^[^:]+://[^/]+/[^/]+/([^/?#]+)"))}', - archivename = "%{namespace" .. suffix .. "}-%{repo" .. suffix .. "}-%{ref" .. suffix .. "}", - archiveurl = "%{forgeurl" .. suffix .. "}/archive/%{ref" .. suffix .. "}/%{archivename" .. suffix .. "}.%{archiveext" .. suffix .. "}" }, - pagure_fork = { - archiveext = "tar.gz", - owner = '%{lua:print(string.match(rpm.expand("%{forgeurl' .. suffix .. '}"), "https://[^/]+/fork/([^/]+)/[^/?#]+"))}', - repo = '%{lua:print(string.match(rpm.expand("%{forgeurl' .. suffix .. '}"), "https://[^/]+/fork/[^/]+/([^/?#]+)"))}', - archivename = "%{owner" .. suffix .. "}-%{repo" .. suffix .. "}-%{ref" .. suffix .. "}", - archiveurl = "%{forgeurl" .. suffix .. "}/archive/%{ref" .. suffix .. "}/%{archivename" .. suffix .. "}.%{archiveext" .. suffix .. "}" }, - pagure_ns_fork = { - owner = '%{lua:print(string.match(rpm.expand("%{forgeurl' .. suffix .. '}"), "https://[^/]+/fork/([^/]+)/[^/]+/[^/?#]+"))}', - namespace = '%{lua:print(string.match(rpm.expand("%{forgeurl' .. suffix .. '}"), "https://[^/]+/fork/[^/]+/([^/]+)/[^/?#]+")}', - repo = '%{lua:print(string.match(rpm.expand("%{forgeurl' .. suffix .. '}"), "https://[^/]+/fork/[^/]+/[^/]+/([^/?#]+)")}', - archivename = "%{owner" .. suffix .. "}-%{namespace" .. suffix .. "}-%{repo" .. suffix .. "}-%{ref" .. suffix .. "}", - archiveurl = "%{forgeurl" .. suffix .. "}/archive/%{ref" .. suffix .. "}/%{archivename" .. suffix .. "}.%{archiveext" .. suffix .. "}" }, - ["gitea.com"] = { - archiveext = "tar.gz", - archivename = "%{fileref" .. suffix .. "}", - archiveurl = "%{forgeurl" .. suffix .. "}/archive/%{ref" .. suffix .. "}.%{archiveext" .. suffix .. "}", - topdir = "%{repo}" }, - github = { - archiveext = "tar.gz", - archivename = "%{repo" .. suffix .. "}-%{fileref" .. suffix .. "}", - archiveurl = "%{forgeurl" .. suffix .. "}/archive/%{ref" .. suffix .. "}/%{archivename" .. suffix .. "}.%{archiveext" .. suffix .. "}" }, - ["code.googlesource.com"] = { - archiveext = "tar.gz", - repo = '%{lua:print(string.match(rpm.expand("%{forgeurl' .. suffix .. '}"), "^[^:]+://.+/([^/?#]+)"))}', - archiveurl = "%{forgeurl" .. suffix .. "}/+archive/%{ref" .. suffix .. "}.%{archiveext" .. suffix .. "}", - topdir = "" }, - ["bitbucket.org"] = { - shortcommit = '%{lua:print(string.sub(rpm.expand("%{commit' .. suffix .. '}"), 1, 12))}', - owner = '%{lua:print(string.match(rpm.expand("%{forgeurl' .. suffix .. '}"), "^[^:]+://[^/]+/([^/?#]+)"))}', - archivename = "%{owner" .. suffix .. "}-%{repo" .. suffix .. "}-%{shortcommit" .. suffix .. "}", - archiveurl = "%{forgeurl" .. suffix .. "}/get/%{ref" .. suffix .. "}.%{archiveext" .. suffix .. "}" } } - -- Packaging a moving branch is quite a bad idea, but since at least Gitlab - -- will treat branches and tags the same way better support branches explicitly - -- than have packagers hijack %{tag} to download branch states - local spec = {} - for _, v in ipairs({'forgeurl','tag','commit','branch','version'}) do - spec[v] = rpm.expand("%{?" .. v .. suffix .. "}") - end - -- Compute the reference of the object to fetch - local isrelease = false - if (spec["tag"] ~= "") then ref = "%{?tag" .. suffix .. "}" - elseif (spec["commit"] ~= "") then ref = "%{?commit" .. suffix .. "}" - elseif (spec["branch"] ~= "") then ref = "%{?branch" .. suffix .. "}" - else ref = "%{?version" .. suffix .. "}" - isrelease = true - end - if (rpm.expand(ref) == "") then - if (suffix == "") then - rpm.expand("%{error:You need to define Version:, %{commit} or %{tag} before the macro invocation !}") - else - rpm.expand("%{error:You need to define %{version" .. suffix .. "}, %{commit" .. suffix .. "} or %{tag" .. suffix .. "} before the macro invocation !}") - end - end - local forgeurl = spec["forgeurl"] - -- For backwards compatibility only - local expliciturl = rpm.expand("%{?-u*}") - if (expliciturl ~= "") then - rpm.expand("%{warn:-u use in %%forgemeta is deprecated, use -z instead to select a separate set of rpm variables!}") - forgeurl = expliciturl - end - local forge - forgeurl, forge = idforge(forgeurl, silent) - if (forge ~= nil) then - fedora.explicitset("forgeurl" .. suffix, forgeurl, verbose) - -- Custom processing of quirky forges that can not be handled with simple variables - if (forge == "github") then - -- Workaround the way GitHub injects "v"s before some version strings (but not all!) - -- To package one of the minority of sane GitHub projects that do not munge their version - -- strings set tag to %{version} in your spec - local fileref = ref - if (ref == "%{?version" .. suffix .. "}") then - ref = "v" .. ref - elseif (fileref ~= "%{?commit" .. suffix .. "}") and - string.match(rpm.expand(fileref), "^v[%d]") then - fileref = string.gsub(rpm.expand(fileref), "^v", "") - elseif (string.match(rpm.expand(fileref), "/")) then - fileref = string.gsub(rpm.expand(fileref), "/", "-") - end - fedora.safeset("fileref" .. suffix, fileref, verbose) - elseif (forge == "gitea.com") then - -- Workaround the way gitea mangles /s in ref names - local fileref = ref - fileref = string.gsub(rpm.expand(fileref), "/", "-") - fedora.safeset("fileref" .. suffix, fileref, verbose) - elseif (forge == "code.googlesource.com") then - if (ref == "%{?version" .. suffix .. "}") then - ref = "v" .. ref - end - elseif (forge == "bitbucket.org") then - if (spec["commit"] == "") then - rpm.expand("%{error:All BitBucket URLs require commit value knowledge: you need to define %{commit}!}") - end - end - fedora.safeset("ref" .. suffix, ref, verbose) - -- Mass setting of the remaining variables - for k,v in pairs(variables[forge]) do - fedora.safeset(k .. suffix, variables[forge][k], verbose) - end - for k,v in pairs(variables["default"]) do - if (variables[forge][k] == nil) then - fedora.safeset(k .. suffix, variables["default"][k], verbose) - end - end - end - -- Generic rules - for _, v in ipairs({'archiveurl','archivename','archiveext','topdir'}) do - spec[v] = rpm.expand("%{?" .. v .. suffix .. "}") - end - -- Source URL processing (computing the forgesource spec variable) - local forgesource = "%{archiveurl" .. suffix .. "}" - if (string.match(spec["archiveurl"], "/([^/]+)$") ~= spec["archivename"] .. "." .. spec["archiveext"]) then - forgesource = "%{?archiveurl" .. suffix .. "}#/%{?archivename" .. suffix .. "}.%{archiveext" .. suffix .. "}" - end - fedora.safeset("forgesource" .. suffix, forgesource, verbose) - -- Setup processing (computing the forgesetup and extractdir variables) - local forgesetupargs = "-n %{extractdir" .. suffix .. "}" - local extractdir = "%{topdir" .. suffix .. "}" - if (spec["topdir"] == "") then - forgesetupargs = "-c " .. forgesetupargs - extractdir = "%{archivename" .. suffix .. "}" - end - if not ismain then - if (spec["topdir"] ~= "") then - forgesetupargs = "-T -D -b " .. suffix .. " " .. forgesetupargs - else - forgesetupargs = "-T -D -a " .. suffix .. " " .. forgesetupargs - end - end - fedora.safeset("forgesetupargs" .. suffix, forgesetupargs, verbose) - fedora.safeset("extractdir" .. suffix, extractdir, verbose) - -- dist processing (computing the correct prefix for snapshots) - local distprefix = "" - if not isrelease then - distprefix = string.lower(rpm.expand(ref)) - if (ref == "%{?commit" .. suffix .. "}") then - distprefix = string.sub(distprefix, 1, 7) - elseif (ref ~= "%{?branch" .. suffix .. "}") then - distprefix = string.gsub(distprefix, "[%p%s]+", ".") - distprefix = string.gsub(distprefix, "^" .. string.lower(rpm.expand("%{?repo}")) .. "%.?", "") - local v = string.gsub(rpm.expand("%{version}"), "[%p%s]+", ".") - for _, p in ipairs({'','v','v.','version','version.','tags.v', 'tags.v.'}) do - distprefix = getversionsuffix(distprefix, p .. v) - end - distprefix = string.gsub(distprefix, "^%.", "") - end - if (distprefix ~= "") then - distprefix = "%{scm" .. suffix .. "}" .. distprefix - date = rpm.expand("%{?date" .. suffix .. "}") - if (date ~= "") then - distprefix = date .. distprefix - else - distprefix = "%([ -r %{_sourcedir}/%{archivename" .. suffix .. "}.%{archiveext" .. suffix .. "} ] && date +%Y%m%d -u -r %{_sourcedir}/%{archivename" .. suffix .. "}.%{archiveext" .. suffix .. "})" .. distprefix - end - distprefix = "." .. distprefix - end - end - if (spec["version"] ~= "") and - (spec["version"] ~= "0") and - (spec["version"] ~= rpm.expand("%{?version}")) then - distprefix = ".%{version" .. suffix .. "}" .. distprefix - end - if (rpm.expand(distprefix) ~= "") then - if not ismain then - distprefix = string.gsub(distprefix, "^%.", ".s") - end - fedora.safeset ("distprefix" .. suffix, distprefix, verbose) - end - if ismain then - fedora.zalias({"forgeurl", "forgesource", "forgesetupargs", - "archivename", "archiveext", "archiveurl", - "topdir", "extractdir", "repo", "owner", "namespace", - "scm", "shortcommit", "distprefix"}, verbose) - end - -- Final spec variable summary if the macro was called with -i - if informative then - rpm.expand("%{echo:Packaging variables read or set by %%forgemeta}") - fedora.echovars({"forgeurl", "forgesource", "forgesetupargs", - "archivename", "archiveext", "archiveurl", - "topdir", "extractdir", "repo", "owner", "namespace", - "scm", "tag", "commit", "shortcommit", "branch", "version", - "date", "distprefix"}, suffix) - fedora.echovars({"dist"},"") - rpm.expand("%{echo: (snapshot date is either manually supplied or computed once %%{_sourcedir}/%%{archivename" .. suffix .. "}.%%{archiveext" .. suffix .. "} is available)}") - end -end - -return { - meta = meta, -} - diff --git a/forge_deprecated-srpm.lua b/forge_deprecated-srpm.lua new file mode 100644 index 0000000..c37effa --- /dev/null +++ b/forge_deprecated-srpm.lua @@ -0,0 +1,73 @@ +-- Lua code used by macros.forge_deprecated-srpm + +local fedora = require "fedora.common" +local auto = require "fedora.auto" + +local function suffixes() + return fedora.suffixes("forgeurl") +end + +local function floop(f, index, otherargs) + auto.floop(f, index, suffixes(), otherargs) +end + +local changed = { + ["forgeurl"] = "forge_url", + ["forgesource"] = "forge_source", + ["forgesetupargs"] = "forge_setup_arguments", + ["shortcommit"] = "forge_short_commit", + ["extractdir"] = "forge_extract_dir", + ["topdir"] = "forge_top_dir", + ["archivename"] = "forge_archive_name", + ["archiveext"] = "forge_archive_extension", + ["archiveurl"] = "forge_source", + ["fileref"] = "forge_file_ref" +} + +local namespaced = {"scm", "ref", "tag", "commit", "version", "branch", + "owner", "repo", "namespace", "date"} + +-- %forge_deprecated_init core +local function init(suffix, verbose) + auto.id('forge_deprecated.init("' .. suffix .. '")') + fedora.warning([[ +Aliasing deprecated variable names to “forge_” variables. This aliasing will be +removed.]]) + local ismain = (suffix == "") or (suffix == "0") + if ismain then + olds = {} + for old, _ in pairs(changed) do + table.insert(olds, old) + end + fedora.zalias(olds, verbose) + fedora.zalias(namespaced, verbose) + end + for old, new in pairs(changed) do + fedora.safeset(new .. suffix, fedora.ref(old .. suffix), verbose) + end + fedora.alias(namespaced, "", suffix, "forge", suffix, verbose) +end + +local function post_init(suffix, verbose) + auto.id('forge_deprecated.post_init("' .. suffix .. '")') + local ismain = (suffix == "") or (suffix == "0") + for old, new in pairs(changed) do + fedora.safeset(old .. suffix, fedora.ref(new .. suffix), verbose) + end + fedora.alias(namespaced, "forge", suffix, "", suffix, verbose) + if ismain then + olds = {} + for old, _ in pairs(changed) do + table.insert(olds, old) + end + fedora.zalias(olds, verbose) + fedora.zalias(namespaced, verbose) + end +end + + +return { + floop = floop, + init = init, + post_init = post_init, +} diff --git a/macros.auto-rpm b/macros.auto-rpm new file mode 100644 index 0000000..3dc9483 --- /dev/null +++ b/macros.auto-rpm @@ -0,0 +1,63 @@ +# Automated packaging framework, safe to use after the SRPM build stage + +# Macro registration is described in macros.auto-srpm + +# Executes automated packaging macros in the %prep section +# Control variables, flags and arguments: +# -v be verbose +%auto_prep(v) %{lua: +local fedora = require "fedora.common" +local auto = require "fedora.auto" +local verbose = fedora.hasflag("v") +auto.exec("auto_prep", verbose) +} + +# Executes automated packaging macros in the %generate_buildrequires section +# macros registered for %auto_pkg execution must make sure to ignore +# verbosity flags or process them in a way that does not interfere with the +# way %generate_buildrequires captures stdout +%auto_generate_buildrequires() %{lua: +local fedora = require "fedora.common" +local auto = require "fedora.auto" +auto.exec("auto_generate_buildrequires", false) +} + +# Executes automated packaging macros in the %build section +# Control variables, flags and arguments: +# -v be verbose +%auto_build(v) %{lua: +local fedora = require "fedora.common" +local auto = require "fedora.auto" +local verbose = fedora.hasflag("v") +auto.exec("auto_build", verbose) +} + +# Executes automated packaging macros in the %install section +# Control variables, flags and arguments: +# -v be verbose +%auto_install(v) %{lua: +local fedora = require "fedora.common" +local auto = require "fedora.auto" +local verbose = fedora.hasflag("v") +auto.exec("auto_install", verbose) +} + +# Executes automated packaging macros in the %check section +# Control variables, flags and arguments: +# -v be verbose +%auto_check(v) %{lua: +local fedora = require "fedora.common" +local auto = require "fedora.auto" +local verbose = fedora.hasflag("v") +auto.exec("auto_check", verbose) +} + +# Executes automated packaging macros that create %files sections +# Control variables, flags and arguments: +# -v be verbose +%auto_files(v) %{lua: +local fedora = require "fedora.common" +local auto = require "fedora.auto" +local verbose = fedora.hasflag("v") +auto.exec("auto_files", verbose) +} diff --git a/macros.auto-srpm b/macros.auto-srpm new file mode 100644 index 0000000..9151e44 --- /dev/null +++ b/macros.auto-srpm @@ -0,0 +1,82 @@ +# Automated packaging framework, SRPM build stage + +# To register macros for execution by one of the %auto_ macros, drop a +# file with the auto extension in %auto_dir that contains lines in the +# following syntax: +# auto_ +# +# Registered macros MUST accept the -v verbosity flag. They will usually depend +# on control variables computed and set as part of %auto_init. +# +# %auto_ macros accept %{_after} control variables. When that +# is the case: +# – %{_after} should contain a space separated list of macro names, +# without leading %s +# – if is registered for %auto_, %auto_ will make sure to +# execute after all the macros listed in %{_after}, +# – elements listed in %{_after} but not registered for %auto_, +# are ignored +# +# %auto_ macros accept %{_last}. When %{_last} is set to +# anything other than false, and absent other considerations, % will be +# moved to the end of the execution plan. + +# Directory holding auto_ macro registrations. +%_rpmautodir @@_RPMAUTODIR@@ + +# Executes automated packaging macros in the preamble, to initialize variables +# (before %auto_pkg) +# Control variables, flags and arguments: +# -v be verbose +%auto_init(v) %{lua: +local fedora = require "fedora.common" +local auto = require "fedora.auto" +local verbose = fedora.hasflag("v") +auto.exec("auto_init", verbose) +} + +# Executes automated packaging macros in the preamble, to create package +# headers (after %auto_init) +# macros registered for %auto_pkg execution must provide a +# %_source_names counterpart that returns a space-separated list of the +# potential SRPM names that they may create +# Control variables, flags and arguments: +# -v be verbose +%auto_pkg(v) %{lua: +local fedora = require "fedora.common" +local auto = require "fedora.auto" +local buildsys = require "fedora.srpm.buildsys" +local verbose = fedora.hasflag("v") +if fedora.read("__auto_pkg_done") then + if verbose then + fedora.echo([[ +Detected previous %%auto_pkg execution, resetting SRPM state. Additive +executions of %%auto_pkg are not supported due to the fact rpmbuild will +itself re-execute the preamble under some circumstances. + ]]) + end + buildsys.reset(verbose) +end +auto.pkg_exec("auto_pkg", verbose) +fedora.safeset("__auto_pkg_done", true, verbose) +} + +# Executes automated packaging macros in the %sourcelist section +# Control variables, flags and arguments: +# -v be verbose +%auto_sources(v) %{lua: +local fedora = require "fedora.common" +local auto = require "fedora.auto" +local verbose = fedora.hasflag("v") +auto.exec("auto_sources", verbose) +} + +# Executes automated packaging macros in the %patchlist section +# Control variables, flags and arguments: +# -v be verbose +%auto_patches(v) %{lua: +local fedora = require "fedora.common" +local auto = require "fedora.auto" +local verbose = fedora.hasflag("v") +auto.exec("auto_patches", verbose) +} diff --git a/macros.buildsys-srpm b/macros.buildsys-srpm new file mode 100644 index 0000000..93d916b --- /dev/null +++ b/macros.buildsys-srpm @@ -0,0 +1,46 @@ +# buildsys macros, safe to use at SRPM build stage + +# A single Name: and %package substitute +# Control variables, flags and arguments: +# %{source_name} the SRPM name +# %{source_summary} the SRPM summary +# %{source_epoch} the SRPM epoch +# %{source_version} the SRPM version +# %{source_release} the SRPM release (before %{?dist}) +# %{source_post_release} the SRPM release (after %{?dist}) +# %{source_license} the SRPM license +# %{source_url} the SRPM URL +# %{source_description} the SRPM description +# -n declare a package named +# (%package-like behavior) +# -v be verbose +# %1 declare a package named %{source_name}-%{%1} +# (%package-like behavior) +%new_package(n:v) %{lua: +local fedora = require "fedora.common" +local buildsys = require "fedora.srpm.buildsys" +local pkg_name = fedora.readflag("n") +local verbose = fedora.hasflag("v") +local name_suffix = fedora.read("1") +buildsys.new_package(pkg_name, name_suffix, verbose) +} + +# A complete package header generator +# Control variables, flags and arguments: +# %{__current_buildsys_name} the (sub)package name +# %{__current_buildsys_summary} the (sub)package summary +# %{__current_buildsys_epoch} the (sub)package epoch +# %{__current_buildsys_version} the (sub)package version +# %{__current_buildsys_release} the (sub)package release (before %{dist}) +# %{__current_buildsys_post_release} the (sub)package release (after %{dist}) +# %{__current_buildsys_license} the (sub)package license +# %{__current_buildsys_url} the (sub)package URL +# %{__current_buildsys_tags} free-form additional (sub)package tags +# %{__current_buildsys_description} the (sub)package description +# -v be verbose +%buildsys_pkg(v) %{lua: +local fedora = require "fedora.common" +local buildsys = require "fedora.srpm.buildsys" +local verbose = fedora.hasflag("v") +buildsys.pkg(verbose) +} diff --git a/macros.doc-rpm b/macros.doc-rpm new file mode 100644 index 0000000..24ea645 --- /dev/null +++ b/macros.doc-rpm @@ -0,0 +1,25 @@ +# Installs the files associated with a documentation package in %install +# Control variables, flags and arguments: +# -z only process the zth block of definitions +# "" for the no-suffix block +# -v be verbose +%doc_install(z:v) %{lua: +local fedora = require "fedora.common" +local doc = require "fedora.rpm.doc" +local suffix = fedora.readflag("z") +local verbose = fedora.hasflag("v") +doc.floop(doc.install, suffix, {verbose}) +} + +# Creates a %files section for a documentation package +# Control variables, flags and arguments: +# -z only process the zth block of definitions +# "" for the no-suffix block +# -v be verbose +%doc_files(z:v) %{lua: +local fedora = require "fedora.common" +local doc = require "fedora.rpm.doc" +local suffix = fedora.readflag("z") +local verbose = fedora.hasflag("v") +doc.floop(doc.files, suffix, {verbose}) +} diff --git a/macros.doc-rpm.internal b/macros.doc-rpm.internal new file mode 100644 index 0000000..7951750 --- /dev/null +++ b/macros.doc-rpm.internal @@ -0,0 +1,34 @@ +# RPM macros for documentation packages +# +# Internal utility macros +# Direct use in spec file is not supported + +%__doc_install() %{expand: +( + %{?__current_shell_quiet} + cd %{_builddir}/%{?buildsubdir} + echo "%%defattr(644, root, root, 0755)" >> %{?__current_doc_list} + ( + %define listfiles_include %{?__current_doc_docs} + %define listfiles_exclude %{?__current_docs_docs_exclude} + (%listfiles + ) | sed -E '/^$/d + s|^[[:blank:]]*(.+)[[:blank:]]*$|%%doc "./\\1"|g' \\ + | tee -a %{?__current_doc_list} > /dev/null + ) + ( + %define listfiles_include %{?__current_doc_licenses} + %define listfiles_exclude %{?__current_doc_licenses_exclude} + (%listfiles + ) | sed -E '/^$/d + s|^[[:blank:]]*(.+)[[:blank:]]*$|license "./\\1"|g + s/^/%%/g' \\ + | tee -a %{?__current_doc_list} > /dev/null + ) + %{?__current_shell_verbose} +) +} + +%__doc_files() %{expand: +%files -n %{__current_doc_name} -f %{__current_doc_list} +} diff --git a/macros.doc-srpm b/macros.doc-srpm new file mode 100644 index 0000000..3573314 --- /dev/null +++ b/macros.doc-srpm @@ -0,0 +1,50 @@ +# Forge macros, safe to use at SRPM build stage + +# Computes documentation-related variables +# Control variables, flags and arguments: +# %{doc_packages} create a documentation package +# that enhances the space-separated +# of packages (default: all) +# %{doc_docs} space-separated list of shell +# matching documentation +# files to include, if they exist +# at the start of the %install +# section; globs are relative to +# %{_builddir}/%{?buildsubdir} +# %{doc_docs_exclude} space-separated list of shell +# to exclude from +# %{doc_docs} +# %{doc_licenses} space-separated list of shell +# matching license files to +# include, if they exist at the +# start of the %install section +# %{doc_licenses_exclude} space-separated list of shell +# to exclude from +# %{doc_licenses} +# -z only process the zth block of definitions +# "" for the no-suffix block +# -v be verbose +# -i list the resulting variable values +%doc_init(z:vi) %{lua: +local fedora = require "fedora.common" +local doc = require "fedora.srpm.doc" +local suffix = fedora.readflag("z") +local informative = fedora.hasflag("i") +local verbose = fedora.hasflag("v") +doc.floop(doc.init, suffix, {verbose, informative}) +} + +# Creates documentation package headers +# Control variables, flags and arguments: +# -z only process the zth block of definitions +# "" for the no-suffix block +# -v be verbose +%doc_pkg(z:va) %{lua: +local fedora = require "fedora.common" +local doc = require "fedora.srpm.doc" +local suffix = fedora.readflag("z") +local verbose = fedora.hasflag("v") +doc.floop(doc.pkg, suffix, {verbose}) +} + +%doc_pkg_last true diff --git a/macros.fedora-misc b/macros.fedora-misc deleted file mode 100644 index 82286f3..0000000 --- a/macros.fedora-misc +++ /dev/null @@ -1,63 +0,0 @@ -# Fedora macros, safe to use after the SRPM build stage - -# Lists files matching inclusion globs, excluding files matching exclusion -# globs -# – globs are space-separated lists of shell globs. Such lists require -# %{quote:} use when passed as rpm arguments or flags. -# Control variables, flags and arguments: -# %{listfiles_include} inclusion globs -# %{listfiles_exclude} exclusion globs -# -i inclusion globs -# -x exclusion globs -# … arguments passed to the macro without flags will be -# interpreted as inclusion globs -%listfiles(i:x:) %{expand: -%if %{lua: print(string.len(rpm.expand("%{?-i*}%{?listfiles_include}%*")))} - listfiles_include=$(realpath -e --relative-base=. %{?-i*} %{?listfiles_include} %* | sort -u) - %if %{lua: print(string.len(rpm.expand("%{?-x*}%{?listfiles_exclude}")))} - while IFS= read -r finc ; do - realpath -qe --relative-base=. %{?-x*} %{?listfiles_exclude} \\ - | sort -u | grep -q "${finc}" || echo "${finc}" - done <<< "${listfiles_include}" - %else - echo "${listfiles_include}" - %endif -%endif -} - -# https://github.com/rpm-software-management/rpm/issues/581 -# Writes the contents of a list of rpm variables to a macro file -# Control variables, flags and arguments: -# -f the macro file to process: -# – it must contain corresponding anchors -# – for example %writevars -f myfile foo bar will replace: -# @@FOO@@ with the rpm evaluation of %{foo} and -# @@BAR@@ with the rpm evaluation of %{bar} -# in myfile -%writevars(f:) %{lua: -local fedora = require "fedora.common" -local macrofile = rpm.expand("%{-f*}") -local rpmvars = {} -for i = 1, rpm.expand("%#") do - table.insert(rpmvars, rpm.expand("%" .. i)) -end -fedora.writevars(macrofile,rpmvars) -} - -# gpgverify verifies signed sources. There is documentation in the script. -%gpgverify(k:s:d:) %{lua: -local script = rpm.expand("%{_rpmconfigdir}/redhat/gpgverify ") -local keyring = rpm.expand("%{-k*}") -local signature = rpm.expand("%{-s*}") -local data = rpm.expand("%{-d*}") -print(script) -if keyring ~= "" then - print(rpm.expand("--keyring='%{SOURCE" .. keyring .. "}' ")) -end -if signature ~= "" then - print(rpm.expand("--signature='%{SOURCE" .. signature .. "}' ")) -end -if data ~= "" then - print(rpm.expand("--data='%{SOURCE" .. data .. "}' ")) -end -} diff --git a/macros.fedora-misc-rpm b/macros.fedora-misc-rpm new file mode 100644 index 0000000..f69889b --- /dev/null +++ b/macros.fedora-misc-rpm @@ -0,0 +1,67 @@ +# Fedora macros, safe to use after the SRPM build stage + +# Lists files matching inclusion globs, excluding files matching exclusion +# globs +# – globs are space-separated lists of shell globs. Such lists require +# %{quote:} use when passed as rpm arguments or flags. +# Control variables, flags and arguments: +# %{listfiles_include} inclusion globs +# %{listfiles_exclude} exclusion globs +# -i inclusion globs +# -x exclusion globs +# … arguments passed to the macro without flags will be +# interpreted as inclusion globs +%listfiles(i:x:) %{expand: +%dnl %listfiles must do something shell-side in all cases otherwise (%listfiles +%dnl ) | will fail +set +x +%if %{lua: print(string.len(rpm.expand("%{?-i*}%{?listfiles_include}%*")))} + listfiles_include=$(realpath -e --relative-base=. %{?-i*} %{?listfiles_include} %* | sort -u) + %if %{lua: print(string.len(rpm.expand("%{?-x*}%{?listfiles_exclude}")))} + while IFS= read -r finc ; do + realpath -qe --relative-base=. %{?-x*} %{?listfiles_exclude} \\ + | sort -u | grep -q "${finc}" || echo "${finc}" + done <<< "${listfiles_include}" + %else + echo "${listfiles_include}" + %endif +%endif +} + +# https://github.com/rpm-software-management/rpm/issues/581 +# Writes the contents of a list of rpm variables to a macro file +# Control variables, flags and arguments: +# -f the macro file to process: +# – it must contain corresponding anchors +# – for example %writevars -f myfile foo bar will replace: +# @@FOO@@ with the rpm evaluation of %{foo} and +# @@BAR@@ with the rpm evaluation of %{bar} +# in myfile +%writevars(f:) %{lua: +local fedora = require "fedora.common" +local macrofile = fedora.readflag("f") +local rpmvars = {} +for i = 1, rpm.expand("%#") do + table.insert(rpmvars, rpm.expand("%" .. i)) +end +fedora.writevars(macrofile,rpmvars) +} + +# gpgverify verifies signed sources. There is documentation in the script. +%gpgverify(k:s:d:) %{lua: +local fedora = require "fedora.common" +local script = rpm.expand("%{_rpmconfigdir}/redhat/gpgverify ") +local keyring = fedora.readflag("k") +local signature = fedora.readflag("s") +local data = fedora.readflag("d") +print(script) +if keyring then + print(rpm.expand( "--keyring='%{SOURCE" .. keyring .. "}' ")) +end +if signature then + print(rpm.expand("--signature='%{SOURCE" .. signature .. "}' ")) +end +if data then + print(rpm.expand( "--data='%{SOURCE" .. data .. "}' ")) +end +} diff --git a/macros.fedora-misc-srpm b/macros.fedora-misc-srpm index f3ac0ce..c429c28 100644 --- a/macros.fedora-misc-srpm +++ b/macros.fedora-misc-srpm @@ -13,7 +13,7 @@ # Applies the fedora.wordwrap filter to the content of an rpm variable, and # prints the result. # – putting multiple lines of UTF-8 text inside a variable is usually -# accomplished with %{expand:some_text} +# accomplished with %{expand:Lorem ipsum dolor sit amet…} # Control variables, flags and arguments: # -v (default value: _description) %wordwrap(v:) %{lua: @@ -21,23 +21,3 @@ local fedora = require "fedora.common" local variable = "%{?" .. rpm.expand("%{-v*}%{!-v:_description}") .. "}" print(fedora.wordwrap(variable)) } - -# A single Name: and %package substitute -# Control variables, flags and arguments: -# %{source_name} the SRPM name -# %{source_summary} the SRPM summary -# %{source_description} the SRPM description -# -n declare a package named -# (%package-like behavior) -# -v be verbose -# %1 declare a package named %{source_name}-%{%1} -# (%package-like behavior) -%new_package(n:v) %{lua: -local fedora = require "fedora.common" -local pkg_name = fedora.readflag("n") -local verbose = fedora.hasflag("v") -local name_suffix = fedora.read("1") -local source_name = fedora.read("source_name") -local first = not ( fedora.read("name") or fedora.read("currentname") ) -fedora.new_package(source_name, pkg_name, name_suffix, first, verbose) -} diff --git a/macros.forge b/macros.forge deleted file mode 100644 index efd7327..0000000 --- a/macros.forge +++ /dev/null @@ -1,70 +0,0 @@ -# Computes forge-related variables for use in the rest of the spec file -# Control variables, flags and arguments: -# %{forgeurl} the project url on the target forge -# %{tag} the packaged tag, OR -# %{commit} the packaged commit, OR -# %{version} the packaged version -# – %{version}/%{version0} are set via: -# Version: -# – because git is lacking a built-in version -# reference, %{version} will be translated -# into %{tag} using unreliable heuristics; -# set %{tag} directly if those fail -# %{date} the packaged timestamp -# … %forgemeta will compute a huge number of variables: -# — the packager can override it by setting some of -# those before the %forgemeta call -# – use the -i flag to list those variables -# -z only process the zth block of definitions -# "" for the no-suffix block -# -i list the resulting variable values -# -s silently ignore problems in %{forgeurl} -# -v be verbose -# -a process all sources in one go, instead of using -# separate -z calls -%forgemeta(z:isva) %{lua: -local fedora = require "fedora.common" -local forge = require "fedora.srpm.forge" -local verbose = rpm.expand("%{-v}") ~= "" -local informative = rpm.expand("%{-i}") ~= "" -local silent = rpm.expand("%{-s}") ~= "" -local processall = (rpm.expand("%{-a}") ~= "") and (rpm.expand("%{-z}") == "") -if processall then - for _,s in pairs(fedora.getsuffixes("forgeurl")) do - forge.meta(s,verbose,informative,silent) - end -else - forge.meta(rpm.expand("%{-z*}"),verbose,informative,silent) -end -} - -# Unpacks sources computed by %forgemeta -# Control variables, flags and arguments: -# %{forgesource} the source archive that will be processed -# %{forgesetupargs} %setup arguments - -# -z only process the zth block of definitions -# "" for the no-suffix block -# -v be verbose -# -a process all sources in one go, instead of using -# separate -z calls -%forgesetup(z:va) %{lua: -local fedora = require "fedora.common" -if (rpm.expand("%{-z}") == "") and (rpm.expand("%{-a}") ~= "") then - for _,s in pairs(fedora.getsuffixes("forgesetupargs")) do - print(rpm.expand("%setup %{!-v:-q} %{?forgesetupargs" .. s .. "}\\n")) - end -else - print( rpm.expand("%setup %{!-v:-q} %{?forgesetupargs" .. rpm.expand("%{-z*}") .. "}\\n")) -end -} - -# Calls %autosetup using %forgemeta results -# – this will probably be removed since it is unsafe in presence of multiple -# sources -# Control variables, flags and arguments: -# -z process the zth block of definitions -# -v -N -S -p relayed to %autosetup -%forgeautosetup(z:vNS:p:q) %{lua: -print(rpm.expand("%autosetup %{-v} %{-N} %{?-S} %{?-p} %{?forgesetupargs" .. rpm.expand("%{-z*}") .. "}\\n")) -} diff --git a/macros.forge-rpm b/macros.forge-rpm new file mode 100644 index 0000000..ab80335 --- /dev/null +++ b/macros.forge-rpm @@ -0,0 +1,29 @@ +# Forge macros, safe to use after the SRPM build stage + +# Unpacks and patches sources computed by %forgemeta +# Control variables, flags and arguments: +# %{forge_source} the source archive that will be processed +# %{forge_setup_arguments} %setup arguments +# %{forge_patchlist} list of patches to apply using +# %apply_patch in %patchlist one filename +# per line format +# %{patch_mode} %apply_patch mode (%{__scm} like) +# is the usual %patchX +# number +# %{patch_level} default: -p1 +# %{forge_patch_mode} fallback for %{patch_mode} +# if it is not declared for one of the +# patches in %{forge_patchlist} +# %{forge_patch_level} fallback for %{patch_level} +# if it is not declared for one of the +# patches in %{forge_patchlist} +# -z only process the zth block of definitions +# "" for the no-suffix block +# -v be verbose +%forge_prep(z:v) %{lua: +local fedora = require "fedora.common" +local forge = require "fedora.rpm.forge" +local suffix = fedora.readflag("z") +local verbose = fedora.hasflag("v") +forge.floop(forge.prep, suffix, {verbose}) +} diff --git a/macros.forge-srpm b/macros.forge-srpm new file mode 100644 index 0000000..2ad1d1b --- /dev/null +++ b/macros.forge-srpm @@ -0,0 +1,65 @@ +# Forge macros, safe to use at SRPM build stage + +# Computes forge-related variables for use in the rest of the spec file +# Control variables, flags and arguments: +# %{forge_url} the project url on the target forge +# %{tag} the packaged tag, OR +# %{commit} the packaged commit, OR +# %{version} the packaged version +# – %{version}/%{version0} are set via: +# Version: +# – because git is lacking a built-in version +# reference, %{version} will be translated +# into %{tag} using unreliable heuristics; +# set %{tag} directly if those fail +# %{time} the packaged timestamp, in RFC 3339 format +# (as outputed by date -u --iso-8601=seconds) +# … %forge_init will compute a huge number of variables: +# — the packager can override it by setting some of +# those before the %forge_init call +# – use the -i flag to list those variables +# -z only process the zth block of definitions +# "" for the no-suffix block +# -i list the resulting variable values +# -s silently ignore problems in %{forgeurl} +# -v be verbose +%forge_init(z:isv) %{lua: +local fedora = require "fedora.common" +local forge = require "fedora.srpm.forge" +local suffix = fedora.readflag("z") +local informative = fedora.hasflag("i") +local silent = fedora.hasflag("s") +local verbose = fedora.hasflag("v") +forge.floop(forge.init, suffix, {verbose, informative, silent}) +forge.info_generic(informative) +} + +# Returns all the %{forge_source}s +# – typically used in the %sourcelist section +# Control variables, flags and arguments: +# %{forge_source} one of the sources to return +# -z only process the zth block of definitions +# "" for the no-suffix block +# -v be verbose +%forge_sources(z:v) %{lua: +local fedora = require "fedora.common" +local forge = require "fedora.srpm.forge" +local suffix = fedora.readflag("z") +local verbose = fedora.hasflag("v") +forge.floop(forge.source, suffix, {}) +} + +# Returns all the patches declared in %{forge_patchlist}s +# – typically used in the %patchlist section +# Control variables, flags and arguments: +# %{forge_patchlist} one of the patch lists to return +# -z only process the zth block of definitions +# "" for the no-suffix block +# -v be verbose +%forge_patches(z:v) %{lua: +local fedora = require "fedora.common" +local forge = require "fedora.srpm.forge" +local suffix = fedora.readflag("z") +local verbose = fedora.hasflag("v") +forge.floop(forge.patchlist, suffix, {}) +} diff --git a/macros.forge_deprecated-rpm b/macros.forge_deprecated-rpm new file mode 100644 index 0000000..8f2f98d --- /dev/null +++ b/macros.forge_deprecated-rpm @@ -0,0 +1,32 @@ +# Legacy deprecated compatibility forge logic; RPM stage +# remove in F35 + +%forgesetup(z:va) %{lua: +local fedora = require "fedora.common" +local forge = require "fedora.rpm.forge" +local suffix = fedora.readflag("z") +local verbose = fedora.hasflag("v") +local all = fedora.hasflag("a") +fedora.warning([[ +%%forgesetup is deprecated and will be removed. Adjust your variable names and +replace %%forgesetup with %%auto_prep.]]) +if not (suffix or all) then + suffix = "0" +end +forge.floop(forge.prep, suffix, {verbose}) +} + +# Calls %autosetup using %forgemeta results +# – this is unsafe in presence of multiple sources +# Control variables, flags and arguments: +# -z process the zth block of definitions +# -v -N -S -p relayed to %autosetup +%forgeautosetup(z:vNS:p:q) %{lua: +local fedora = require "fedora.common" +fedora.warning([[ +%%forgeautosetup is deprecated, unsafe, and will be removed. Adjust your +variable names replace %%forgeautosetup with %%auto_prep]]) +print(rpm.expand([[ +%autosetup %{-v} %{-N} %{?-S} %{?-p} %{?forge_setup_arguments]] .. rpm.expand("%{-z*}") .. [[} +]])) +} diff --git a/macros.forge_deprecated-srpm b/macros.forge_deprecated-srpm new file mode 100644 index 0000000..bdc3b70 --- /dev/null +++ b/macros.forge_deprecated-srpm @@ -0,0 +1,24 @@ +# Legacy deprecated compatibility forge logic; SRPM stage +# remove in F35 + +%forgemeta(z:isva) %{lua: +local fedora = require "fedora.common" +local forge = require "fedora.srpm.forge" +local deprecated = require "fedora.srpm.forge_deprecated" +local suffix = fedora.readflag("z") +local informative = fedora.hasflag("i") +local silent = fedora.hasflag("s") +local verbose = fedora.hasflag("v") +local all = fedora.hasflag("a") +fedora.warning([[ +%%forgemeta is deprecated and will be removed. Adjust your variable names and +replace %%forgemeta with %%auto_init.]]) +deprecated.floop(deprecated.init, suffix, {verbose}) +if not (suffix or all) then + forge.floop(forge.init, "0", {verbose, informative, silent}) +else + forge.floop(forge.init, suffix, {verbose, informative, silent}) +end +forge.info_generic(informative) +forge.floop(deprecated.post_init, suffix, {verbose}) +} diff --git a/redhat-rpm-config.spec b/redhat-rpm-config.spec index 7d1453c..31779de 100644 --- a/redhat-rpm-config.spec +++ b/redhat-rpm-config.spec @@ -26,22 +26,32 @@ Source60: redhat-annobin-cc1 # The macros defined by these files are for things that need to be defined # at srpm creation time when it is not feasible to require the base packages -# that would otherwise be providing the macros. other language/arch specific +# that would otherwise be providing the macros, and when %prep has not been +# executed and source content is unknown. Other language/arch specific # macros should not be defined here but instead in the base packages that can # be pulled in at rpm build time, this is specific for srpm creation. -Source100: macros.fedora-misc-srpm Source102: macros.mono-srpm Source103: macros.nodejs-srpm Source104: macros.ldc-srpm Source105: macros.valgrind-srpm - -# Other misc macros +Source106: macros.fedora-misc-srpm +Source107: macros.forge-srpm +Source108: macros.auto-srpm +Source109: macros.forge_deprecated-srpm +Source110: macros.doc-srpm +Source112: macros.buildsys-srpm + +# Other misc macros, for use in %prep and afterwards Source150: macros.dwz Source151: macros.kmp Source152: macros.vpath -Source153: macros.forge Source154: macros.ldconfig -Source155: macros.fedora-misc +Source156: macros.fedora-misc-rpm +Source157: macros.forge-rpm +Source158: macros.auto-rpm +Source159: macros.forge_deprecated-rpm +Source160: macros.doc-rpm +Source161: macros.doc-rpm.internal # Build policy scripts # this comes from https://github.com/rpm-software-management/rpm/pull/344 @@ -86,7 +96,19 @@ Source701: brp-strip-lto # Convenience lua functions Source800: common.lua -Source801: forge.lua +Source801: auto.lua +Source802: forge-srpm.lua +Source803: forge-rpm.lua +Source804: forge_deprecated-srpm.lua +Source805: doc-srpm.lua +Source806: doc-rpm.lua +Source807: buildsys-srpm.lua + +# Automated macro registration +Source850: forge-srpm.auto +Source851: forge-rpm.auto +Source852: doc-srpm.auto +Source853: doc-rpm.auto # Documentation Source900: buildflags.md @@ -146,6 +168,9 @@ Macros and scripts for building kernel module packages. # of source numbers in install section %setup -c -T cp -p %{sources} . +# Auto package macros registry location +%global _rpmautodir %{_rpmconfigdir}/auto +%writevars -f macros.auto-srpm _rpmautodir %install mkdir -p %{buildroot}%{rrcdir} @@ -170,8 +195,22 @@ mkdir -p %{buildroot}%{_fileattrsdir} install -p -m 644 -t %{buildroot}%{_fileattrsdir} *.attr mkdir -p %{buildroot}%{_rpmluadir}/fedora/{rpm,srpm} -install -p -m 644 -t %{buildroot}%{_rpmluadir}/fedora common.lua -install -p -m 644 -t %{buildroot}%{_rpmluadir}/fedora/srpm forge.lua +install -p -m 644 -t %{buildroot}%{_rpmluadir}/fedora common.lua auto.lua +install -p -m 644 forge-srpm.lua \ + %{buildroot}%{_rpmluadir}/fedora/srpm/forge.lua +install -p -m 644 forge_deprecated-srpm.lua \ + %{buildroot}%{_rpmluadir}/fedora/srpm/forge_deprecated.lua +install -p -m 644 forge-rpm.lua \ + %{buildroot}%{_rpmluadir}/fedora/rpm/forge.lua +install -p -m 644 doc-srpm.lua \ + %{buildroot}%{_rpmluadir}/fedora/srpm/doc.lua +install -p -m 644 doc-rpm.lua \ + %{buildroot}%{_rpmluadir}/fedora/rpm/doc.lua +install -p -m 644 buildsys-srpm.lua \ + %{buildroot}%{_rpmluadir}/fedora/srpm/buildsys.lua + +mkdir -p %{buildroot}%{_rpmautodir} +install -p -m 644 -t %{buildroot}%{_rpmautodir} *auto %files %dir %{rrcdir} @@ -188,16 +227,19 @@ install -p -m 644 -t %{buildroot}%{_rpmluadir}/fedora/srpm forge.lua %{rrcdir}/brp-ldconfig %{_fileattrsdir}/*.attr %{_rpmconfigdir}/macros.d/macros.*-srpm +%{_rpmconfigdir}/macros.d/macros.*-rpm +%{_rpmconfigdir}/macros.d/macros.*-rpm.internal %{_rpmconfigdir}/macros.d/macros.dwz -%{_rpmconfigdir}/macros.d/macros.forge %{_rpmconfigdir}/macros.d/macros.ldconfig %{_rpmconfigdir}/macros.d/macros.vpath -%{_rpmconfigdir}/macros.d/macros.fedora-misc %dir %{_rpmluadir}/fedora %dir %{_rpmluadir}/fedora/srpm %dir %{_rpmluadir}/fedora/rpm -%{_rpmluadir}/fedora/*.lua +%{_rpmluadir}/fedora/*lua %{_rpmluadir}/fedora/srpm/*lua +%{_rpmluadir}/fedora/rpm/*lua +%dir %{_rpmautodir} +%{_rpmautodir}/*auto %doc buildflags.md From a18401599a28f016ad5da4a7886110b03a4b17a0 Mon Sep 17 00:00:00 2001 From: Nicolas Mailhot Date: Oct 14 2020 15:00:24 +0000 Subject: [PATCH 2/2] Add the possibility to use a detached changelog Make the buildsys subsystem populate the changelog from a detached file. The %auto_changelog macro depends on the presence of %auto_sources in sourcelist (%auto_sources is already used for forge auto patching, for example). ```rpm %sourcelist %auto_sources […] %changelog %auto_changelog ``` --- diff --git a/buildsys-rpm.auto b/buildsys-rpm.auto new file mode 100644 index 0000000..bb8e967 --- /dev/null +++ b/buildsys-rpm.auto @@ -0,0 +1 @@ +auto_changelog buildsys_changelog diff --git a/buildsys-srpm.auto b/buildsys-srpm.auto new file mode 100644 index 0000000..77c7a4f --- /dev/null +++ b/buildsys-srpm.auto @@ -0,0 +1 @@ +auto_sources buildsys_sources diff --git a/buildsys-srpm.lua b/buildsys-srpm.lua index ac1b22c..e35e1d5 100644 --- a/buildsys-srpm.lua +++ b/buildsys-srpm.lua @@ -32,6 +32,16 @@ Name: %{source_name} fedora.mset(myrads["evr"], namespace, '', { fedora.mread(myrads["evr"], sn, '', false), }, verbose, false) + local changelog = '' + local changelog_file = '%{_sourcedir}/%{' .. namespace .. '_changelog_file}' + local f = io.open(fedora.expand(changelog_file), "r") + if f then + for l in f:lines() do + changelog = changelog .. l .. "\n" + end + io.close(f) + end + fedora.set(namespace .. "_changelog_content", changelog, verbose) print(fedora.expand([[ %{?buildsys_epoch:Epoch: %{buildsys_epoch}} Version: %{buildsys_version} diff --git a/macros.auto-rpm b/macros.auto-rpm index 3dc9483..e9d8e15 100644 --- a/macros.auto-rpm +++ b/macros.auto-rpm @@ -61,3 +61,13 @@ local auto = require "fedora.auto" local verbose = fedora.hasflag("v") auto.exec("auto_files", verbose) } + +# Executes automated packaging macros that populate the changelog +# Control variables, flags and arguments: +# -v be verbose +%auto_changelog(v) %{lua: +local fedora = require "fedora.common" +local auto = require "fedora.auto" +local verbose = fedora.hasflag("v") +auto.exec("auto_changelog", verbose) +} diff --git a/macros.buildsys-rpm b/macros.buildsys-rpm new file mode 100644 index 0000000..b6ea190 --- /dev/null +++ b/macros.buildsys-rpm @@ -0,0 +1,6 @@ +# buildsys macros, safe to use at RPM build stage + +# Adds buildsys changelog to spec +%buildsys_changelog(v) %{expand: +%{buildsys_changelog_content} +} diff --git a/macros.buildsys-srpm b/macros.buildsys-srpm index 93d916b..ac31506 100644 --- a/macros.buildsys-srpm +++ b/macros.buildsys-srpm @@ -1,5 +1,8 @@ # buildsys macros, safe to use at SRPM build stage +# State files required in package sources +%buildsys_changelog_file rpm-changelog.txt + # A single Name: and %package substitute # Control variables, flags and arguments: # %{source_name} the SRPM name @@ -44,3 +47,17 @@ local buildsys = require "fedora.srpm.buildsys" local verbose = fedora.hasflag("v") buildsys.pkg(verbose) } + +# Prints sources in %sourcelist compatible format +%buildsys_sources(v) %{lua: + for _, r in ipairs({'changelog_file'}) do + local f = '%{buildsys_' .. r .. '}' + local p = '%{_sourcedir}/' .. f + print(rpm.expand([=[ +%([[ ! -e ']=] .. p .. [=[' ]] && echo " " > ]=] .. p .. [=[) +]=] .. f .. [=[ +]=])) + end +} + +%buildsys_sources_last true diff --git a/redhat-rpm-config.spec b/redhat-rpm-config.spec index 31779de..ad1f073 100644 --- a/redhat-rpm-config.spec +++ b/redhat-rpm-config.spec @@ -52,6 +52,7 @@ Source158: macros.auto-rpm Source159: macros.forge_deprecated-rpm Source160: macros.doc-rpm Source161: macros.doc-rpm.internal +Source162: macros.buildsys-rpm # Build policy scripts # this comes from https://github.com/rpm-software-management/rpm/pull/344 @@ -109,6 +110,8 @@ Source850: forge-srpm.auto Source851: forge-rpm.auto Source852: doc-srpm.auto Source853: doc-rpm.auto +Source854: buildsys-srpm.auto +Source855: buildsys-rpm.auto # Documentation Source900: buildflags.md