build-all: add --help and verify args

This commit is contained in:
Andrej730
2026-08-21 15:49:16 +05:00
parent a5b6f83a3d
commit 5b00c8b451
+147 -38
View File
@@ -32,20 +32,7 @@ Example usage:
python build-all.py IfcParse IfcOpenShell-Python python build-all.py IfcParse IfcOpenShell-Python
Available arguments: Run with --help to see available arguments.
``-py-313`` - build for specific Python version
(building for all supported Python version by default).
``-occt-xxx`` - use a specific OCCT version (e.g. ``-occt-7.8.1``) instead of the default
``-wasm`` - compile for wasm
``-without-xxx`` - do not build dependency ``xxx`` (e.g. ``--without-swig``)
``-mac-cross-compile-intel`` - cross compile for Intel Mac on Apple Silicon host
``-shared`` - build shared libraries. By default will build static.
``-ifcopenshell-shared`` - build only IfcOpenShell's own libraries as shared
(dependencies stay static). Redundant if ``-shared`` is also passed.
``-diskcleanup`` - clean up build directories after finishing building dependencies
``-build-examples`` - build IfcOpenShell examples
``-lto`` - enable link-time optimization (adds ``-flto`` to compiler flags)
``-v`` - enable verbose logs
Used environment variables: Used environment variables:
@@ -118,6 +105,9 @@ Used environment variables:
""" """
from __future__ import annotations
import argparse
import glob import glob
import logging import logging
import multiprocessing import multiprocessing
@@ -129,12 +119,13 @@ import subprocess as sp
import sys import sys
import sysconfig import sysconfig
import tarfile import tarfile
import textwrap
import threading import threading
import time import time
from collections.abc import Generator, Sequence from collections.abc import Generator, Sequence
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Literal from typing import Literal, NamedTuple
from urllib.request import urlretrieve from urllib.request import urlretrieve
from typing_extensions import assert_never from typing_extensions import assert_never
@@ -202,10 +193,134 @@ strip = "strip"
xz = "xz" # Used implicitly for `tar -xf *.tar.xz`. xz = "xz" # Used implicitly for `tar -xf *.tar.xz`.
brew = "brew" brew = "brew"
explicit_targets = [s for s in sys.argv[1:] if not s.startswith("-")]
class Args(NamedTuple):
explicit_targets: list[str]
build_examples: bool
diskcleanup: bool
lto: bool
verbose: bool
shared: bool
ifcopenshell_shared: bool
mac_cross_compile_intel: bool
wasm: bool
class DynamicArgs(NamedTuple):
without: set[str]
py_versions: set[str]
occt_version: str | None
@classmethod
def from_unknown_flags(cls, unknown_flags: list[str], arg_parser: argparse.ArgumentParser) -> DynamicArgs:
flags = set(s.lstrip("-") for s in unknown_flags if s.startswith("-"))
without: set[str] = set()
py_versions: set[str] = set()
occt_versions: set[str] = set()
leftover: set[str] = set()
for f in flags:
if f.startswith("without-"):
without.add(f.removeprefix("without-").lower())
elif f.startswith("py-"):
py_versions.add(f.removeprefix("py-"))
elif f.startswith("occt-"):
occt_versions.add(f.removeprefix("occt-"))
else:
leftover.add(f)
if leftover:
arg_parser.error(f"unrecognized arguments: {', '.join('-' + f for f in sorted(leftover))}")
if len(occt_versions) > 1:
arg_parser.error(f"more than one OCCT version provided: {', '.join(sorted(occt_versions))}")
occt_version = next(iter(occt_versions), None)
return cls(without=without, py_versions=py_versions, occt_version=occt_version)
def parse_args() -> tuple[Args, DynamicArgs]:
arg_parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=textwrap.dedent("""\
Additional dynamic -flags (not declared above):
-py-313 build for specific Python version
(building for all supported Python versions by default)
-occt-xxx use a specific OCCT version (e.g. -occt-7.8.1) instead of the default
-without-xxx do not build dependency `xxx` (e.g. --without-swig)"""),
)
arg_parser.add_argument("explicit_targets", nargs="*", help="Targets provided by CLI.")
arg_parser.add_argument(
"--build-examples",
action="store_true",
default=False,
help="Build IfcOpenShell examples.",
)
arg_parser.add_argument(
"-diskcleanup",
"--diskcleanup",
action="store_true",
default=False,
help="Clean up build directories after finishing building dependencies.",
)
arg_parser.add_argument(
"-lto",
"--lto",
action="store_true",
default=False,
help="Enable link-time optimization (adds -flto to compiler flags).",
)
arg_parser.add_argument(
"-v",
"--verbose",
action="store_true",
default=False,
help="Enable verbose logs.",
)
arg_parser.add_argument(
"-shared",
"--shared",
action="store_true",
default=False,
help="Build shared libraries. By default will build static.",
)
arg_parser.add_argument(
"-ifcopenshell-shared",
"--ifcopenshell-shared",
action="store_true",
default=False,
help="Build only IfcOpenShell's own libraries as shared (dependencies stay static). "
"Redundant if -shared is also passed.",
)
arg_parser.add_argument(
"-mac-cross-compile-intel",
"--mac-cross-compile-intel",
action="store_true",
default=False,
help="Cross compile for Intel Mac on Apple Silicon host.",
)
arg_parser.add_argument("-wasm", "--wasm", action="store_true", default=False, help="Compile for wasm.")
namespace, unknown_flags = arg_parser.parse_known_args()
args = Args(
explicit_targets=namespace.explicit_targets,
build_examples=namespace.build_examples,
diskcleanup=namespace.diskcleanup,
lto=namespace.lto,
verbose=namespace.verbose,
shared=namespace.shared,
ifcopenshell_shared=namespace.ifcopenshell_shared,
mac_cross_compile_intel=namespace.mac_cross_compile_intel,
wasm=namespace.wasm,
)
dynamic_args = DynamicArgs.from_unknown_flags(unknown_flags, arg_parser)
return args, dynamic_args
ARGS, DYNAMIC_ARGS = parse_args()
explicit_targets: set[str] = set(ARGS.explicit_targets)
"""Targets provided by CLI.""" """Targets provided by CLI."""
flags = set(s.lstrip("-") for s in sys.argv[1:] if s.startswith("-"))
"""CLI flags."""
# Helper function for coloured printing # Helper function for coloured printing
@@ -224,17 +339,11 @@ def cecho(message, color=NO_COLOR):
logger.info(f"{color}{message}\033[0m") logger.info(f"{color}{message}\033[0m")
# Flags.
BUILD_EXAMPLES = "build-examples" in flags
DISK_CLEANUP = "diskcleanup" in flags
LTO = "lto" in flags
VERBOSE = "v" in flags
APPLE = platform.system() == "Darwin" APPLE = platform.system() == "Darwin"
MAC_CROSS_COMPILE_INTEL = "mac-cross-compile-intel" in flags MAC_CROSS_COMPILE_INTEL = ARGS.mac_cross_compile_intel
assert platform.system() == "Darwin" or not MAC_CROSS_COMPILE_INTEL assert platform.system() == "Darwin" or not MAC_CROSS_COMPILE_INTEL
WASM = "wasm" in flags WASM = ARGS.wasm
"""Build WASM outside pyodide build environment.""" """Build WASM outside pyodide build environment."""
WASM_CMAKE_IS_USING_INIT_VARS = False WASM_CMAKE_IS_USING_INIT_VARS = False
if WASM: if WASM:
@@ -380,12 +489,12 @@ dependency_tree: dict[str, tuple[str, ...]] = {
def gather_dependencies(dep: str) -> Generator[str]: def gather_dependencies(dep: str) -> Generator[str]:
yield dep yield dep
for d in dependency_tree[dep]: for d in dependency_tree[dep]:
if f"without-{d.lower()}" not in flags: if d.lower() not in DYNAMIC_ARGS.without:
for x in gather_dependencies(d): for x in gather_dependencies(d):
yield x yield x
if VERBOSE: if ARGS.verbose:
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
ch.setFormatter(formatter) ch.setFormatter(formatter)
@@ -406,9 +515,9 @@ else:
MAC_CROSS_COMPILE_INTEL_AUTOCONF_HOST_ARGS = [] MAC_CROSS_COMPILE_INTEL_AUTOCONF_HOST_ARGS = []
OFF_ON = ["OFF", "ON"] OFF_ON = ["OFF", "ON"]
BUILD_STATIC = "shared" not in flags BUILD_STATIC = not ARGS.shared
"""Whether dependencies are built static.""" """Whether dependencies are built static."""
IFCOPENSHELL_STATIC = BUILD_STATIC and "ifcopenshell-shared" not in flags IFCOPENSHELL_STATIC = BUILD_STATIC and not ARGS.ifcopenshell_shared
"""Whether IfcOpenShell's own libraries are built static.""" """Whether IfcOpenShell's own libraries are built static."""
ENABLE_FLAG = "--enable-static" if BUILD_STATIC else "--enable-shared" ENABLE_FLAG = "--enable-static" if BUILD_STATIC else "--enable-shared"
DISABLE_FLAG = "--disable-shared" if BUILD_STATIC else "--disable-static" DISABLE_FLAG = "--disable-shared" if BUILD_STATIC else "--disable-static"
@@ -417,18 +526,18 @@ LINK_TYPE_UCFIRST = LINK_TYPE.capitalize()
LIBRARY_EXT = "a" if BUILD_STATIC else "so" LIBRARY_EXT = "a" if BUILD_STATIC else "so"
PIC = "-fPIC" if BUILD_STATIC else "" PIC = "-fPIC" if BUILD_STATIC else ""
if any(f.startswith("py-") for f in flags): if DYNAMIC_ARGS.py_versions:
PYTHON_VERSIONS = [pyv for pyv in PYTHON_VERSIONS if f"py-{''.join(pyv.split('.')[:2])}" in flags] PYTHON_VERSIONS = [pyv for pyv in PYTHON_VERSIONS if "".join(pyv.split(".")[:2]) in DYNAMIC_ARGS.py_versions]
if any(f.startswith("occt-") for f in flags): if DYNAMIC_ARGS.occt_version is not None:
OCCT_VERSION = next(f.split("-", 1)[1] for f in flags if f.startswith("occt-")) OCCT_VERSION = DYNAMIC_ARGS.occt_version
if explicit_targets: if explicit_targets:
targets = {dep for target in explicit_targets for dep in gather_dependencies(target)} targets = {dep for target in explicit_targets for dep in gather_dependencies(target)}
else: else:
targets = set(dependency_tree.keys()) targets = set(dependency_tree.keys())
targets = set(t for t in targets if "without-%s" % t.lower() not in flags) targets = set(t for t in targets if t.lower() not in DYNAMIC_ARGS.without)
if not explicit_targets and not BUILD_BONSAIVIEWER: if not explicit_targets and not BUILD_BONSAIVIEWER:
targets.difference_update({"BonsaiViewer", "qt6"}) targets.difference_update({"BonsaiViewer", "qt6"})
if BUILD_BONSAIVIEWER: if BUILD_BONSAIVIEWER:
@@ -821,7 +930,7 @@ def build_dependency(
) )
logger.info(f"\rInstalled {name} \n") logger.info(f"\rInstalled {name} \n")
if DISK_CLEANUP: if ARGS.diskcleanup:
shutil.rmtree(build_dir, ignore_errors=True) shutil.rmtree(build_dir, ignore_errors=True)
@@ -948,7 +1057,7 @@ else:
CFLAGS = CFLAGS_MINIMAL CFLAGS = CFLAGS_MINIMAL
LDFLAGS = f"{LDFLAGS} {ADDITIONAL_ARGS_STR}" LDFLAGS = f"{LDFLAGS} {ADDITIONAL_ARGS_STR}"
if LTO: if ARGS.lto:
for f in compiler_flags: for f in compiler_flags:
locals()[f] += f" -flto={IFCOS_NUM_BUILD_PROCS}" locals()[f] += f" -flto={IFCOS_NUM_BUILD_PROCS}"
@@ -1441,7 +1550,7 @@ os.makedirs(ifcos_build_dir, exist_ok=True)
cmake_args = [ cmake_args = [
"-DUSE_MMAP=OFF", "-DUSE_MMAP=OFF",
f"-DBUILD_EXAMPLES={OFF_ON[BUILD_EXAMPLES]}", f"-DBUILD_EXAMPLES={OFF_ON[ARGS.build_examples]}",
"-DBUILD_SHARED_LIBS=" + OFF_ON[not IFCOPENSHELL_STATIC], "-DBUILD_SHARED_LIBS=" + OFF_ON[not IFCOPENSHELL_STATIC],
"-DGLTF_SUPPORT=ON", "-DGLTF_SUPPORT=ON",
"-DBoost_NO_BOOST_CMAKE=On", "-DBoost_NO_BOOST_CMAKE=On",