Blob Blame History Raw
# EPEL5-specific macros

%epel 5

# This file is named such that it appears alphabetically after other macro
# files in /etc/rpm.  This allows us to overwrite RHEL-provided macros if
# necessary.  (It turns out that it is necessary; see _font_pkg below.)

# First some miscellaneous macros
%make_build %{__make} %{?_smp_mflags}
%make_install %{__make} install DESTDIR=%{?buildroot}
%bash_completion_dir /usr/share/bash-completion/


# Now the big magic macros

# How this works:
# RPM for whatever reason allows the redifinition of the section specifiers:
# %description, %prep, %build, %install, and even %files.  This gives us a
# convenient place to hang new tags (just before %description is emitted) and
# to add things to the beginning of a section.

# Also, note that lua state is global unless declared local.  So non-local
# variables, as well as functions, defined in one macro are available to
# others.

#
# ================
# Some utility functions
#
# This turns debug output on or off.
# Wanted to make this a toggle, but rpm will re-parse the sec when it sees
# BuildArch:, so this just toggles again.
%epel_macros_trace() %{lua:
    local args = rpm.expand("%*")
    if args == "0" or args == "off" or args == "no" then
        trace = 0
    else
        trace = 1
    end
}%nil

# Initializes global lua state and then undefines itself.
# Should be called before or as part of any macros below this one.
%epel_macros_init() %{lua:
    function db(str, nest)
        if type(trace) ~= "number" or trace == 0 then
            return
        end
        if type(nest) == "number" and nest > 0 then
            io.stderr:write(string.rep("| ", nest))
        end
        io.stderr:write(str .. "\\n")
    end
\
    db("Epel macros loading.")
\
    -- Return true if a particular macro is set to a "true" value, or false if not
    function istrue(macro)
        db("istrue <= " .. macro)
        local val = rpm.expand(macro)
        db(val)
        if val == "0" or val == "off" or val == "no" or val == macro then
            return False
        end
        if val == "1" or val == "on" or val == "yes" then
            return True
        end
        error("Error: macro " .. macro .. " has non-true/false value.")
    end
\
    -- Return true if a particular macro is set to a "true" value, or false if not
    function isfalse(macro)
        db("isfalse <= " .. macro)
        local val = rpm.expand('%' .. macro)
        if val == "" or val == "0" or val == "off" or val == "no" then
            return True
        end
        if val == "1" or val == "on" or val == "yes" then
            return False
        end
        error("Error: macro " .. macro .. " has non-true/false value.")
    end
\
    -- Return the value of a particular macro
    function getval(macro, default)
        db("getval <= " .. macro)
        macro = '%' .. macro
        local val = rpm.expand(macro)
        if val == macro then
            return default
        end
        return val
    end
\
    -- Quiet debugging output
    function db_silent()
        savedtrace = trace
        trace = 0
    end
\
    -- Restore debugging output, but does nothing if it wasn't enabled to begin
    -- with.
    function db_endsilent()
        trace = savedtrace
    end
\
    -- Iterate through a bunch of numbered macros, accumulating any found
    -- values in a table
    function get_numbered_macros(macrobase, max)
        db("get_numbered_macros <= " .. macrobase .. ", " .. max)
\
        local val
        local out = {}
\
        db_silent() -- Doing a load of getvals, so make them quiet
        for i=0,maxcheck do
            val = getval(macrobase .. i)
            if val ~= nil then
                table.insert(out, val)
            end
        end
        db_endsilent()
\
        return out
    end
\
    rpm.define("epel_macros_init %{nil}")
}%nil

# ================

# This injects a BuildRoot: tag, a Group: tag and a %clean section by hooking
# %description. RPM requires that tags like BuildRoot:, Group: or License: be
# located before %description in the spec, so this is the perfect place to add
# them or change their values.  Of course, we don't provide either if the spec
# does.

# This also defines %license to the string "%doc" for use later in the %files
# section.  This can't just be a simple macro because RPM will overwrite it
# when it sees a License: tag.  Hanging it here is quite convenient, but if
# %license is actually used in the spec then this will cause an issue.  Hanging
# it off of %files fixes this but has its own problems involving the use of
# %define instead of %global and some expansions magically turning into endless
# recursion.

# It would be nice if the macro could define itself out of existence
# by defining %description to itself.  Doesn't work, though, so instead the
# function just keeps state.

# What happens if the spec already has a %clean section?  Sadly just defining
# it to exit doesn't work, because this makes it abort the previous scriptlet
# before the commands appended from __spec_*_post get run.

# So instead we redefine %clean to a deeply magical "cat << 'true '" and then
# prepend a call to true at the beginning of %__spec_install_post.  The normal
# place for %clean is just after install.  at some place before %files and
# after but not _immediately after %description, this means that what's in the
# spec's existing %clean section will just be appended to the previous section
# just after a call to exit.  So the old clean section will still be there but
# will have no effect.

# If the clean section appears elsewhere, no harm is done because
# __spec_prep_post and __spec_build_post are just exit 0.  The magic heredoc
# runs off the end of the scriptlet, which causes no error.  If %clean appears
# immediately after %description, the magic cat is just appended there.  Which
# isn't great, but doesn't harm anything.

# Alternately, we could just completely ignore %clean.  RPM doesn't care if it
# isn't there, so removing it probably costs more than it could possibly save.
# It is an interesting exercise, though.

%description %epel_macros_init%{lua:
    db("In %description.")
\
    -- %group is reset changes after each Group: tag.
    -- Unfortuantely it isn't reset when a package tag is seen.
    local group = rpm.expand("%{?group}")
    if type(group) == "nil" or string.len(group) == 0 or group == "Unspecified" then
        -- What we use for Group: isn't important; it just has to be there
        db("No Group tag found; adding one.")
        print("Group: Unspecified\\n")
    else
        db("Group specified: " .. group)
    end
    -- If we don't do this, the %group keeps the value from the last specified
    -- Group: tag, even though we just provided one.  And if we do this, then
    -- RPM will re-assign it.  Which is confusing, but whatever.
    rpm.expand("%undefine group")
\
    -- Don't do anything else twice
    if did_description == 1 then
        db("Already did %description processing")
        print("%description")
        return
    end
    local buildroot = rpm.expand("%{?buildroot}")
    if type(buildroot) == "nil" or string.len(buildroot) == 0 then
        -- Use the recommended form for BuildRoot
        db("No BuildRoot tag found; adding one.")
        buildroot = rpm.expand("%(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)")
        print("BuildRoot: " .. buildroot .. "\\n\\n")
    else
        db("Buildroot already specified: " .. buildroot)
    end
\
    -- Add a %clean section
    db("Adding %clean.")
    print("%clean\\n")
    -- Note that we can't use %buildroot here, because rpm hasn't parsed the
    -- BuildRoot tag yet.
    print("rm -rf " .. buildroot .. "\\n\\n")
\
    -- Make sure any other %clean doesn't get executed.
    -- db("Redefining %clean.")
    rpm.define("clean cat << \'true \'")
\
    -- Redefine __spec_install_post
    db("Redefining %__spec_install_post.")
    local oldvar = rpm.expand("%{__spec_install_post}")
    oldvar = string.gsub(oldvar, "%s+\\n", " \\\\\\n")
    rpm.define("__spec_install_post echo foo\\\\\\ntrue\\\\" .. oldvar)
\
    -- Make %license work in the %files list.
    rpm.define("license %doc")
\
    -- Actually output the %description tag
    print("%description")
\
    -- The next call to this macro should skip all the work
    did_description = 1
}%nil

# ================
# Add the usual buildroot cleaning to the very beginning of %install
%install %{lua:
    db("In %install.")
    print("%install\\n")
    print(rpm.expand("rm -rf %{buildroot}\\n"))
}%nil

# Add a directory for RPM macros that can be used consistently across Fedora and EPEL
%rpmmacrodir %{_sysconfdir}/rpm

# RPM before 4.6.0 does not provide the patches and sources tables to lua code.
# This means that the autosetup macro, if copied over to EL5, won't find any
# patches to apply.  However, it is possible to fill those tables manually by
# iterating over a large number of possible 'SOURCE' tags and seeing if they
# exist.
%el5_sources_limit 100000
%el5_patches_limit 100000

%el5_setup_sources %epel_macros_init%{lua:
    maxcheck = getval("el5_sources_limit")
\
    sources = get_numbered_macros("SOURCE", maxcheck)
\
    for i,v in ipairs(sources) do
        db("Found source: " .. v)
    end
}%nil

%el5_setup_patches() %{lua:
    maxcheck = getval("el5_patches_limit")
\
    patches = get_numbered_macros("PATCH", maxcheck)
\
    for i,v in ipairs(patches) do
        db("Found patch: " .. v)
    end
}%nil

# vim: set filetype=spec: