aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/project/cmake/toolset.py
diff options
context:
space:
mode:
Diffstat (limited to 'project/cmake/toolset.py')
-rw-r--r--project/cmake/toolset.py182
1 files changed, 182 insertions, 0 deletions
diff --git a/project/cmake/toolset.py b/project/cmake/toolset.py
new file mode 100644
index 0000000..0bf8e64
--- /dev/null
+++ b/project/cmake/toolset.py
@@ -0,0 +1,182 @@
+# Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+# This file is part of the "cmake-common" project.
+# For details, see https://github.com/egor-tensin/cmake-common.
+# Distributed under the MIT License.
+
+# See docs/cmake.md for a more thorough description of my pain.
+
+import abc
+import os.path
+import shutil
+
+import project.mingw
+from project.os import on_windows
+from project.platform import Platform
+from project.toolset import ToolchainType
+
+
+class Toolchain(abc.ABC):
+ @abc.abstractmethod
+ def cmake_args(self):
+ pass
+
+ @abc.abstractmethod
+ def build_system_args(self):
+ pass
+
+ @staticmethod
+ def detect(hint, platform, build_dir):
+ if hint is ToolchainType.AUTO:
+ if on_windows():
+ # On Windows, 'auto' means 'msvc', and we need to specify the
+ # -A parameter. This might break if none of the Visual Studio
+ # generators are available, but the NMake one is, although I
+ # don't know how this can be possible normally.
+ hint = ToolchainType.MSVC
+ else:
+ # On Linux, if the platform wasn't specified, auto-detect
+ # everything. There's no need to set -mXX flags, etc.
+ if platform is Platform.AUTO:
+ return Auto()
+ # If a specific platform was requested, we might need to set
+ # some CMake/compiler flags, like -m32/-m64.
+ hint = ToolchainType.GCC
+ if hint is ToolchainType.MSVC:
+ return MSVC(platform)
+ if hint is ToolchainType.GCC:
+ return GCC.setup(platform, build_dir)
+ if hint is ToolchainType.MINGW:
+ return MinGW.setup(platform, build_dir)
+ if hint is ToolchainType.CLANG:
+ return Clang.setup(platform, build_dir)
+ if hint is ToolchainType.CLANG_CL:
+ return ClangCL.setup(platform, build_dir)
+ raise NotImplementedError(f'unrecognized toolset: {hint}')
+
+
+class Auto(Toolchain):
+ def cmake_args(self):
+ return []
+
+ def build_system_args(self):
+ return []
+
+
+class MSVC(Auto):
+ def __init__(self, platform):
+ self.platform = platform
+
+ def cmake_args(self):
+ # This doesn't actually specify the generator of course, but I don't
+ # want to implement VS detection logic.
+ return ['-A', self.platform.msvc_arch()]
+
+ def build_system_args(self):
+ return []
+
+
+class Makefile(Toolchain):
+ def __init__(self, path):
+ self.path = path
+
+ @staticmethod
+ def _get_config_path(build_dir):
+ return os.path.join(build_dir, 'custom_toolchain.cmake')
+
+ @staticmethod
+ def _get_makefile_generator():
+ if on_windows():
+ if shutil.which('mingw32-make'):
+ return 'MinGW Makefiles'
+ return 'Unix Makefiles'
+ # On Linux/Cygwin, make all the way:
+ return 'Unix Makefiles'
+
+ @classmethod
+ def write_config(cls, build_dir, contents):
+ path = Makefile._get_config_path(build_dir)
+ with open(path, mode='w') as file:
+ file.write(contents)
+ return cls(path)
+
+ def cmake_args(self):
+ return [
+ '-D', f'CMAKE_TOOLCHAIN_FILE={self.path}',
+ # The Visual Studio generator is the default on Windows, override
+ # it:
+ '-G', self._get_makefile_generator(),
+ ]
+
+ def build_system_args(self):
+ return []
+
+
+class GCC(Makefile):
+ @staticmethod
+ def _format(platform):
+ return f'''
+set(CMAKE_C_COMPILER gcc)
+set(CMAKE_CXX_COMPILER g++)
+{platform.makefile_toolchain_file()}'''
+
+ @staticmethod
+ def setup(platform, build_dir):
+ return GCC.write_config(build_dir, GCC._format(platform))
+
+
+class MinGW(Makefile):
+ @staticmethod
+ def _format(platform):
+ paths = project.mingw.MinGW(platform)
+ return f'''
+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)
+'''
+
+ @staticmethod
+ def setup(platform, build_dir):
+ return MinGW.write_config(build_dir, MinGW._format(platform))
+
+
+class Clang(Makefile):
+ @staticmethod
+ def _format(platform):
+ return f'''
+if(CMAKE_VERSION VERSION_LESS "3.15" AND WIN32)
+ set(CMAKE_C_COMPILER clang-cl)
+ set(CMAKE_CXX_COMPILER clang-cl)
+else()
+ set(CMAKE_C_COMPILER clang)
+ set(CMAKE_CXX_COMPILER clang++)
+endif()
+{platform.makefile_toolchain_file()}'''
+
+ def _get_makefile_generator(self):
+ if on_windows():
+ # MinGW utilities like make might be unavailable, but NMake can
+ # very much be there.
+ if shutil.which('nmake'):
+ return 'NMake Makefiles'
+ return super()._get_makefile_generator()
+
+ @staticmethod
+ def setup(platform, build_dir):
+ return Clang.write_config(build_dir, Clang._format(platform))
+
+
+class ClangCL(Clang):
+ @staticmethod
+ def _format(platform):
+ return f'''
+set(CMAKE_C_COMPILER clang-cl)
+set(CMAKE_CXX_COMPILER clang-cl)
+set(CMAKE_SYSTEM_NAME Windows)
+{platform.makefile_toolchain_file()}'''
+
+ @staticmethod
+ def setup(platform, build_dir):
+ return ClangCL.write_config(build_dir, ClangCL._format(platform))