| |
@@ -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 <run> 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 @@
|
| |
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 <radical> = <value>, for all <radical>s in <radicals>,
|
| |
+ -- with <value> = reference of <namespace>_<radical><suffix> if
|
| |
+ -- <namespace>_<radical><suffix> is set. Radicals for which
|
| |
+ -- <namespace>_<radical><suffix> 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 <namespace>_<radical><suffix> variable for all <radical>s in
|
| |
+ -- <radicals>, to a <value> taken in the <stack> list of <radical> = <value>
|
| |
+ -- tables. When <radical> exists in multiple tables, only its first <value> is
|
| |
+ -- taken into account. When <radical> does not exist in any table, no
|
| |
+ -- <namespace>_<radical><suffix> is set.
|
| |
+ -- <stack> is usually constructed with multiple mread calls, taking
|
| |
+ -- domain-specific fallback needs into consideration.
|
| |
+ -- If <force> 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<root> to
|
| |
- -- <root><suffix> 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 <namespace>_<radical><suffix> variable for all <radical>s in
|
| |
+ -- <radicals>, to a reference to <from_namespace>_<radical><from_suffix>,
|
| |
+ -- if <from_namespace>_<radical><from_suffix> is set.
|
| |
+ -- inherit_from is recursive: the next <from_suffix> is read in
|
| |
+ -- <from_namespace>_<from_key><from_suffix>
|
| |
+ 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 <namespace>_<radical><suffix> variable for all <radical>s in
|
| |
+ -- <radicals>, to a reference to <namespace>_<radical><from_suffix>,
|
| |
+ -- if <namespace>_<radical><from_suffix> is set.
|
| |
+ -- <from_suffix> is read in <namespace>_<from_key><suffix>
|
| |
+ -- inherit is recursive, and will also process <from_key> 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
|
| |
- -- <rpmvar><suffix>, 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_namespace><radical><to_suffix>
|
| |
+ -- to %{<from_namespace><radical><from_suffix>} if
|
| |
+ -- <from_namespace><radical><from_suffix> 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_<rpmvar>
|
| |
+ -- to <rpmvar><suffix> 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 <namespace>_<radical>
|
| |
+ 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 <rpmvar><suffix> 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
|
| |
- -- <rpmvar><suffix> is set to a non empty value
|
| |
- local function getsuffixes(rpmvar)
|
| |
- suffixes = {}
|
| |
- for suffix in pairs(getsuffixed(rpmvar)) do
|
| |
- table.insert(suffixes,suffix)
|
| |
+ -- <rpmvar><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 <rpmvar><suffix> 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] = <value> table and returns a suffix for which all the
|
| |
+ -- <key><suffix> rpm variables have the corresponding <value> 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 @@
|
| |
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,
|
| |
}
|
| |
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_<call> 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 that takes care of reading the rpm changelog from
a detached file
– 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:
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).