aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorEgor Tensin <Egor.Tensin@gmail.com>2020-03-30 00:13:20 +0300
committerEgor Tensin <Egor.Tensin@gmail.com>2020-03-30 00:53:14 +0300
commit8a8941b853ba5ccecc65cf55222c6c129e92d675 (patch)
treed3a251cdfe4be68914f43103876d908930314bb4
parentproject.boost: first-class MinGW-w64 support (diff)
downloadcmake-common-8a8941b853ba5ccecc65cf55222c6c129e92d675.tar.gz
cmake-common-8a8941b853ba5ccecc65cf55222c6c129e92d675.zip
project: minor-ish refactoring
-rw-r--r--project/boost/archive.py16
-rw-r--r--project/boost/build.py4
-rw-r--r--project/boost/toolchain.py119
-rw-r--r--project/cmake/build.py8
-rw-r--r--project/os.py8
-rw-r--r--project/platform.py22
-rw-r--r--project/utils.py35
7 files changed, 122 insertions, 90 deletions
diff --git a/project/boost/archive.py b/project/boost/archive.py
index e4c85e0..5316005 100644
--- a/project/boost/archive.py
+++ b/project/boost/archive.py
@@ -8,9 +8,9 @@ from contextlib import contextmanager
import logging
import os.path
import shutil
-import tempfile
from project.boost.directory import BoostDir
+from project.utils import temp_file
class Archive:
@@ -75,15 +75,7 @@ class TemporaryStorage(ArchiveStorage):
@contextmanager
def write_archive(self, version, contents):
- temp = tempfile.NamedTemporaryFile(prefix=f'boost_{version}_',
- suffix=version.archive_ext,
- dir=self._dir, delete=False)
- with temp as dest:
- path = dest.name
- logging.info('Writing Boost archive: %s', path)
- dest.write(contents)
- try:
+ tmp = temp_file(contents, prefix=f'boost_{version}_',
+ suffix=version.archive_ext, dir=self._dir)
+ with tmp as path:
yield path
- finally:
- logging.info('Removing temporary Boost archive: %s', path)
- os.remove(path)
diff --git a/project/boost/build.py b/project/boost/build.py
index c9081eb..6d2b897 100644
--- a/project/boost/build.py
+++ b/project/boost/build.py
@@ -22,7 +22,7 @@ import sys
import tempfile
from project.boost.directory import BoostDir
-from project.boost.toolchain import detect_toolchain
+from project.boost.toolchain import Toolchain
from project.configuration import Configuration
from project.linkage import Linkage
from project.platform import Platform
@@ -71,7 +71,7 @@ class BuildParameters:
def enum_b2_args(self):
with self._create_build_dir() as build_dir:
for platform in self.platforms:
- with detect_toolchain(platform, mingw=self.mingw) as toolchain:
+ with Toolchain.detect(platform, mingw=self.mingw) as toolchain:
for configuration in self.configurations:
for link, runtime_link in self._linkage_options():
yield self._build_params(build_dir, toolchain,
diff --git a/project/boost/toolchain.py b/project/boost/toolchain.py
index 9cc452b..cacc2c2 100644
--- a/project/boost/toolchain.py
+++ b/project/boost/toolchain.py
@@ -17,9 +17,9 @@ parameter.
import abc
from contextlib import contextmanager
import logging
-import tempfile
import project.os
+from project.utils import temp_file
class Toolchain(abc.ABC):
@@ -30,13 +30,28 @@ class Toolchain(abc.ABC):
def get_b2_args(self):
pass
+ @staticmethod
+ @contextmanager
+ def detect(platform, mingw=False):
+ if mingw:
+ with MinGW.setup(platform) as toolchain:
+ yield toolchain
+ else:
+ yield Native(platform)
-class NativeToolchain(Toolchain):
+ @staticmethod
+ def _format_user_config(tag, compiler, **kwargs):
+ features = (f'<{k}>{v}' for k, v in kwargs.items())
+ features = ' '.join(features)
+ return f'using gcc : {tag} : {compiler} : {features} ;'
+
+
+class Native(Toolchain):
def get_b2_args(self):
return [f'address-model={self.platform.get_address_model()}']
-class MingwToolchain(Toolchain):
+class MinGW(Toolchain):
TAG = 'custom'
def __init__(self, platform, config_path):
@@ -44,65 +59,41 @@ class MingwToolchain(Toolchain):
self.config_path = config_path
def get_b2_args(self):
- return [f'--user-config={self.config_path}', f'toolset=gcc-{MingwToolchain.TAG}']
-
-
-def _native_toolchain(platform):
- return NativeToolchain(platform)
-
-
-def _get_mingw_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}')
-
-
-def _get_mingw_path(platform):
- prefix = _get_mingw_prefix(platform)
- ext = ''
- if project.os.on_windows_like():
- # Boost.Build wants the .exe extension at the end on Cygwin.
- ext = '.exe'
- path = f'{prefix}-w64-mingw32-g++{ext}'
- return path
-
-
-def _format_user_config(tag, compiler, **kwargs):
- features = (f'<{k}>{v}' for k, v in kwargs.items())
- features = ' '.join(features)
- return f'using gcc : {tag} : {compiler} : {features} ;'
-
-
-def _format_mingw_user_config(platform):
- compiler = _get_mingw_path(platform)
- features = {
- 'target-os': 'windows',
- 'address-model': platform.get_address_model(),
- }
- return _format_user_config(MingwToolchain.TAG, compiler, **features)
-
-
-@contextmanager
-def _mingw_toolchain(platform):
- tmp = tempfile.NamedTemporaryFile(mode='w', prefix='mingw_w64_', suffix='.jam')
- with tmp as file:
- config = _format_mingw_user_config(platform)
+ return [f'--user-config={self.config_path}', f'toolset=gcc-{MinGW.TAG}']
+
+ @staticmethod
+ 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}')
+
+ @staticmethod
+ def _get_compiler_path(platform):
+ prefix = MinGW._get_compiler_prefix(platform)
+ ext = ''
+ if project.os.on_windows_like():
+ # Boost.Build wants the .exe extension at the end on Cygwin.
+ ext = '.exe'
+ path = f'{prefix}-w64-mingw32-g++{ext}'
+ return path
+
+ @staticmethod
+ def _format_mingw_user_config(platform):
+ compiler = MinGW._get_compiler_path(platform)
+ features = {
+ 'target-os': 'windows',
+ 'address-model': platform.get_address_model(),
+ }
+ return Toolchain._format_user_config(MinGW.TAG, compiler, **features)
+
+ @staticmethod
+ @contextmanager
+ def setup(platform):
+ config = MinGW._format_mingw_user_config(platform)
logging.info('Using user config:\n%s', config)
- file.write(config)
- file.flush()
- try:
- yield MingwToolchain(platform, file.name)
- finally:
- logging.info('Removing temporary user config file')
-
-
-@contextmanager
-def detect_toolchain(platform, mingw=False):
- if mingw:
- with _mingw_toolchain(platform) as toolchain:
- yield toolchain
- else:
- yield _native_toolchain(platform)
+ tmp = temp_file(config, mode='w', prefix='mingw_w64_', suffix='.jam')
+ with tmp as path:
+ yield MinGW(platform, path)
diff --git a/project/cmake/build.py b/project/cmake/build.py
index 5355c0b..df7b764 100644
--- a/project/cmake/build.py
+++ b/project/cmake/build.py
@@ -45,6 +45,10 @@ from project.configuration import Configuration
import project.utils
+def run_cmake(cmake_args):
+ return project.utils.run(['cmake'] + cmake_args)
+
+
class GenerationPhase:
def __init__(self, build_dir, params):
self.build_dir = build_dir
@@ -66,7 +70,7 @@ class GenerationPhase:
return result
def run(self):
- project.utils.run_cmake(self._cmake_args())
+ run_cmake(self._cmake_args())
class BuildPhase:
@@ -87,7 +91,7 @@ class BuildPhase:
return result
def run(self):
- project.utils.run_cmake(self._cmake_args())
+ run_cmake(self._cmake_args())
class BuildParameters:
diff --git a/project/os.py b/project/os.py
index 2f5f088..53b64e1 100644
--- a/project/os.py
+++ b/project/os.py
@@ -36,6 +36,14 @@ def on_windows_like():
return os is OS.WINDOWS or os is OS.CYGWIN
+def on_linux():
+ return OS.current() is OS.LINUX
+
+
def on_linux_like():
os = OS.current()
return os is OS.LINUX or os is OS.CYGWIN
+
+
+def on_cygwin():
+ return OS.current() is OS.CYGWIN
diff --git a/project/platform.py b/project/platform.py
index 63f6231..249238e 100644
--- a/project/platform.py
+++ b/project/platform.py
@@ -5,37 +5,43 @@
import argparse
from enum import Enum
+import platform
class Platform(Enum):
- '''I only build for x86(-64), so here it goes.
-
- Win32 is just Visual Studio convention, it's effectively an alias for x86.
- '''
+ '''I only build for x86(-64), so here it goes.'''
X86 = 'x86'
X64 = 'x64'
- WIN32 = 'Win32'
def __str__(self):
return self.value
@staticmethod
+ def native():
+ # Source: https://stackoverflow.com/a/12578715/514684
+ if platform.machine().endswith('64'):
+ return Platform.X64
+ return Platform.X86
+
+ @staticmethod
def all():
- return (Platform.X86, Platform.X64)
+ return tuple(Platform)
@staticmethod
def parse(s):
try:
+ if s == 'Win32':
+ # AppVeyor convention:
+ return Platform.X86
return Platform(s)
except ValueError:
raise argparse.ArgumentTypeError(f'invalid platform: {s}')
def get_address_model(self):
+ '''Maps to Boost's address-model.'''
if self is Platform.X86:
return 32
if self is Platform.X64:
return 64
- if self is Platform.WIN32:
- return 32
raise NotImplementedError(f'unsupported platform: {self}')
diff --git a/project/utils.py b/project/utils.py
index 8c57f59..68327af 100644
--- a/project/utils.py
+++ b/project/utils.py
@@ -7,6 +7,7 @@ from contextlib import contextmanager
import logging
import os.path
import subprocess
+import tempfile
def normalize_path(s):
@@ -41,5 +42,35 @@ def run(cmd_line):
return subprocess.run(cmd_line, check=True)
-def run_cmake(cmake_args):
- return run(['cmake'] + cmake_args)
+@contextmanager
+def delete_on_error(path):
+ try:
+ yield
+ except:
+ logging.info('Removing temporary file: %s', path)
+ os.remove(path)
+ raise
+
+
+@contextmanager
+def delete(path):
+ try:
+ yield
+ finally:
+ logging.info('Removing temporary file: %s', path)
+ os.remove(path)
+
+
+@contextmanager
+def temp_file(contents, **kwargs):
+ '''Make NamedTemporaryFile usable on Windows.
+
+ It can't be opened a second time on Windows, hence this silliness.
+ '''
+ tmp = tempfile.NamedTemporaryFile(delete=False, **kwargs)
+ with tmp as file, delete_on_error(file.name):
+ path = file.name
+ logging.info('Writing temporary file: %s', path)
+ file.write(contents)
+ with delete(path):
+ yield path