diff options
Diffstat (limited to 'project')
-rw-r--r-- | project/boost/build.py | 15 | ||||
-rw-r--r-- | project/boost/toolchain.py | 41 | ||||
-rw-r--r-- | project/cmake/build.py | 16 | ||||
-rw-r--r-- | project/cmake/toolchain.py | 37 | ||||
-rw-r--r-- | project/configuration.py | 24 | ||||
-rw-r--r-- | project/mingw.py | 42 | ||||
-rw-r--r-- | project/platform.py | 105 |
7 files changed, 176 insertions, 104 deletions
diff --git a/project/boost/build.py b/project/boost/build.py index b262e47..7cf1364 100644 --- a/project/boost/build.py +++ b/project/boost/build.py @@ -40,7 +40,7 @@ from project.os import on_linux_like from project.utils import normalize_path, setup_logging -DEFAULT_PLATFORMS = (Platform.native(),) +DEFAULT_PLATFORMS = (Platform.AUTO,) DEFAULT_CONFIGURATIONS = (Configuration.DEBUG, Configuration.RELEASE,) # For my development, I link everything statically (to be able to pull the # binaries from a CI, etc. and run them everywhere): @@ -71,7 +71,6 @@ class BuildParameters: self.boost_dir = boost_dir self.build_dir = build_dir - self.stage_dir = 'stage' self.platforms = platforms self.configurations = configurations self.link = link @@ -126,12 +125,11 @@ class BuildParameters: def _build_params(self, build_dir, toolchain, configuration, link, runtime_link): params = [] params.append(self._build_dir(build_dir)) - params.append(self._stagedir(toolchain, configuration)) - params.append('--layout=system') - params += toolchain.get_b2_args() - params.append(self._variant(configuration)) params.append(self._link(link)) params.append(self._runtime_link(runtime_link)) + params.append('--layout=system') + params += toolchain.b2_args(configuration) + params += configuration.b2_args() params += self.b2_args return params @@ -139,11 +137,6 @@ class BuildParameters: def _build_dir(build_dir): return f'--build-dir={build_dir}' - def _stagedir(self, toolchain, configuration): - platform = str(toolchain.platform) - configuration = str(configuration) - return f'--stagedir={os.path.join(self.stage_dir, platform, configuration)}' - @staticmethod def _link(link): return f'link={link}' diff --git a/project/boost/toolchain.py b/project/boost/toolchain.py index 08bc49c..5422a62 100644 --- a/project/boost/toolchain.py +++ b/project/boost/toolchain.py @@ -104,11 +104,8 @@ class Toolchain(abc.ABC): def __init__(self, platform): self.platform = platform - def get_b2_args(self): - return [ - # Always pass the address-model explicitly. - f'address-model={self.platform.get_address_model()}' - ] + def b2_args(self, configuration): + return self.platform.b2_args(configuration) @staticmethod @contextmanager @@ -139,8 +136,8 @@ class Auto(Toolchain): class MSVC(Auto): - def get_b2_args(self): - return super().get_b2_args() + [ + def b2_args(self, configuration): + return super().b2_args(configuration) + [ 'toolset=msvc', ] @@ -187,14 +184,16 @@ class BoostBuildToolset: self.options = options @property - def toolset_id(self): + def toolset(self): if self.version: return f'{self.compiler}-{self.version}' return self.compiler - @property - def b2_arg(self): - return f'toolset={self.toolset_id}' + def b2_toolset(self): + return f'toolset={self.toolset}' + + def b2_args(self): + return [self.b2_toolset()] def _format_using_options(self): return ''.join(f'\n <{name}>{val}' for name, val in self.options) @@ -232,13 +231,14 @@ class ConfigFile(Toolchain): with tmp as path: yield cls(platform, path, toolset) - def get_b2_args(self): + def b2_args(self, configuration): # All the required options and the toolset definition should be in the # user configuration file. - return super().get_b2_args() + [ - f'--user-config={self.config_path}', - self.toolset.b2_arg, - ] + args = [] + args += super().b2_args(configuration) + args.append(f'--user-config={self.config_path}') + args += self.toolset.b2_args() + return args class GCC(ConfigFile): @@ -273,8 +273,9 @@ class MinGW(GCC): @staticmethod def get_toolset(platform): - path = project.mingw.get_gxx(platform) - return BoostBuildToolset(MinGW.COMPILER, path, MinGW.get_options()) + paths = project.mingw.MinGW(platform) + compiler = paths.gxx() + return BoostBuildToolset(MinGW.COMPILER, compiler, MinGW.get_options()) class Clang(ConfigFile): @@ -320,8 +321,8 @@ class Clang(ConfigFile): class ClangCL(Toolchain): - def get_b2_args(self): - return super().get_b2_args() + [ + def b2_args(self, configuration): + return super().b2_args(configuration) + [ 'toolset=clang-win', 'define=BOOST_USE_WINDOWS_H', ] diff --git a/project/cmake/build.py b/project/cmake/build.py index 1e9a90d..a2fea78 100644 --- a/project/cmake/build.py +++ b/project/cmake/build.py @@ -34,7 +34,7 @@ from project.toolchain import ToolchainType from project.utils import normalize_path, mkdir_parent, run, setup_logging -DEFAULT_PLATFORM = None +DEFAULT_PLATFORM = Platform.AUTO DEFAULT_CONFIGURATION = Configuration.DEBUG DEFAULT_TOOLSET = ToolchainType.AUTO @@ -81,18 +81,14 @@ class GenerationPhase: def _get_boost_args(self): if self.boost_dir is None: return [] - stagedir = self._stagedir(self.boost_dir, self.platform, self.configuration) + root = self.boost_dir + librarydir = self.platform.boost_librarydir(self.configuration) + librarydir = os.path.join(self.boost_dir, librarydir) return [ - '-D', f'BOOST_ROOT={self.boost_dir}', - '-D', f'BOOST_LIBRARYDIR={stagedir}', + '-D', f'BOOST_ROOT={root}', + '-D', f'BOOST_LIBRARYDIR={librarydir}', ] - @staticmethod - def _stagedir(boost_dir, platform, configuration): - if platform is None: - platform = Platform.native() - return os.path.join(boost_dir, 'stage', str(platform), str(configuration), 'lib') - def run(self, toolchain): run_cmake(self._cmake_args(toolchain)) diff --git a/project/cmake/toolchain.py b/project/cmake/toolchain.py index 14197aa..0e68738 100644 --- a/project/cmake/toolchain.py +++ b/project/cmake/toolchain.py @@ -11,7 +11,6 @@ import shutil import project.mingw from project.os import on_windows -from project.platform import Platform from project.toolchain import ToolchainType @@ -68,11 +67,9 @@ class MSVC(Auto): self.platform = platform def get_cmake_args(self): - if self.platform is None: - return [] # This doesn't actually specify the generator of course, but I don't # want to implement VS detection logic. - return ['-A', self.platform.get_cmake_arch()] + return self.platform.cmake_msvc_args() def get_build_args(self): return ['/m'] @@ -102,17 +99,6 @@ class Makefile(Toolchain): file.write(contents) return cls(path) - @staticmethod - def _format_platform_compiler_flags(platform): - if platform is None: - # If the platform wasn't specified, don't use the -m flag, etc. - return '' - # Otherwise, use the standard -m32/-m64 flags. - return f''' -set(CMAKE_C_FLAGS -m{platform.get_address_model()}) -set(CMAKE_CXX_FLAGS -m{platform.get_address_model()}) -''' - def get_cmake_args(self): return [ '-D', f'CMAKE_TOOLCHAIN_FILE={self.path}', @@ -131,7 +117,7 @@ class GCC(Makefile): return f''' set(CMAKE_C_COMPILER gcc) set(CMAKE_CXX_COMPILER g++) -{Makefile._format_platform_compiler_flags(platform)}''' +{platform.makefile_toolchain_file()}''' @staticmethod def setup(platform, build_dir): @@ -141,16 +127,13 @@ set(CMAKE_CXX_COMPILER g++) class MinGW(Makefile): @staticmethod def _format(platform): - if platform is None: - # MinGW only supports x86/x64, plus we need the platform for the - # compiler file name, so default to x64 unless specified. - platform = Platform.X64 + paths = project.mingw.MinGW(platform) return f''' -set(CMAKE_C_COMPILER {project.mingw.get_gcc(platform)}) -set(CMAKE_CXX_COMPILER {project.mingw.get_gxx(platform)}) -set(CMAKE_AR {project.mingw.get_ar(platform)}) -set(CMAKE_RANLIB {project.mingw.get_ranlib(platform)}) -set(CMAKE_RC_COMPILER {project.mingw.get_windres(platform)}) +set(CMAKE_C_COMPILER {paths.gcc()}) +set(CMAKE_CXX_COMPILER {paths.gxx()}) +set(CMAKE_AR {paths.ar()}) +set(CMAKE_RANLIB {paths.ranlib()}) +set(CMAKE_RC_COMPILER {paths.windres()}) set(CMAKE_SYSTEM_NAME Windows) ''' @@ -170,7 +153,7 @@ else() set(CMAKE_C_COMPILER clang) set(CMAKE_CXX_COMPILER clang++) endif() -{Makefile._format_platform_compiler_flags(platform)}''' +{platform.makefile_toolchain_file()}''' def _get_makefile_generator(self): if on_windows(): @@ -192,7 +175,7 @@ class ClangCL(Clang): set(CMAKE_C_COMPILER clang-cl) set(CMAKE_CXX_COMPILER clang-cl) set(CMAKE_SYSTEM_NAME Windows) -{Makefile._format_platform_compiler_flags(platform)}''' +{platform.makefile_toolchain_file()}''' @staticmethod def setup(platform, build_dir): diff --git a/project/configuration.py b/project/configuration.py index 4b25c6e..5bfc317 100644 --- a/project/configuration.py +++ b/project/configuration.py @@ -29,7 +29,7 @@ class Configuration(Enum): except ValueError as e: raise argparse.ArgumentTypeError(f'invalid configuration: {s}') from e - def to_boost_variant(self): + def variant(self): '''Roughly maps CMake's CMAKE_BUILD_TYPE to Boost's variant. AFAIK, Boost only supports debug/release, MinSizeRel and RelWithDebInfo @@ -38,5 +38,25 @@ class Configuration(Enum): MinSizeRel/RelWithDebInfo. ''' if self in (Configuration.MINSIZEREL, Configuration.RELWITHDEBINFO): - return Configuration.RELEASE.to_boost_variant() + return Configuration.RELEASE.variant() return str(self).lower() + + def b2_variant(self): + return [f'variant={self.variant()}'] + + def b2_args(self): + args = [] + args += self.b2_variant() + return args + + def build_type(self): + '''Maps to CMAKE_BUILD_TYPE.''' + return str(self) + + def cmake_build_type(self): + return ['-D', f'CMAKE_BUILD_TYPE={self.build_type()}'] + + def cmake_args(self): + args = [] + args += self.cmake_build_type() + return args diff --git a/project/mingw.py b/project/mingw.py index 731cee9..4ac921f 100644 --- a/project/mingw.py +++ b/project/mingw.py @@ -4,36 +4,24 @@ # Distributed under the MIT License. -def _get_compiler_prefix(platform): - target_arch = platform.get_address_model() - if target_arch == 32: - return 'i686' - if target_arch == 64: - return 'x86_64' - raise RuntimeError(f'unexpected address model: {target_arch}') +class MinGW: + def __init__(self, platform): + self.prefix = platform.mingw_prefix() + def _get(self, what): + return f'{self.prefix}-w64-mingw32-{what}' -def _get(platform, what): - prefix = _get_compiler_prefix(platform) - path = f'{prefix}-w64-mingw32-{what}' - return path + def gcc(self): + return self._get('gcc') + def gxx(self): + return self._get('g++') -def get_gcc(platform): - return _get(platform, 'gcc') + def ar(self): + return self._get('gcc-ar') + def ranlib(self): + return self._get('gcc-ranlib') -def get_gxx(platform): - return _get(platform, 'g++') - - -def get_ar(platform): - return _get(platform, 'gcc-ar') - - -def get_ranlib(platform): - return _get(platform, 'gcc-ranlib') - - -def get_windres(platform): - return _get(platform, 'windres') + def windres(self): + return self._get('windres') diff --git a/project/platform.py b/project/platform.py index d25827b..b19c93a 100644 --- a/project/platform.py +++ b/project/platform.py @@ -6,19 +6,29 @@ import argparse from enum import Enum import platform +import os.path +from project.os import on_windows -class Platform(Enum): - '''I only build for x86(-64), so here it goes.''' +class Platform(Enum): + # I only build for x86(-64), so here it goes. X86 = 'x86' X64 = 'x64' + # 'auto' means that no additional arguments will be passed to either + # Boost's b2 nor CMake (except on Windows, see below). + AUTO = 'auto' def __str__(self): return str(self.value) @staticmethod - def native(): + def windows_native(): + # On Windows, no explicit platform would mean x64 for VS 2019 and x86 + # for VS 2017. To account for this discrepancy, it is assumed that + # Windows builds can only target either x86 or x64 (which I don't think + # is true?), and we default to x64 most of the time. + # # Source: https://stackoverflow.com/a/12578715/514684 if platform.machine().endswith('64'): return Platform.X64 @@ -26,30 +36,111 @@ class Platform(Enum): @staticmethod def all(): - return tuple(Platform) + return Platform.X86, Platform.X64, @staticmethod def parse(s): try: if s == 'Win32': - # AppVeyor convention: + # Visual Studio/AppVeyor convention: return Platform.X86 return Platform(s) except ValueError as e: raise argparse.ArgumentTypeError(f'invalid platform: {s}') from e - def get_address_model(self): + def mingw_prefix(self): + if self is Platform.AUTO: + if on_windows(): + # On Windows, use the host architecture. + return Platform.windows_native().mingw_prefix() + # On Linux, assume that the target is x64. + return Platform.X64.mingw_prefix() + if self is Platform.X86: + return 'i686' + if self is Platform.X64: + return 'x86_64' + raise NotImplementedError(f'unsupported platform: {self}') + + def address_model(self): '''Maps to Boost's address-model.''' + if self is Platform.AUTO: + if on_windows(): + # On Windows, use the host architecture. + return Platform.windows_native().address_model() + # On Linux, assume that the target is x64. + raise RuntimeError('cannot determine address model unless the target platform is specified explicitly') if self is Platform.X86: return 32 if self is Platform.X64: return 64 raise NotImplementedError(f'unsupported platform: {self}') - def get_cmake_arch(self): + def stagedir(self, configuration): + '''Path to the built libraries inside the Boost build directory.''' + if self is Platform.AUTO: + if on_windows(): + # On Windows, use the host architecture. + return Platform.windows_native().stagedir(configuration) + # On Linux, the libraries are stored in stage/auto/CONFIGURATION/lib. + return os.path.join('stage', str(self), str(configuration)) + + def boost_librarydir(self, configuration): + '''Same as above, but for CMake; adds /lib/ at the end.''' + return os.path.join(self.stagedir(configuration), 'lib') + + def b2_address_model(self): + if self is Platform.AUTO and not on_windows(): + # On Linux, don't specify the architecture explicitly (it is + # assumed that the host architecture will be targeted). + return [] + return [f'address-model={self.address_model()}'] + + def b2_stagedir(self, configuration): + return [f'--stagedir={self.stagedir(configuration)}'] + + def b2_args(self, configuration): + args = [] + args += self.b2_address_model() + args += self.b2_stagedir(configuration) + return args + + def makefile_toolchain_file(self): + # For Makefile generators, we make a special toolchain file that + # specifies the -m32/-m64 flags, etc. + if self is Platform.AUTO: + # Let the compiler decide. + return '' + if self is Platform.X86: + address_model = 32 + elif self is Platform.X64: + address_model = 64 + else: + raise NotImplementedError(f'unsupported platform: {self}') + return f''' +set(CMAKE_C_FLAGS -m{address_model}) +set(CMAKE_CXX_FLAGS -m{address_model}) +''' + + def msvc_arch(self): '''Maps to CMake's -A argument for MSVC.''' + if self is Platform.AUTO: + if on_windows(): + # On Windows, use the host architecture. + return Platform.windows_native().msvc_arch() + # I don't think the -A argument is supported on any generators + # except the Visual Studio ones. + raise RuntimeError('-A parameter is only supported for Visual Studio generators') if self is Platform.X86: return 'Win32' if self is Platform.X64: return 'x64' raise NotImplementedError(f'unsupported platform: {self}') + + def cmake_msvc_arch(self): + return ['-A', self.msvc_arch()] + + def cmake_msvc_args(self): + # When using the MSVC toolset, pass the appropriate -A flag. + args = [] + args += self.cmake_msvc_arch() + return args |