mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 14:23:53 +00:00
bfce5b9415
Moving to Python to make Windows build scripts more maintainable. It's intended to be a drop-in replacement, so it should be possible to just switch `.\build-deps.cmd` to `python build-deps.py`, keeping exactly the same arguments and behaviour will be the same. `build-deps.cmd` is deprecated, but not yet removed, but will be shortly after more testing. Other batch files will be migrated to Python shortly after too.
288 lines
11 KiB
Python
288 lines
11 KiB
Python
# /// script
|
|
# [tool.ty.environment]
|
|
# root = ["."]
|
|
# ///
|
|
###############################################################################
|
|
# #
|
|
# This file is part of IfcOpenShell. #
|
|
# #
|
|
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
|
# it under the terms of the Lesser GNU General Public License as published by #
|
|
# the Free Software Foundation, either version 3.0 of the License, or #
|
|
# (at your option) any later version. #
|
|
# #
|
|
# IfcOpenShell is distributed in the hope that it will be useful, #
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
|
# Lesser GNU General Public License for more details. #
|
|
# #
|
|
# You should have received a copy of the Lesser GNU General Public License #
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
|
# #
|
|
###############################################################################
|
|
#
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Literal, NamedTuple
|
|
|
|
from common import SCRIPT_DIR, logger, run
|
|
|
|
ArchBits = Literal[32, 64]
|
|
# TODO: according to cmake docs, there's no `ARM` platform, only `ARM64`.
|
|
# So it can be deleted?
|
|
VsPlatform = Literal["Win32", "x64", "ARM", "ARM64"]
|
|
"""Platform names as used by Visual Studio (e.g. `cmake -A`), but fine to use for general platform checks too."""
|
|
GeneratorType = Literal["EMPTY", "SHORTHAND", "FULL_NAME"]
|
|
|
|
|
|
class CMakeGeneratorInfo(NamedTuple):
|
|
name: str
|
|
"""E.g. "Visual Studio 16 2019"."""
|
|
generator_num: int
|
|
"""E.g. 16 for "Visual Studio 16 2019"."""
|
|
vs_ver: int
|
|
"""E.g. 2019."""
|
|
vc_ver: str
|
|
"""E.g. "14.2"."""
|
|
vs_toolset: str
|
|
"""Default toolset for this generator, e.g. "v142".
|
|
See `VsCfgResult.vs_toolset_override` for an explicit override.
|
|
"""
|
|
boost_bootstrap_ver: str
|
|
"""E.g. "vc142"."""
|
|
|
|
|
|
CMAKE_GENERATORS = {
|
|
"Visual Studio 12 2013": CMakeGeneratorInfo("Visual Studio 12 2013", 12, 2013, "12.0", "v120", "vc120"),
|
|
"Visual Studio 14 2015": CMakeGeneratorInfo("Visual Studio 14 2015", 14, 2015, "14.0", "v140", "vc140"),
|
|
"Visual Studio 15 2017": CMakeGeneratorInfo("Visual Studio 15 2017", 15, 2017, "14.1", "v141", "vc141"),
|
|
"Visual Studio 16 2019": CMakeGeneratorInfo("Visual Studio 16 2019", 16, 2019, "14.2", "v142", "vc142"),
|
|
"Visual Studio 17 2022": CMakeGeneratorInfo("Visual Studio 17 2022", 17, 2022, "14.3", "v143", "vc143"),
|
|
"Visual Studio 18 2026": CMakeGeneratorInfo("Visual Studio 18 2026", 18, 2026, "14.5", "v145", "vc145"),
|
|
}
|
|
|
|
|
|
class VsCfgResult(NamedTuple):
|
|
generator: CMakeGeneratorInfo
|
|
vs_platform: VsPlatform
|
|
"""E.g. "x64"."""
|
|
vs_toolset_override: str | None
|
|
"""E.g. "v142", or None if not explicitly overridden via generator shorthand."""
|
|
arch_bits: ArchBits
|
|
boost_toolset: str
|
|
"""E.g. "msvc-14.2"."""
|
|
gen_shorthand: str
|
|
"""E.g. "vs2019-x64"."""
|
|
deps_dir: Path
|
|
"""E.g. Path("_deps")."""
|
|
install_dir: Path
|
|
"""E.g. Path("_deps-vs2019-x64-installed")."""
|
|
build_dir: str
|
|
"""E.g. "_build-vs2019-x64"."""
|
|
build_deps_cache_path: Path
|
|
"""E.g. Path("BuildDepsCache-x64-v142.txt")."""
|
|
|
|
PRINT_VARS_SKIP = {"gen_shorthand", "deps_dir", "install_dir", "build_dir", "build_deps_cache_path"}
|
|
|
|
def print_vars(self) -> None:
|
|
fields = [f for f in self._fields if f not in self.PRINT_VARS_SKIP]
|
|
name_width = max(len(f) for f in fields)
|
|
for field in fields:
|
|
logger.info(f"{field.upper():<{name_width}}: [{getattr(self, field)}]")
|
|
|
|
def is_vs_platform(self, platform: VsPlatform) -> bool:
|
|
"""Compare against `vs_platform`, just to prevent typos."""
|
|
return self.vs_platform == platform
|
|
|
|
|
|
VS_TOOLSET_TO_VC_VER = {info.vs_toolset: info.vc_ver for info in CMAKE_GENERATORS.values()}
|
|
"""E.g. "v142" -> "14.2"."""
|
|
|
|
VS_TOOLSET_TO_VS_VER = {info.vs_toolset: info.vs_ver for info in CMAKE_GENERATORS.values()}
|
|
"""E.g. "v142" -> 2019."""
|
|
|
|
|
|
class VSArchInfo(NamedTuple):
|
|
vs_platform: VsPlatform
|
|
arch_bits: ArchBits
|
|
|
|
|
|
VSCMD_ARG_TGT_ARCH_TO_INFO = {
|
|
"x86": VSArchInfo("Win32", 32),
|
|
"x64": VSArchInfo("x64", 64),
|
|
"arm": VSArchInfo("ARM", 32),
|
|
"arm64": VSArchInfo("ARM64", 64),
|
|
}
|
|
|
|
VS_PLATFORM_TO_INFO = {info.vs_platform: info for info in VSCMD_ARG_TGT_ARCH_TO_INFO.values()}
|
|
|
|
|
|
VSVar = Literal[
|
|
"VSINSTALLDIR",
|
|
"VisualStudioVersion",
|
|
"VSCMD_ARG_TGT_ARCH",
|
|
"UCRTVersion",
|
|
]
|
|
|
|
|
|
def get_vs_var(var: VSVar) -> str:
|
|
value = os.getenv(var)
|
|
if value is None:
|
|
logger.error("Visual Studio environment variables not set - cannot proceed.")
|
|
sys.exit(1)
|
|
return value
|
|
|
|
|
|
class VsCfg:
|
|
"""Determines the CMake generator, VS platform/toolset, and derived paths for a build.
|
|
|
|
Usage: `VsCfg.build(generator, repo_root)`.
|
|
"""
|
|
|
|
@staticmethod
|
|
def build(generator: str | None, repo_root: Path) -> VsCfgResult:
|
|
generator = generator or ""
|
|
vs_platform: VsPlatform | None = None
|
|
vs_toolset: str | None = None
|
|
boost_toolset_ver: str | None = None
|
|
|
|
generator_type = VsCfg._determine_generator_type(generator)
|
|
if generator_type in ("EMPTY", "SHORTHAND"):
|
|
generator, vs_platform, vs_toolset, boost_toolset_ver = VsCfg._parse_generator_shorthand(
|
|
generator, generator_type
|
|
)
|
|
|
|
# Just to be double sure.
|
|
assert generator in CMAKE_GENERATORS
|
|
VsCfg._ensure_cmake_supports_generator(generator)
|
|
|
|
vs_platform = vs_platform or VsCfg._vs_platform_from_env()
|
|
generator_info = CMAKE_GENERATORS[generator]
|
|
arch_bits = VS_PLATFORM_TO_INFO[vs_platform].arch_bits
|
|
|
|
boost_toolset = f"msvc-{boost_toolset_ver or generator_info.vc_ver}"
|
|
|
|
gen_shorthand = f"vs{generator_info.vs_ver}-{vs_platform}"
|
|
if vs_toolset is not None:
|
|
gen_shorthand += f"-{vs_toolset}"
|
|
|
|
# NOTE For IfcOpenShell we can build all of our deps both x86 and x64 using different VS versions in the same
|
|
# directories so no need for "-{vs_ver}-{vs_platform}" postfix.
|
|
deps_dir = repo_root / "_deps"
|
|
install_dir = repo_root / f"_deps-{gen_shorthand}-installed"
|
|
# build_dir is a relative build directory used for CMake-based projects.
|
|
build_dir = f"_build-{gen_shorthand}"
|
|
|
|
if vs_toolset is not None:
|
|
build_deps_cache_path = SCRIPT_DIR / f"BuildDepsCache-{vs_platform}-{vs_toolset}.txt"
|
|
else:
|
|
build_deps_cache_path = SCRIPT_DIR / f"BuildDepsCache-{vs_platform}.txt"
|
|
|
|
result = VsCfgResult(
|
|
generator=generator_info,
|
|
vs_platform=vs_platform,
|
|
vs_toolset_override=vs_toolset,
|
|
arch_bits=arch_bits,
|
|
boost_toolset=boost_toolset,
|
|
gen_shorthand=gen_shorthand,
|
|
deps_dir=deps_dir,
|
|
install_dir=install_dir,
|
|
build_dir=build_dir,
|
|
build_deps_cache_path=build_deps_cache_path,
|
|
)
|
|
result.print_vars()
|
|
return result
|
|
|
|
@staticmethod
|
|
def _generator_from_visual_studio_version() -> str:
|
|
# E.g. '17.0' -> 17.
|
|
vs_version = get_vs_var("VisualStudioVersion")
|
|
generator_num = int(vs_version.replace(".0", ""))
|
|
|
|
for candidate, info in CMAKE_GENERATORS.items():
|
|
if info.generator_num == generator_num:
|
|
logger.info(
|
|
f"Generator not passed, but VisualStudioVersion={vs_version} environment variable detected:"
|
|
)
|
|
logger.info(f"using '{candidate}' as the generator.")
|
|
return candidate
|
|
|
|
logger.error(
|
|
f"Generator is not provided and VisualStudioVersion='{vs_version}' is not supported - cannot proceed."
|
|
)
|
|
sys.exit(1)
|
|
|
|
@staticmethod
|
|
def _vs_platform_from_env() -> VsPlatform:
|
|
# Fall back to `VSCMD_ARG_TGT_ARCH`, the command prompt's target platform, set by vsvarsall.
|
|
VSCMD_ARG_TGT_ARCH = get_vs_var("VSCMD_ARG_TGT_ARCH")
|
|
return VSCMD_ARG_TGT_ARCH_TO_INFO[VSCMD_ARG_TGT_ARCH].vs_platform
|
|
|
|
@staticmethod
|
|
def _ensure_cmake_supports_generator(generator: str) -> None:
|
|
cmake_help = run("cmake", "--help")
|
|
if generator not in cmake_help:
|
|
logger.error(f"The used CMake version does not support generator '{generator}' - cannot proceed.")
|
|
sys.exit(1)
|
|
|
|
@staticmethod
|
|
def _determine_generator_type(generator: str) -> GeneratorType:
|
|
match generator:
|
|
case "":
|
|
return "EMPTY"
|
|
case _ if "vs20" in generator:
|
|
# E.g. 'vs2022-x64'.
|
|
return "SHORTHAND"
|
|
case _:
|
|
if generator not in CMAKE_GENERATORS:
|
|
supported_generators = ", ".join(repr(g) for g in CMAKE_GENERATORS)
|
|
logger.error(
|
|
f"Invalid or unsupported CMake generator string passed: '{generator}' - cannot proceed."
|
|
)
|
|
logger.error(f"Supported CMake generator strings: {supported_generators}")
|
|
sys.exit(1)
|
|
return "FULL_NAME"
|
|
|
|
@staticmethod
|
|
def _parse_generator_shorthand(
|
|
generator: str, generator_type: GeneratorType
|
|
) -> tuple[str, VsPlatform | None, str | None, str | None]:
|
|
# E.g. '2022-x64'.
|
|
gen_shorthand = generator.replace("vs", "")
|
|
|
|
vs_platform = None
|
|
vs_toolset = None
|
|
boost_toolset_ver = None
|
|
|
|
# Order matters, e.g. "-arm64" also matches "-arm", so it must come last.
|
|
for arch, info in VSCMD_ARG_TGT_ARCH_TO_INFO.items():
|
|
if f"-{arch}" in gen_shorthand.lower():
|
|
vs_platform = info.vs_platform
|
|
|
|
for toolset, vc_ver in VS_TOOLSET_TO_VC_VER.items():
|
|
if f"-{toolset}" in gen_shorthand:
|
|
vs_toolset = toolset
|
|
boost_toolset_ver = vc_ver
|
|
|
|
vs_ver = gen_shorthand[:4]
|
|
|
|
if generator_type == "SHORTHAND":
|
|
for candidate, info in CMAKE_GENERATORS.items():
|
|
if info.vs_ver == int(vs_ver):
|
|
generator = candidate
|
|
break
|
|
else:
|
|
supported_generators = ", ".join(repr(g) for g in CMAKE_GENERATORS)
|
|
logger.error(f"Invalid or unsupported CMake generator string passed: '{generator}' - cannot proceed.")
|
|
logger.error(f"Supported CMake generator strings: {supported_generators}")
|
|
sys.exit(1)
|
|
else:
|
|
generator = VsCfg._generator_from_visual_studio_version()
|
|
|
|
return generator, vs_platform, vs_toolset, boost_toolset_ver
|
|
|
|
|
|
def vs_cfg(generator: str | None, repo_root: Path) -> VsCfgResult:
|
|
return VsCfg.build(generator, repo_root)
|