aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--project/boost/build.py15
-rw-r--r--project/boost/toolchain.py41
-rw-r--r--project/cmake/build.py16
-rw-r--r--project/cmake/toolchain.py37
-rw-r--r--project/configuration.py24
-rw-r--r--project/mingw.py42
-rw-r--r--project/platform.py105
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