mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-22 00:51:04 +00:00
build-all - small refactor for readability
This commit is contained in:
+78
-32
@@ -18,6 +18,30 @@
|
|||||||
# #
|
# #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
Example usage:
|
||||||
|
# Build all targets by default.
|
||||||
|
python build-all.py
|
||||||
|
|
||||||
|
# Build just the provided targets.
|
||||||
|
python build-all.py IfcParse IfcOpenShell-Python
|
||||||
|
|
||||||
|
|
||||||
|
Available arguments:
|
||||||
|
``-py-313`` - build for specific Python version
|
||||||
|
(building for all supported Python version by default).
|
||||||
|
``-wasm`` - compile for wasm
|
||||||
|
``-shared`` - build shared libraries. By default will build static.
|
||||||
|
|
||||||
|
|
||||||
|
Used environment variables:
|
||||||
|
- ``CXXFLAGS``, ``CPPFLAGS``, ``CFLAGS``, ``LDFLAGS``
|
||||||
|
- ``BUILD_DIR`` - build directory. By default will use "build" folder in IfcOpenShell repository.
|
||||||
|
- ``DEPS_DIR`` - dependencies directory. By default will create automatic folder in build directory.
|
||||||
|
- ``BUILD_CFG`` - build configuration, 'RelWithDebInfo' by default.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# #
|
# #
|
||||||
# This script builds IfcOpenShell and its dependencies #
|
# This script builds IfcOpenShell and its dependencies #
|
||||||
@@ -72,6 +96,9 @@ ssl._create_default_https_context = ssl._create_unverified_context
|
|||||||
|
|
||||||
import time
|
import time
|
||||||
from urllib.request import urlretrieve
|
from urllib.request import urlretrieve
|
||||||
|
from collections.abc import Generator, Sequence
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Union, Literal
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -118,7 +145,9 @@ wget = "wget"
|
|||||||
strip = "strip"
|
strip = "strip"
|
||||||
|
|
||||||
explicit_targets = [s for s in sys.argv[1:] if not s.startswith("-")]
|
explicit_targets = [s for s in sys.argv[1:] if not s.startswith("-")]
|
||||||
|
"""Targets provided by CLI."""
|
||||||
flags = set(s.lstrip("-") for s in sys.argv[1:] if s.startswith("-"))
|
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
|
||||||
|
|
||||||
@@ -160,10 +189,10 @@ CMAKE_DIR = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "cmak
|
|||||||
|
|
||||||
build_dir = os.environ.get("BUILD_DIR", os.path.join(os.path.dirname(__file__), "..", "build"))
|
build_dir = os.environ.get("BUILD_DIR", os.path.join(os.path.dirname(__file__), "..", "build"))
|
||||||
|
|
||||||
path = [build_dir, platform.system(), "wasm" if "wasm" in flags else platform.machine()]
|
DEFAULT_DEPS_DIR = Path(build_dir) / platform.system() / ("wasm" if "wasm" in flags else platform.machine())
|
||||||
if TOOLSET:
|
if TOOLSET:
|
||||||
path.append(TOOLSET)
|
DEFAULT_DEPS_DIR = DEFAULT_DEPS_DIR / TOOLSET
|
||||||
DEFAULT_DEPS_DIR = os.path.realpath(os.path.join(*path))
|
DEFAULT_DEPS_DIR = os.path.realpath(DEFAULT_DEPS_DIR)
|
||||||
|
|
||||||
DEPS_DIR = os.getenv("DEPS_DIR", DEFAULT_DEPS_DIR)
|
DEPS_DIR = os.getenv("DEPS_DIR", DEFAULT_DEPS_DIR)
|
||||||
|
|
||||||
@@ -208,7 +237,7 @@ cecho(
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
dependency_tree = {
|
dependency_tree: dict[str, tuple[str, ...]] = {
|
||||||
"IfcParse": ("boost", "libxml2", "hdf5"),
|
"IfcParse": ("boost", "libxml2", "hdf5"),
|
||||||
"IfcGeom": ("IfcParse", "occ", "json", "cgal", "eigen"),
|
"IfcGeom": ("IfcParse", "occ", "json", "cgal", "eigen"),
|
||||||
"IfcConvert": ("IfcGeom",),
|
"IfcConvert": ("IfcGeom",),
|
||||||
@@ -230,10 +259,10 @@ dependency_tree = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def v(dep):
|
def gather_dependencies(dep: str) -> Generator[str]:
|
||||||
yield dep
|
yield dep
|
||||||
for d in dependency_tree[dep]:
|
for d in dependency_tree[dep]:
|
||||||
for x in v(d):
|
for x in gather_dependencies(d):
|
||||||
yield x
|
yield x
|
||||||
|
|
||||||
|
|
||||||
@@ -247,21 +276,21 @@ BUILD_STATIC = "shared" not in flags
|
|||||||
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"
|
||||||
LINK_TYPE = "static" if BUILD_STATIC else "shared"
|
LINK_TYPE = "static" if BUILD_STATIC else "shared"
|
||||||
LINK_TYPE_UCFIRST = LINK_TYPE[0].upper() + LINK_TYPE[1:]
|
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 any(f.startswith("py-") for f in flags):
|
||||||
PYTHON_VERSIONS = [pyv for pyv in PYTHON_VERSIONS if "py-%s" % "".join(pyv.split(".")[0:2]) in flags]
|
PYTHON_VERSIONS = [pyv for pyv in PYTHON_VERSIONS if f"py-{''.join(pyv.split('.')[:2])}" in flags]
|
||||||
|
|
||||||
if len(explicit_targets):
|
if explicit_targets:
|
||||||
targets = set(sum((list(v(target)) for target in explicit_targets), []))
|
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 "without-%s" % t.lower() not in flags)
|
||||||
|
|
||||||
print("Building:", *sorted(targets, key=lambda t: len(list(v(t)))))
|
print("Building:", *sorted(targets, key=lambda t: len(list(gather_dependencies(t)))))
|
||||||
|
|
||||||
# Check that required tools are in PATH
|
# Check that required tools are in PATH
|
||||||
|
|
||||||
@@ -290,7 +319,7 @@ except:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def run(cmds, cwd=None, can_fail=False):
|
def run(cmds: Sequence[str], cwd: Union[str, None] = None, can_fail: bool = False) -> str:
|
||||||
"""
|
"""
|
||||||
Wraps `subprocess.Popen.communicate()` and logs the command being executed,
|
Wraps `subprocess.Popen.communicate()` and logs the command being executed,
|
||||||
sets up logging `stderr` to `LOG_FILE` (in append mode) and returns stdout
|
sets up logging `stderr` to `LOG_FILE` (in append mode) and returns stdout
|
||||||
@@ -384,7 +413,7 @@ def run_cmake(arg1, cmake_args, cmake_dir=None, cwd=None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def git_clone_or_pull_repository(clone_url, target_dir, revision=None):
|
def git_clone_or_pull_repository(clone_url: str, target_dir: str, revision: Union[str, None] = None) -> None:
|
||||||
"""Lazily clones the `git` repository denoted by `clone_url` into
|
"""Lazily clones the `git` repository denoted by `clone_url` into
|
||||||
the `target_dir` or pulls latest changes if the `target_dir` exists (naively assumes
|
the `target_dir` or pulls latest changes if the `target_dir` exists (naively assumes
|
||||||
that a working clone exists there) and optionally checks out a revision
|
that a working clone exists there) and optionally checks out a revision
|
||||||
@@ -408,24 +437,34 @@ def git_clone_or_pull_repository(clone_url, target_dir, revision=None):
|
|||||||
|
|
||||||
|
|
||||||
def build_dependency(
|
def build_dependency(
|
||||||
name,
|
name: str,
|
||||||
mode,
|
mode: Literal[
|
||||||
build_tool_args,
|
"cmake",
|
||||||
download_url,
|
"autoconf",
|
||||||
download_name,
|
"ctest",
|
||||||
download_tool=download_tool_default,
|
"bjam",
|
||||||
revision=None,
|
],
|
||||||
patch=None,
|
build_tool_args: list[str],
|
||||||
|
download_url: str,
|
||||||
|
download_name: str,
|
||||||
|
download_tool: Literal["py", "git"] = download_tool_default,
|
||||||
|
revision: Union[str, None] = None,
|
||||||
|
patch: Union[str, list[str], None] = None,
|
||||||
shell=None,
|
shell=None,
|
||||||
pre_compile_subs=[],
|
pre_compile_subs: Sequence[tuple[str, str, str]] = (),
|
||||||
additional_files={},
|
additional_files: Union[dict[str, str], None] = None,
|
||||||
no_append_name=False,
|
no_append_name=False,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
) -> None:
|
||||||
"""Handles building of dependencies with different tools (which are
|
"""Handles building of dependencies with different tools (which are
|
||||||
distinguished with the `mode` argument. `build_tool_args` is expected to be
|
distinguished with the `mode` argument. `build_tool_args` is expected to be
|
||||||
a list which is necessary in order to not mess up quoting of compiler and
|
a list which is necessary in order to not mess up quoting of compiler and
|
||||||
linker flags."""
|
linker flags.
|
||||||
|
|
||||||
|
:param pre_compile_subs: A sequence of ``(fn, before, after)``
|
||||||
|
:param additional_files: Mapping path->url.
|
||||||
|
:param kwargs: Additional ``mode`` related kwargs.
|
||||||
|
"""
|
||||||
check_dir = os.path.join(DEPS_DIR, "install", name)
|
check_dir = os.path.join(DEPS_DIR, "install", name)
|
||||||
if os.path.exists(check_dir):
|
if os.path.exists(check_dir):
|
||||||
logger.info(f"Found existing {name}, skipping")
|
logger.info(f"Found existing {name}, skipping")
|
||||||
@@ -488,9 +527,10 @@ def build_dependency(
|
|||||||
if not os.path.exists(extract_dir):
|
if not os.path.exists(extract_dir):
|
||||||
run([tar, "-xf", download_name], cwd=build_dir)
|
run([tar, "-xf", download_name], cwd=build_dir)
|
||||||
|
|
||||||
for path, url in additional_files.items():
|
if additional_files:
|
||||||
if not os.path.exists(path):
|
for path, url in additional_files.items():
|
||||||
urlretrieve(url, os.path.join(extract_dir, path))
|
if not os.path.exists(path):
|
||||||
|
urlretrieve(url, os.path.join(extract_dir, path))
|
||||||
|
|
||||||
if patch is not None:
|
if patch is not None:
|
||||||
if isinstance(patch, str):
|
if isinstance(patch, str):
|
||||||
@@ -786,7 +826,7 @@ if "python" in targets and not USE_CURRENT_PYTHON_VERSION and "wasm" not in flag
|
|||||||
|
|
||||||
# On OSX a dynamic python library is built or it would not be compatible
|
# On OSX a dynamic python library is built or it would not be compatible
|
||||||
# with the system python because of some threading initialization
|
# with the system python because of some threading initialization
|
||||||
PYTHON_CONFIGURE_ARGS = []
|
PYTHON_CONFIGURE_ARGS: list[str] = []
|
||||||
if platform.system() == "Darwin":
|
if platform.system() == "Darwin":
|
||||||
PYTHON_CONFIGURE_ARGS = ["--enable-shared"]
|
PYTHON_CONFIGURE_ARGS = ["--enable-shared"]
|
||||||
|
|
||||||
@@ -851,8 +891,8 @@ if "boost" in targets:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if "cgal" in targets:
|
if "cgal" in targets:
|
||||||
gmp_args = []
|
gmp_args: list[str] = []
|
||||||
mpfr_args = []
|
mpfr_args: list[str] = []
|
||||||
if "wasm" in flags:
|
if "wasm" in flags:
|
||||||
gmp_args.extend(("--disable-assembly", "--host", "none", "--enable-cxx"))
|
gmp_args.extend(("--disable-assembly", "--host", "none", "--enable-cxx"))
|
||||||
mpfr_args.extend(("--host", "none"))
|
mpfr_args.extend(("--host", "none"))
|
||||||
@@ -1057,7 +1097,12 @@ if "IfcOpenShell-Python" in targets:
|
|||||||
python_dir = os.path.join(IFCOS_DIR, "pythonwrapper")
|
python_dir = os.path.join(IFCOS_DIR, "pythonwrapper")
|
||||||
os.makedirs(python_dir, exist_ok=True)
|
os.makedirs(python_dir, exist_ok=True)
|
||||||
|
|
||||||
def compile_python_wrapper(python_version, python_library, python_include, python_executable):
|
def compile_python_wrapper(
|
||||||
|
python_version: str, python_library: str, python_include: str, python_executable: Union[str, None]
|
||||||
|
) -> Union[str, None]:
|
||||||
|
"""
|
||||||
|
:return: Path to module dir if ``python_executable`` was provided, otherwise ``None``.
|
||||||
|
"""
|
||||||
logger.info(f"\rConfiguring python {python_version} wrapper...")
|
logger.info(f"\rConfiguring python {python_version} wrapper...")
|
||||||
|
|
||||||
cache_path = os.path.join(python_dir, "CMakeCache.txt")
|
cache_path = os.path.join(python_dir, "CMakeCache.txt")
|
||||||
@@ -1144,6 +1189,7 @@ if "IfcOpenShell-Python" in targets:
|
|||||||
)
|
)
|
||||||
|
|
||||||
module_dir = compile_python_wrapper(python_version, python_library, python_include, python_executable)
|
module_dir = compile_python_wrapper(python_version, python_library, python_include, python_executable)
|
||||||
|
assert module_dir
|
||||||
run([cp, "-R", module_dir, os.path.join(DEPS_DIR, "install", "ifcopenshell", f"python-{python_version}")])
|
run([cp, "-R", module_dir, os.path.join(DEPS_DIR, "install", "ifcopenshell", f"python-{python_version}")])
|
||||||
|
|
||||||
logger.info("\rBuilt IfcOpenShell...\n\n")
|
logger.info("\rBuilt IfcOpenShell...\n\n")
|
||||||
|
|||||||
Reference in New Issue
Block a user