about summary refs log tree commit diff
path: root/third_party/bazel/rules_haskell/haskell/private/version_macros.py
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/bazel/rules_haskell/haskell/private/version_macros.py')
-rwxr-xr-xthird_party/bazel/rules_haskell/haskell/private/version_macros.py101
1 files changed, 101 insertions, 0 deletions
diff --git a/third_party/bazel/rules_haskell/haskell/private/version_macros.py b/third_party/bazel/rules_haskell/haskell/private/version_macros.py
new file mode 100755
index 000000000000..4bc6052cb032
--- /dev/null
+++ b/third_party/bazel/rules_haskell/haskell/private/version_macros.py
@@ -0,0 +1,101 @@
+#!/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()