summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--README15
-rwxr-xr-xbootstrap.template14
-rw-r--r--configure.py226
-rwxr-xr-xconfigure.py.template20
-rw-r--r--talerbuildconfig.py275
-rw-r--r--testconfigure.py13
7 files changed, 337 insertions, 227 deletions
diff --git a/.gitignore b/.gitignore
index aee2e4c..ed077ae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
config.mk
+__pycache__
diff --git a/README b/README
index fce232a..2b2e5a5 100644
--- a/README
+++ b/README
@@ -1 +1,14 @@
-shared build-system files for (some) parts of Taler.
+Shared build-system files for (some) parts of Taler.
+
+A repository using these build-system files should be structured as follows:
+
+<my-repository.git>
+- bootstrap (copied/adjusted from bootstrap.template)
+- build-system (directory containing build system "stuff")
+--| configure.py (copied/adjusted from bootstrap.template)
+--| taler-build-scripts (git submodule of taler-build-scripts)
+--| Makefile
+
+Makefile and configure.py can also be placed directly into the root of the
+repository. However, this might lead to errors when "make" can be invoked
+before bootstrap and configure has been done.
diff --git a/bootstrap.template b/bootstrap.template
new file mode 100755
index 0000000..82700e1
--- /dev/null
+++ b/bootstrap.template
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# Bootstrap the repository. Used when the repository is checked out from git.
+# When using the source tarball, running this script is not necessary.
+
+set -eu
+
+if ! git --version >/dev/null; then
+ echo "git not installed"
+ exit 1
+fi
+
+git submodule update --init
+cp build-system/taler-build-scripts/configure ./configure
diff --git a/configure.py b/configure.py
deleted file mode 100644
index 4406560..0000000
--- a/configure.py
+++ /dev/null
@@ -1,226 +0,0 @@
-# This file is part of TALER
-# (C) 2019 GNUnet e.V.
-#
-# Authors:
-# Author: ng0 <ng0@taler.net>
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE
-# LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
-# OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
-# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
-# ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-# THIS SOFTWARE.
-#
-# SPDX-License-Identifier: 0BSD
-
-import argparse
-import os
-import sys
-import logging
-from distutils.spawn import find_executable
-import subprocess
-
-"""
-This application aims to replicate a small GNU Coding Standards
-configure script, taylored at projects in GNU Taler. We hope it
-can be of use outside of GNU Taler, hence it is dedicated to the
-public domain ('0BSD').
-It takes a couple of arguments on the commandline equivalent to
-configure by autotools, in addition some environment variables
-xan take precedence over the switches. In the absence of switches,
-/usr/local is assumed as the PREFIX.
-When all data from tests are gathered, it generates a config.mk
-Makefile fragement, which is the processed by a Makefile (usually) in
-GNU Make format.
-"""
-
-
-def existence(name):
- return find_executable(name) is not None
-
-
-def tool_version(name):
- return subprocess.getstatusoutput(name)[1]
-
-
-def tool_emscripten():
- if existence('emcc'):
- emscripten_version = tool_version('emcc --version')
- return f"emscripten version {emscripten_version} found"
- else:
- return f"emscripten compiler not found"
-
-
-def tool_pybabel():
- # No suffix. Would probably be cheaper to do this in
- # the dict as well.
- if existence('pybabel'):
- return 'pybabel'
- else:
- # Has suffix, try suffix. We know the names in advance,
- # so use a dictionary and iterate over it. Use enough names
- # to safe updating this for another couple of years.
- #
- # Food for thought: If we only accept python 3.7 or higher,
- # is checking pybabel + pybabel-3.[0-9]* too much and could
- # be broken down to pybabel + pybabel-3.7 and later names?
- version_dict = {
- '3.0': 'pybabel-3.0',
- '3.1': 'pybabel-3.1',
- '3.2': 'pybabel-3.2',
- '3.3': 'pybabel-3.3',
- '3.4': 'pybabel-3.4',
- '3.5': 'pybabel-3.5',
- '3.6': 'pybabel-3.6',
- '3.7': 'pybabel-3.7',
- '3.8': 'pybabel-3.8',
- '3.9': 'pybabel-3.9',
- '4.0': 'pybabel-4.0',
- }
- for value in version_dict.values():
- if existence(value):
- return value
-
-
-def tool_browser():
- # TODO: read xdg-open value first.
- browser_dict = {
- 'ice': 'icecat',
- 'ff': 'firefox',
- 'chg': 'chrome',
- 'ch': 'chromium',
- 'o': 'opera'
- }
- if 'BROWSER' in os.environ:
- return os.environ.get('BROWSER')
- else:
- for value in browser_dict.values():
- if existence(value):
- return value
-
-
-def tool_node():
- if existence('node') is None:
- sys.exit(
- 'Error: node executable not found.\nIf you are using Ubuntu Linux or Debian Linux, try installing the\nnode-legacy package or symlink node to nodejs.'
- )
- else:
- if subprocess.getstatusoutput(
- "node -p 'process.exit(!(/v([0-9]+)/.exec(process.version)[1] >= 4))'"
- )[1] is not '':
- sys.exit('Your node version is too old, use Node 4.x or newer')
- else:
- node_version = tool_version("node --version")
- return f"Using Node version {node_version}"
-
-
-def tool_yarn():
- if existence('yarn'):
- p1 = subprocess.run(['yarn', 'help'],
- stderr=subprocess.STDOUT,
- stdout=subprocess.PIPE)
- if 'No such file or directory' in p1.stdout.decode('utf-8'):
- if existence('cmdtest'):
- print(
- 'WARNING: cmdtest is installed, this can lead\nto know issues with yarn.'
- )
- sys.exit(
- 'ERROR: You seem to have the wrong kind of "yarn" installed, please remove the\nconflicting binary before continuing!'
- )
- return 'yarn'
- elif existence('yarnpkg'):
- return 'yarnpkg'
- else:
- sys.exit(
- 'ERROR: yarn missing. See https://yarnpkg.com/en/docs/install\n'
- )
-
-
-def tool_posix():
- messages = []
-
- tool_find = existence('find')
- if tool_find is None:
- messages.append('prerequisite find(1) not found.')
-
- tool_xargs = existence('xargs')
- if tool_xargs is None:
- messages.append('prerequisite xargs(1) not found.')
-
- tool_msgmerge = existence('msgmerge')
- if tool_msgmerge is None:
- messages.append('prerequisite msgmerge(1) not found.')
-
- return messages
-
-
-def main():
- logging.basicConfig(level=logging.DEBUG)
- logger = logging.getLogger(__name__)
-
- parser = argparse.ArgumentParser()
- parser.add_argument(
- '-p',
- '--prefix',
- type=str,
- default='/usr/local',
- help='Directory prefix for installation'
- )
- parser.add_argument(
- '-yarn', '--with-yarn', type=str, help='name of yarn executable'
- )
- parser.add_argument(
- '-browser',
- '--with-browser',
- type=str,
- help='name of your webbrowser executable'
- )
- parser.add_argument(
- '-pybabel',
- '--with-pybabel',
- type=str,
- help='name of your pybabel executable'
- )
- args = parser.parse_args()
- if 'DEBUG' in os.environ:
- logger.debug('%s', args)
-
- # get PREFIX
- if 'PREFIX' in os.environ:
- p_myprefix = os.environ.get('PREFIX')
- if p_myprefix is not None and os.path.isdir(p_myprefix) is True:
- myprefix = p_myprefix
- elif args.prefix is not '/usr/local':
- myprefix = args.prefix
- else:
- myprefix = parser.get_default('prefix')
-
- # get yarn executable
- if args.with_yarn is not None:
- yarnexe = args.with_yarn
- else:
- yarnexe = str(tool_yarn())
-
- mybrowser = tool_browser()
- mypybabel = tool_pybabel()
- f = open('config.mk', 'w+')
- f.writelines([
- '# This mk fragment is autogenerated by configure.py\n',
- f'prefix={myprefix}\n', f'yarnexe={yarnexe}\n',
- f'RUN_BROWSER={mybrowser}\n', f'pybabel={mypybabel}\n'
- ])
- f.close()
- print(tool_node())
- print(tool_emscripten())
- posixlist = tool_posix()
- for msg in posixlist:
- print(posixlist[msg])
-
-
-if __name__ == "__main__":
- main()
diff --git a/configure.py.template b/configure.py.template
new file mode 100755
index 0000000..f55fec1
--- /dev/null
+++ b/configure.py.template
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+
+import sys
+from pathlib import Path
+
+base_dir = Path(__file__, "../build-system/taler-build-scripts").resolve()
+if not base_dir.exists():
+ print(
+ f"build system directory ({base_dir}) missing", file=sys.stderr
+ )
+ sys.exit(1)
+sys.path.insert(0, base_dir)
+
+from talerbuildconfig import *
+
+b = BuildConfig()
+b.enable_prefix()
+b.enable_configmk()
+b.add_tool(PosixTool("find"))
+b.run()
diff --git a/talerbuildconfig.py b/talerbuildconfig.py
new file mode 100644
index 0000000..95f0fa2
--- /dev/null
+++ b/talerbuildconfig.py
@@ -0,0 +1,275 @@
+# This file is part of TALER
+# (C) 2019 GNUnet e.V.
+#
+# Authors:
+# Author: ng0 <ng0@taler.net>
+# Author: Florian Dold <dold@taler.net>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE
+# LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
+# OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+# ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+# THIS SOFTWARE.
+#
+# SPDX-License-Identifier: 0BSD
+
+from abc import ABC
+import argparse
+import os
+import sys
+import logging
+from distutils.spawn import find_executable
+import subprocess
+from dataclasses import dataclass
+
+"""
+This module aims to replicate a small GNU Coding Standards
+configure script, taylored at projects in GNU Taler. We hope it
+can be of use outside of GNU Taler, hence it is dedicated to the
+public domain ('0BSD').
+It takes a couple of arguments on the commandline equivalent to
+configure by autotools, in addition some environment variables
+xan take precedence over the switches. In the absence of switches,
+/usr/local is assumed as the PREFIX.
+When all data from tests are gathered, it generates a config.mk
+Makefile fragement, which is the processed by a Makefile (usually) in
+GNU Make format.
+"""
+
+
+class Tool(ABC):
+ def args(self):
+ ...
+
+ def check(self, buildconfig):
+ ...
+
+
+class BuildConfig:
+ def __init__(self):
+ # Pairs of (key, value) for config.mk variables
+ self.make_variables = []
+ self.tools = []
+ self.tool_results = {}
+ self.args = None
+ self.prefix_enabled = False
+ self.configmk_enabled = False
+
+ def add_tool(self, tool):
+ if isinstance(tool, Tool):
+ self.tools.append(tool)
+ else:
+ raise Exception("Not a tool instance: " + repr(tool))
+
+ def _set_tool(self, name, value, version=None):
+ self.tool_results[name] = (value, version)
+
+ def enable_prefix(self):
+ """If enabled, process the --prefix argument."""
+ self.prefix_enabled = True
+
+ def enable_configmk(self):
+ """If enabled, output the config.mk makefile fragment."""
+ self.configmk_enabled = True
+
+ def run(self):
+ parser = argparse.ArgumentParser()
+ if self.prefix_enabled:
+ parser.add_argument(
+ "--prefix",
+ type=str,
+ default="/usr/local",
+ help="Directory prefix for installation",
+ )
+ for tool in self.tools:
+ tool.args(parser)
+ args = self.args = parser.parse_args()
+
+ for tool in self.tools:
+ res = tool.check(self)
+ if not res:
+ print(f"Error: tool {tool.name} not available")
+ if hasattr(tool, "hint"):
+ print(f"Hint: {tool.hint}")
+
+ for tool in self.tools:
+ path, version = self.tool_results[tool.name]
+ if version is None:
+ print(f"found {tool.name} as {path}")
+ else:
+ print(f"found {tool.name} as {path} (version {version})")
+
+ if self.configmk_enabled:
+ with open("config.mk", "w") as f:
+ f.write("# this makefile fragment is autogenerated by configure.py\n")
+ if self.prefix_enabled:
+ f.write(f"prefix = {args.prefix}\n")
+ for tool in self.tools:
+ path, version = self.tool_results[tool.name]
+ f.write(f"{tool.name} = {path}\n")
+
+
+def existence(name):
+ return find_executable(name) is not None
+
+
+class YarnTool(Tool):
+ name = "yarn"
+ description = "The yarn package manager for node"
+
+ def args(self, parser):
+ parser.add_argument("--with-yarn", action="store")
+
+ def check(self, buildconfig):
+ yarn_arg = buildconfig.args.with_yarn
+ if yarn_arg is not None:
+ buildconfig._set_tool("yarn", yarn_arg)
+ return True
+ if existence("yarn"):
+ p1 = subprocess.run(
+ ["yarn", "help"], stderr=subprocess.STDOUT, stdout=subprocess.PIPE
+ )
+ if "No such file or directory" in p1.stdout.decode("utf-8"):
+ if existence("cmdtest"):
+ buildconfig._warn(
+ "cmdtest is installed, this can lead to known issues with yarn."
+ )
+ buildconfig._error(
+ "You seem to have the wrong kind of 'yarn' installed.\n"
+ "Please remove the conflicting binary before proceeding"
+ )
+ return False
+ yarn_version = tool_version("yarn --version")
+ buildconfig._set_tool("yarn", "yarn", yarn_version)
+ return True
+ elif existence("yarnpkg"):
+ yarn_version = tool_version("yarnpkg --version")
+ buildconfig._set_tool("yarn", "yarnpkg", yarn_version)
+ return True
+ return False
+
+
+def tool_version(name):
+ return subprocess.getstatusoutput(name)[1]
+
+
+class EmscriptenTool:
+ def args(self, parser):
+ pass
+
+ def check(self, buildconfig):
+ if existence("emcc"):
+ emscripten_version = tool_version("emcc --version")
+ buildconfig._set_tool("emcc", "emcc", emscripten_version)
+ return True
+ return False
+
+
+class PyBabelTool(Tool):
+ name = "pybabel"
+
+ def args(self, parser):
+ parser.add_argument(
+ "--with-pybabel", type=str, help="name of the pybabel executable"
+ )
+
+ def check(self, buildconfig):
+ # No suffix. Would probably be cheaper to do this in
+ # the dict as well.
+ if existence("pybabel"):
+ buildconfig._set_tool("pybabel", "pybabel")
+ return True
+ else:
+ # Has suffix, try suffix. We know the names in advance,
+ # so use a dictionary and iterate over it. Use enough names
+ # to safe updating this for another couple of years.
+ #
+ # Food for thought: If we only accept python 3.7 or higher,
+ # is checking pybabel + pybabel-3.[0-9]* too much and could
+ # be broken down to pybabel + pybabel-3.7 and later names?
+ version_dict = {
+ "3.0": "pybabel-3.0",
+ "3.1": "pybabel-3.1",
+ "3.2": "pybabel-3.2",
+ "3.3": "pybabel-3.3",
+ "3.4": "pybabel-3.4",
+ "3.5": "pybabel-3.5",
+ "3.6": "pybabel-3.6",
+ "3.7": "pybabel-3.7",
+ "3.8": "pybabel-3.8",
+ "3.9": "pybabel-3.9",
+ "4.0": "pybabel-4.0",
+ }
+ for value in version_dict.values():
+ if existence(value):
+ buildconfig._set_tool("pybabel", value)
+ return True
+
+
+class BrowserTool(Tool):
+ name = "browser"
+
+ def args(self, parser):
+ parser.add_argument(
+ "--with-browser", type=str, help="name of your webbrowser executable"
+ )
+
+ def check(self, buildconfig):
+ browser_dict = {
+ "ice": "icecat",
+ "ff": "firefox",
+ "chg": "chrome",
+ "ch": "chromium",
+ "o": "opera",
+ }
+ if "BROWSER" in os.environ:
+ buildconfig._set_tool("browser", os.environ["BROWSER"])
+ return True
+ for value in browser_dict.values():
+ if existence(value):
+ buildconfig._set_tool("browser", value)
+ return True
+
+
+class NodeJsTool(Tool):
+ name = "node"
+ hint = "If you are using Ubuntu Linux or Debian Linux, try installing the\nnode-legacy package or symlink node to nodejs."
+
+ def args(self, parser):
+ pass
+
+ def check(self, buildconfig):
+ if existence("node") is None:
+ return False
+ if (
+ subprocess.getstatusoutput(
+ "node -p 'process.exit(!(/v([0-9]+)/.exec(process.version)[1] >= 4))'"
+ )[1]
+ is not ""
+ ):
+ buildconfig._warn("your node version is too old, use Node 4.x or newer")
+ return False
+ node_version = tool_version("node --version")
+ buildconfig._set_tool("node", "node", version=node_version)
+ return True
+
+
+class PosixTool(Tool):
+ def __init__(self, name):
+ self.name = name
+
+ def args(self, parser):
+ pass
+
+ def check(self, buildconfig):
+ found = existence("find")
+ if found:
+ buildconfig._set_tool(self.name, self.name)
+ return True
+ return False
diff --git a/testconfigure.py b/testconfigure.py
new file mode 100644
index 0000000..af7e85a
--- /dev/null
+++ b/testconfigure.py
@@ -0,0 +1,13 @@
+from talerbuildconfig import *
+
+b = BuildConfig()
+b.enable_prefix()
+b.enable_configmk()
+b.add_tool(YarnTool())
+b.add_tool(BrowserTool())
+b.add_tool(PyBabelTool())
+b.add_tool(NodeJsTool())
+b.add_tool(PosixTool("find"))
+b.add_tool(PosixTool("xargs"))
+b.add_tool(PosixTool("msgmerge"))
+b.run()