commit 50099d49212c21a0af866e589068be993938b7a0
parent 891dba51dde9e49716038ffc54a44e7ec6620f64
Author: Florian Dold <dold@taler.net>
Date: Fri, 4 Sep 2026 21:26:11 +0200
libeufin: replace build-common with local build scripts
Diffstat:
7 files changed, 227 insertions(+), 22 deletions(-)
diff --git a/.gitmodules b/.gitmodules
@@ -1,6 +1,3 @@
-[submodule "build-system/taler-build-scripts"]
- path = build-system/taler-build-scripts
- url = ../build-common.git
[submodule "doc/prebuilt"]
path = doc/prebuilt
url = ../taler-docs.git
diff --git a/Makefile b/Makefile
@@ -1,12 +1,12 @@
# This Makefile has been placed under the public domain
--include build-system/config.mk
+-include .config.mk
# Default target, must be at the top.
# Should be changed with care to not break (Debian) packaging.
all: build
-git-archive-all = ./build-system/taler-build-scripts/archive-with-submodules/git_archive_all.py
+archive = ./build-system/archive.py
git_tag=$(shell git describe --tags)
gradle_version=$(shell ./gradlew -q libeufinVersion)
@@ -35,7 +35,7 @@ build:
dist:
$(call versions_check)
mkdir -p build/distributions
- $(git-archive-all) --include ./configure build/distributions/libeufin-$(gradle_version)-sources.tar.gz
+ $(archive) --include ./configure build/distributions/libeufin-$(gradle_version)-sources.tar.gz
.PHONY: deb
deb:
diff --git a/bootstrap b/bootstrap
@@ -10,11 +10,6 @@ if ! git --version >/dev/null; then
exit 1
fi
-if ! python3 --version >/dev/null; then
- echo "python3 not installed"
- exit 1
-fi
-
# Make sure that "git pull" et al. also update
# submodules to avoid accidental rollbacks.
git config --local submodule.recurse true
@@ -22,4 +17,4 @@ git config --local submodule.recurse true
git submodule sync
git submodule update --init
rm -f ./configure
-cp build-system/taler-build-scripts/configure ./configure
+cp build-system/configure.sh ./configure
diff --git a/build-system/archive.py b/build-system/archive.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python3
+
+# This file has been placed in the public domain.
+
+"""Create a source archive containing this repository and its submodules."""
+
+from __future__ import annotations
+
+import argparse
+import os
+from pathlib import Path
+import subprocess
+import sys
+import tarfile
+
+
+def run_git(repo: Path, *args: str, input_data: bytes | None = None) -> bytes:
+ try:
+ return subprocess.run(
+ ["git", *args],
+ cwd=repo,
+ input=input_data,
+ check=True,
+ stdout=subprocess.PIPE,
+ ).stdout
+ except subprocess.CalledProcessError as exc:
+ command = " ".join(("git", *args))
+ raise RuntimeError(f"'{command}' failed in {repo}") from exc
+
+
+def tracked_paths(repo: Path) -> list[Path]:
+ output = run_git(repo, "ls-files", "-z", "--cached")
+ return [
+ Path(os.fsdecode(item)) for item in output.rstrip(b"\0").split(b"\0") if item
+ ]
+
+
+def submodule_paths(repo: Path) -> set[Path]:
+ gitmodules = repo / ".gitmodules"
+ if not gitmodules.exists():
+ return set()
+ result = subprocess.run(
+ [
+ "git",
+ "config",
+ "-f",
+ ".gitmodules",
+ "--get-regexp",
+ r"^submodule\..*\.path$",
+ ],
+ cwd=repo,
+ check=False,
+ stdout=subprocess.PIPE,
+ text=True,
+ )
+ if result.returncode not in (0, 1):
+ raise RuntimeError(f"could not read submodules from {gitmodules}")
+ return {Path(line.split(maxsplit=1)[1]) for line in result.stdout.splitlines()}
+
+
+def export_ignored(repo: Path, paths: list[Path]) -> set[Path]:
+ if not paths:
+ return set()
+ query = b"\0".join(os.fsencode(path) for path in paths) + b"\0"
+ output = run_git(
+ repo, "check-attr", "-z", "--stdin", "export-ignore", input_data=query
+ )
+ fields = output.rstrip(b"\0").split(b"\0")
+ ignored: set[Path] = set()
+ for index in range(0, len(fields), 3):
+ if fields[index + 2] == b"set":
+ ignored.add(Path(os.fsdecode(fields[index])))
+ return ignored
+
+
+def repository_entries(repo: Path) -> list[tuple[Path, Path]]:
+ """Return (filesystem path, archive-relative path) pairs for one repository."""
+ submodules = submodule_paths(repo)
+ candidates: list[tuple[Path, Path]] = []
+
+ for relative in tracked_paths(repo):
+ if relative not in submodules:
+ candidates.append((repo / relative, relative))
+
+ for submodule in sorted(submodules):
+ submodule_dir = repo / submodule
+ if not (submodule_dir / ".git").exists():
+ raise RuntimeError(
+ f"submodule '{submodule}' is not initialized; run './bootstrap' first"
+ )
+ for source, relative in repository_entries(submodule_dir):
+ candidates.append((source, submodule / relative))
+
+ ignored = export_ignored(repo, [relative for _, relative in candidates])
+ return [
+ (source, relative) for source, relative in candidates if relative not in ignored
+ ]
+
+
+def archive_prefix(output: Path) -> Path:
+ name = output.name
+ for suffix in (".tar.gz", ".tgz"):
+ if name.endswith(suffix):
+ prefix = name[: -len(suffix)]
+ if prefix:
+ return Path(prefix)
+ break
+ raise ValueError("output file must end in .tar.gz or .tgz")
+
+
+def parse_args() -> argparse.Namespace:
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument(
+ "--include",
+ action="append",
+ default=[],
+ metavar="FILE",
+ help="include an untracked or export-ignored file (may be repeated)",
+ )
+ parser.add_argument("output", type=Path, help="output .tar.gz file")
+ return parser.parse_args()
+
+
+def main() -> int:
+ args = parse_args()
+ root = Path(run_git(Path.cwd(), "rev-parse", "--show-toplevel").decode().strip())
+ output = args.output.resolve()
+ prefix = archive_prefix(output)
+ entries = repository_entries(root)
+
+ included: list[tuple[Path, Path]] = []
+ for name in args.include:
+ relative = Path(name)
+ if relative.is_absolute() or ".." in relative.parts:
+ raise ValueError(
+ f"included path must be relative to the repository: {name}"
+ )
+ source = (root / relative).absolute()
+ if not source.is_file():
+ raise FileNotFoundError(f"included file does not exist: {name}")
+ included.append((source, relative))
+
+ with tarfile.open(output, "w:gz") as archive:
+ for source, relative in [*included, *entries]:
+ archive.add(source, arcname=prefix / relative, recursive=False)
+
+ print(f"created {output}")
+ return 0
+
+
+if __name__ == "__main__":
+ try:
+ sys.exit(main())
+ except (OSError, RuntimeError, ValueError) as exc:
+ print(f"archive: {exc}", file=sys.stderr)
+ sys.exit(1)
diff --git a/build-system/configure.py b/build-system/configure.py
@@ -1,9 +0,0 @@
-# This configure.py.template file is in the public domain.
-
-from talerbuildconfig import *
-
-b = BuildConfig()
-b.enable_prefix()
-b.enable_configmk()
-b.add_tool(PosixTool("find"))
-b.run()
diff --git a/build-system/configure.sh b/build-system/configure.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+# This file has been placed in the public domain.
+
+set -eu
+
+prefix=/usr/local
+
+usage() {
+ cat <<EOF
+Usage: ./configure [--prefix=DIR] [VARIABLE=VALUE]...
+
+Configure the installation prefix (default: /usr/local).
+Unsupported build variables are ignored with a warning.
+EOF
+}
+
+while test $# -gt 0; do
+ case $1 in
+ --prefix=*)
+ prefix=${1#*=}
+ ;;
+ --prefix)
+ if test $# -lt 2; then
+ echo "configure: option '--prefix' requires an argument" >&2
+ exit 2
+ fi
+ prefix=$2
+ shift
+ ;;
+ -h|--help)
+ usage
+ exit 0
+ ;;
+ *=*)
+ variable=${1%%=*}
+ case $variable in
+ ""|[!A-Za-z_]*|*[!A-Za-z0-9_]*)
+ echo "configure: unrecognized option '$1'" >&2
+ echo "Try './configure --help' for more information." >&2
+ exit 2
+ ;;
+ *)
+ echo "configure: WARNING: unsupported variable '$variable' is ignored" >&2
+ ;;
+ esac
+ ;;
+ *)
+ echo "configure: unrecognized option '$1'" >&2
+ echo "Try './configure --help' for more information." >&2
+ exit 2
+ ;;
+ esac
+ shift
+done
+
+if test -z "$prefix"; then
+ echo "configure: installation prefix must not be empty" >&2
+ exit 2
+fi
+
+cat >.config.mk <<EOF
+# This makefile fragment is generated by configure.
+prefix = $prefix
+EOF
+
+echo "configured installation prefix: $prefix"
diff --git a/build-system/taler-build-scripts b/build-system/taler-build-scripts
@@ -1 +0,0 @@
-Subproject commit 884e13fe65b584f63d4cf92348fab1136af4bd69