#!/usr/bin/env python3
"""Generate Cabal version macros.
Generates the content of a C header file for the given library name and version
and prints it to standard output.
"""
import argparse
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("name", help="The package name.")
parser.add_argument("version", help="The package version.")
args = parser.parse_args()
print(version_macros(args.name, args.version))
def version_macros(name, version):
"""Generate Cabal version macros.
Based on Cabal's version macro generation, see [1].
[1]: http://hackage.haskell.org/package/Cabal-2.4.1.0/docs/src/Distribution.Simple.Build.Macros.html#generatePackageVersionMacros
"""
(major1, major2, minor) = version_components(version)
escaped_name = cpp_escape_name(name)
return "\n".join([
# #define VERSION_pkg "1.2.3"
cpp_ifndef_define(
"VERSION_" + escaped_name,
[],
'"{}"'.format(version),
),
# #define MIN_VERSION_pkg(major1, major2, minor) ...
cpp_ifndef_define(
"MIN_VERSION_" + escaped_name,
["major1", "major2", "minor"],
" \\\n".join([
"(",
" (major1) < {} ||".format(major1),
" (major1) == {} && (major2) < {} ||".format(major1, major2),
" (major1) == {} && (major2) == {} && (minor) <= {} )".format(
major1, major2, minor),
])),
])
def version_components(version):
"""Split version string into major1.major2.minor components."""
components = version.split(".")
num = len(components)
if num < 1:
raise ValueError("version should have at least one component.")
major1 = components[0]
if num >= 2:
major2 = components[1]
else:
major2 = "0"
if num >= 3:
minor = components[2]
else:
minor = "0"
return (major1, major2, minor)
def cpp_escape_name(name):
"""Escape package name to be CPP macro safe."""
return name.replace("-", "_")
def cpp_define(macro, params, val):
"""CPP macro definition, optionally with parameters."""
return "#define {macro}{params} {val}".format(
macro = macro,
params = "({})".format(",".join(params)) if params else "",
val = val,
)
def cpp_ifndef(macro, body):
"""CPP ifndef block."""
return "#ifndef {macro}\n{body}\n#endif /* {macro} */".format(
macro = macro,
body = body,
)
def cpp_ifndef_define(macro, params, val):
"""CPP macro definition, if not previously defined."""
return cpp_ifndef(macro, cpp_define(macro, params, val))
if __name__ == "__main__":
main()