Blob Blame History Raw
-- 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 <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 fedora.readflag() to process rpm flag arguments safely.
--     — otherwise execute <f> for the specified <index> value only, if
--       <index> is part of <range>
--   – <f> is passed <index> then <otherargs> as arguments.
--   – <pre_range> index values are processed before the rest of <range>.
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 <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 fedora.readflag() to process rpm flag arguments safely.
--     — otherwise execute <f> for the specified <index> value only, if
--       <index> is part of <range>
--   – <f> is passed <index> then <otherargs> as arguments.
--   – <pre_range> index values are processed before the rest of <range>.
local function floop(f, index, range, otherargs)
  pfloop(f, index, {}, range, otherargs)
end

-- Executes the <pkg> function in a loop, in a way compatible with package
-- header generation requirements
--   – the loop is controled by <index> (a string):
--     — if the <index> value given to floop is nil, loop over the whole
--       <range>. Use fedora.readflag() to process rpm flag arguments safely.
--     — otherwise execute <pkg> for the specified <index> value only, if
--       <index> is part of <range>
--   – <pkg> is passed <index> then <otherargs> as arguments.
--   – <name>(<index>) must return the %{name} of the SRPM header that would
--     be generated if <pkg>(<index>, <otherargs>) is called before something
--     else generates the SRPM header.
--   – if %{source_name} is set, <name>(<index>) results will be compared to
--     %{source_name} for all valid <index> in the loop. <index> 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 <name>(<index>) for all <index> values in <range>, 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 <macros>, passing -v as verbosity flag if
-- verbose. Each %<name> macro will be executed after the macros listed in the
-- %{<name>_after} variable (when those are also registered for the call).
-- Absent other constrains, %<name> macros for which %{<name>_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 %<name> macro will be executed after the macros listed in the
-- %{<name>_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
-- <name>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 %<name>
-- macro must have a %<name>_source_names counterpart that returns the list of
-- SRPM names that would be generated (if any), allowing <pkg_exec> 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,
}