Skip to content

Commit e507621

Browse files
authored
Merge pull request #69 from python-project-templates/tkp/vcpkg
Refactor toolchains, start on vcpkg
2 parents c2fdeed + 09f727b commit e507621

File tree

8 files changed

+202
-125
lines changed

8 files changed

+202
-125
lines changed

hatch_cpp/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
__version__ = "0.1.8"
22

3-
from .hooks import hatch_register_build_hook
4-
from .plugin import HatchCppBuildHook
5-
from .structs import *
3+
from .config import *
4+
from .hooks import *
5+
from .plugin import *
6+
from .toolchains import *

hatch_cpp/config.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
from __future__ import annotations
2+
3+
from os import system as system_call
4+
from pathlib import Path
5+
from typing import List, Optional
6+
7+
from pydantic import BaseModel, Field, model_validator
8+
9+
from .toolchains import BuildType, HatchCppCmakeConfiguration, HatchCppLibrary, HatchCppPlatform, HatchCppVcpkgConfiguration
10+
11+
__all__ = (
12+
"HatchCppBuildConfig",
13+
"HatchCppBuildPlan",
14+
)
15+
16+
17+
class HatchCppBuildConfig(BaseModel):
18+
"""Build config values for Hatch C++ Builder."""
19+
20+
verbose: Optional[bool] = Field(default=False)
21+
name: Optional[str] = Field(default=None)
22+
libraries: List[HatchCppLibrary] = Field(default_factory=list)
23+
cmake: Optional[HatchCppCmakeConfiguration] = Field(default=None)
24+
platform: Optional[HatchCppPlatform] = Field(default_factory=HatchCppPlatform.default)
25+
vcpkg: Optional[HatchCppVcpkgConfiguration] = Field(default=None)
26+
27+
@model_validator(mode="wrap")
28+
@classmethod
29+
def validate_model(cls, data, handler):
30+
if "toolchain" in data:
31+
data["platform"] = HatchCppPlatform.platform_for_toolchain(data["toolchain"])
32+
data.pop("toolchain")
33+
elif "platform" not in data:
34+
data["platform"] = HatchCppPlatform.default()
35+
if "cc" in data:
36+
data["platform"].cc = data["cc"]
37+
data.pop("cc")
38+
if "cxx" in data:
39+
data["platform"].cxx = data["cxx"]
40+
data.pop("cxx")
41+
if "ld" in data:
42+
data["platform"].ld = data["ld"]
43+
data.pop("ld")
44+
model = handler(data)
45+
if model.cmake and model.libraries:
46+
raise ValueError("Must not provide libraries when using cmake toolchain.")
47+
return model
48+
49+
50+
class HatchCppBuildPlan(HatchCppBuildConfig):
51+
build_type: BuildType = "release"
52+
commands: List[str] = Field(default_factory=list)
53+
54+
def generate(self):
55+
self.commands = []
56+
57+
if self.vcpkg and Path(self.vcpkg.vcpkg).exists():
58+
self.commands.extend(self.vcpkg.generate(self.platform))
59+
60+
if self.libraries:
61+
for library in self.libraries:
62+
compile_flags = self.platform.get_compile_flags(library, self.build_type)
63+
link_flags = self.platform.get_link_flags(library, self.build_type)
64+
self.commands.append(
65+
f"{self.platform.cc if library.language == 'c' else self.platform.cxx} {' '.join(library.sources)} {compile_flags} {link_flags}"
66+
)
67+
elif self.cmake:
68+
self.commands.extend(self.cmake.generate(self))
69+
70+
return self.commands
71+
72+
def execute(self):
73+
for command in self.commands:
74+
system_call(command)
75+
return self.commands
76+
77+
def cleanup(self):
78+
if self.platform.platform == "win32":
79+
for temp_obj in Path(".").glob("*.obj"):
80+
temp_obj.unlink()

hatch_cpp/plugin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
1111

12-
from .structs import HatchCppBuildConfig, HatchCppBuildPlan
12+
from .config import HatchCppBuildConfig, HatchCppBuildPlan
1313
from .utils import import_string
1414

1515
__all__ = ("HatchCppBuildHook",)

hatch_cpp/tests/test_structs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from pydantic import ValidationError
66
from toml import loads
77

8-
from hatch_cpp.structs import HatchCppBuildConfig, HatchCppBuildPlan, HatchCppLibrary, HatchCppPlatform
8+
from hatch_cpp import HatchCppBuildConfig, HatchCppBuildPlan, HatchCppLibrary, HatchCppPlatform
99

1010

1111
class TestStructs:

hatch_cpp/toolchains/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .cmake import *
2+
from .common import *
3+
from .vcpkg import *

hatch_cpp/toolchains/cmake.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
from __future__ import annotations
2+
3+
from os import environ
4+
from pathlib import Path
5+
from sys import version_info
6+
from typing import Any, Dict, Optional
7+
8+
from pydantic import BaseModel, Field
9+
10+
from .common import Platform
11+
12+
__all__ = ("HatchCppCmakeConfiguration",)
13+
14+
15+
class HatchCppCmakeConfiguration(BaseModel):
16+
root: Path
17+
build: Path = Field(default_factory=lambda: Path("build"))
18+
install: Optional[Path] = Field(default=None)
19+
20+
cmake_arg_prefix: Optional[str] = Field(default=None)
21+
cmake_args: Dict[str, str] = Field(default_factory=dict)
22+
cmake_env_args: Dict[Platform, Dict[str, str]] = Field(default_factory=dict)
23+
24+
include_flags: Optional[Dict[str, Any]] = Field(default=None)
25+
26+
def generate(self, config) -> Dict[str, Any]:
27+
commands = []
28+
29+
# Derive prefix
30+
if self.cmake_arg_prefix is None:
31+
self.cmake_arg_prefix = f"{config.name.replace('.', '_').replace('-', '_').upper()}_"
32+
33+
# Append base command
34+
commands.append(f"cmake {Path(self.root).parent} -DCMAKE_BUILD_TYPE={config.build_type} -B {self.build}")
35+
36+
# Setup install path
37+
if self.install:
38+
commands[-1] += f" -DCMAKE_INSTALL_PREFIX={self.install}"
39+
else:
40+
commands[-1] += f" -DCMAKE_INSTALL_PREFIX={Path(self.root).parent}"
41+
42+
# TODO: CMAKE_CXX_COMPILER
43+
if config.platform.platform == "win32":
44+
# TODO: prefix?
45+
commands[-1] += f' -G "{environ.get("GENERATOR", "Visual Studio 17 2022")}"'
46+
47+
# Put in CMake flags
48+
args = self.cmake_args.copy()
49+
for platform, env_args in self.cmake_env_args.items():
50+
if platform == config.platform.platform:
51+
for key, value in env_args.items():
52+
args[key] = value
53+
for key, value in args.items():
54+
commands[-1] += f" -D{self.cmake_arg_prefix}{key.upper()}={value}"
55+
56+
# Include customs
57+
if self.include_flags:
58+
if self.include_flags.get("python_version", False):
59+
commands[-1] += f" -D{self.cmake_arg_prefix}PYTHON_VERSION={version_info.major}.{version_info.minor}"
60+
if self.include_flags.get("manylinux", False) and config.platform.platform == "linux":
61+
commands[-1] += f" -D{self.cmake_arg_prefix}MANYLINUX=ON"
62+
63+
# Include mac deployment target
64+
if config.platform.platform == "darwin":
65+
commands[-1] += f" -DCMAKE_OSX_DEPLOYMENT_TARGET={environ.get('OSX_DEPLOYMENT_TARGET', '11')}"
66+
67+
# Append build command
68+
commands.append(f"cmake --build {self.build} --config {config.build_type}")
69+
70+
# Append install command
71+
commands.append(f"cmake --install {self.build} --config {config.build_type}")
72+
73+
return commands

hatch_cpp/structs.py renamed to hatch_cpp/toolchains/common.py

Lines changed: 10 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
11
from __future__ import annotations
22

3-
from os import environ, system as system_call
3+
from os import environ
44
from pathlib import Path
55
from re import match
66
from shutil import which
7-
from sys import executable, platform as sys_platform, version_info
7+
from sys import executable, platform as sys_platform
88
from sysconfig import get_path
9-
from typing import Any, Dict, List, Literal, Optional
9+
from typing import Any, List, Literal, Optional
1010

1111
from pydantic import AliasChoices, BaseModel, Field, field_validator, model_validator
1212

1313
__all__ = (
14-
"HatchCppBuildConfig",
14+
"BuildType",
15+
"CompilerToolchain",
16+
"Language",
17+
"Binding",
18+
"Platform",
19+
"PlatformDefaults",
1520
"HatchCppLibrary",
1621
"HatchCppPlatform",
17-
"HatchCppBuildPlan",
1822
)
1923

24+
2025
BuildType = Literal["debug", "release"]
2126
CompilerToolchain = Literal["gcc", "clang", "msvc"]
2227
Language = Literal["c", "c++"]
@@ -231,118 +236,3 @@ def get_link_flags(self, library: HatchCppLibrary, build_type: BuildType = "rele
231236
while flags.count(" "):
232237
flags = flags.replace(" ", " ")
233238
return flags
234-
235-
236-
class HatchCppCmakeConfiguration(BaseModel):
237-
root: Path
238-
build: Path = Field(default_factory=lambda: Path("build"))
239-
install: Optional[Path] = Field(default=None)
240-
241-
cmake_arg_prefix: Optional[str] = Field(default=None)
242-
cmake_args: Dict[str, str] = Field(default_factory=dict)
243-
cmake_env_args: Dict[Platform, Dict[str, str]] = Field(default_factory=dict)
244-
245-
include_flags: Optional[Dict[str, Any]] = Field(default=None)
246-
247-
248-
class HatchCppBuildConfig(BaseModel):
249-
"""Build config values for Hatch C++ Builder."""
250-
251-
verbose: Optional[bool] = Field(default=False)
252-
name: Optional[str] = Field(default=None)
253-
libraries: List[HatchCppLibrary] = Field(default_factory=list)
254-
cmake: Optional[HatchCppCmakeConfiguration] = Field(default=None)
255-
platform: Optional[HatchCppPlatform] = Field(default_factory=HatchCppPlatform.default)
256-
257-
@model_validator(mode="wrap")
258-
@classmethod
259-
def validate_model(cls, data, handler):
260-
if "toolchain" in data:
261-
data["platform"] = HatchCppPlatform.platform_for_toolchain(data["toolchain"])
262-
data.pop("toolchain")
263-
elif "platform" not in data:
264-
data["platform"] = HatchCppPlatform.default()
265-
if "cc" in data:
266-
data["platform"].cc = data["cc"]
267-
data.pop("cc")
268-
if "cxx" in data:
269-
data["platform"].cxx = data["cxx"]
270-
data.pop("cxx")
271-
if "ld" in data:
272-
data["platform"].ld = data["ld"]
273-
data.pop("ld")
274-
model = handler(data)
275-
if model.cmake and model.libraries:
276-
raise ValueError("Must not provide libraries when using cmake toolchain.")
277-
return model
278-
279-
280-
class HatchCppBuildPlan(HatchCppBuildConfig):
281-
build_type: BuildType = "release"
282-
commands: List[str] = Field(default_factory=list)
283-
284-
def generate(self):
285-
self.commands = []
286-
if self.libraries:
287-
for library in self.libraries:
288-
compile_flags = self.platform.get_compile_flags(library, self.build_type)
289-
link_flags = self.platform.get_link_flags(library, self.build_type)
290-
self.commands.append(
291-
f"{self.platform.cc if library.language == 'c' else self.platform.cxx} {' '.join(library.sources)} {compile_flags} {link_flags}"
292-
)
293-
elif self.cmake:
294-
# Derive prefix
295-
if self.cmake.cmake_arg_prefix is None:
296-
self.cmake.cmake_arg_prefix = f"{self.name.replace('.', '_').replace('-', '_').upper()}_"
297-
298-
# Append base command
299-
self.commands.append(f"cmake {Path(self.cmake.root).parent} -DCMAKE_BUILD_TYPE={self.build_type} -B {self.cmake.build}")
300-
301-
# Setup install path
302-
if self.cmake.install:
303-
self.commands[-1] += f" -DCMAKE_INSTALL_PREFIX={self.cmake.install}"
304-
else:
305-
self.commands[-1] += f" -DCMAKE_INSTALL_PREFIX={Path(self.cmake.root).parent}"
306-
307-
# TODO: CMAKE_CXX_COMPILER
308-
if self.platform.platform == "win32":
309-
# TODO: prefix?
310-
self.commands[-1] += f' -G "{environ.get("GENERATOR", "Visual Studio 17 2022")}"'
311-
312-
# Put in CMake flags
313-
args = self.cmake.cmake_args.copy()
314-
for platform, env_args in self.cmake.cmake_env_args.items():
315-
if platform == self.platform.platform:
316-
for key, value in env_args.items():
317-
args[key] = value
318-
for key, value in args.items():
319-
self.commands[-1] += f" -D{self.cmake.cmake_arg_prefix}{key.upper()}={value}"
320-
321-
# Include customs
322-
if self.cmake.include_flags:
323-
if self.cmake.include_flags.get("python_version", False):
324-
self.commands[-1] += f" -D{self.cmake.cmake_arg_prefix}PYTHON_VERSION={version_info.major}.{version_info.minor}"
325-
if self.cmake.include_flags.get("manylinux", False) and self.platform.platform == "linux":
326-
self.commands[-1] += f" -D{self.cmake.cmake_arg_prefix}MANYLINUX=ON"
327-
328-
# Include mac deployment target
329-
if self.platform.platform == "darwin":
330-
self.commands[-1] += f" -DCMAKE_OSX_DEPLOYMENT_TARGET={environ.get('OSX_DEPLOYMENT_TARGET', '11')}"
331-
332-
# Append build command
333-
self.commands.append(f"cmake --build {self.cmake.build} --config {self.build_type}")
334-
335-
# Append install command
336-
self.commands.append(f"cmake --install {self.cmake.build} --config {self.build_type}")
337-
338-
return self.commands
339-
340-
def execute(self):
341-
for command in self.commands:
342-
system_call(command)
343-
return self.commands
344-
345-
def cleanup(self):
346-
if self.platform.platform == "win32":
347-
for temp_obj in Path(".").glob("*.obj"):
348-
temp_obj.unlink()

hatch_cpp/toolchains/vcpkg.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from __future__ import annotations
2+
3+
from pathlib import Path
4+
from sys import platform as sys_platform
5+
from typing import Optional
6+
7+
from pydantic import BaseModel, Field
8+
9+
__all__ = ("HatchCppVcpkgConfiguration",)
10+
11+
12+
class HatchCppVcpkgConfiguration(BaseModel):
13+
vcpkg: Optional[str] = Field(default="vcpkg.json")
14+
vcpkg_root: Optional[Path] = Field(default=Path("vcpkg"))
15+
vcpkg_repo: Optional[str] = Field(default="https://github.com/microsoft/vcpkg.git")
16+
17+
def generate(self, config):
18+
commands = []
19+
20+
if self.vcpkg and Path(self.vcpkg.vcpkg).exists():
21+
if not Path(self.vcpkg.vcpkg_root).exists():
22+
commands.append(f"git clone {self.vcpkg.vcpkg_repo} {self.vcpkg.vcpkg_root}")
23+
commands.append(
24+
f"./{self.vcpkg.vcpkg_root / 'bootstrap-vcpkg.sh' if sys_platform != 'win32' else self.vcpkg.vcpkg_root / 'sbootstrap-vcpkg.bat'}"
25+
)
26+
commands.append(
27+
f"./{self.vcpkg.vcpkg_root / 'vcpkg'} install --triplet {config.platform.platform}-{config.platform.toolchain} --manifest-root {Path(self.vcpkg.vcpkg).parent}"
28+
)
29+
30+
return commands

0 commit comments

Comments
 (0)