diff --git a/conan/profile/x86_64-clang-linux-release b/conan/profile/x86_64-clang-linux-release new file mode 100644 index 0000000..384c5af --- /dev/null +++ b/conan/profile/x86_64-clang-linux-release @@ -0,0 +1,17 @@ +[settings] +os=Linux +arch=x86_64 + +compiler=clang +compiler.version=10 +compiler.libcxx=libc++ + +build_type=Release + +[options] + +[build_requires] + +[env] +CC=clang-10 +CXX=clang++-10 diff --git a/conan/profile/x86_64-gcc-linux-debug b/conan/profile/x86_64-gcc-linux-debug new file mode 100644 index 0000000..8dfa395 --- /dev/null +++ b/conan/profile/x86_64-gcc-linux-debug @@ -0,0 +1,17 @@ +[settings] +os=Linux +arch=x86_64 + +compiler=gcc +compiler.version=10.2 +compiler.libcxx=libstdc++11 + +build_type=Debug + +[options] + +[build_requires] + +[env] +CC=gcc +CXX=g++ diff --git a/conan/profile/x86_64-gcc-linux-release b/conan/profile/x86_64-gcc-linux-release new file mode 100644 index 0000000..fe0a551 --- /dev/null +++ b/conan/profile/x86_64-gcc-linux-release @@ -0,0 +1,17 @@ +[settings] +os=Linux +arch=x86_64 + +compiler=gcc +compiler.version=10.2 +compiler.libcxx=libstdc++11 + +build_type=Release + +[options] + +[build_requires] + +[env] +CC=gcc +CXX=g++ diff --git a/conan/profile/x86_64-gcc-mingw-release b/conan/profile/x86_64-gcc-mingw-release new file mode 100644 index 0000000..d94b83f --- /dev/null +++ b/conan/profile/x86_64-gcc-mingw-release @@ -0,0 +1,24 @@ +toolchain=/usr/x86_64-w64-mingw32 +target_host=x86_64-w64-mingw32 +cc_compiler=gcc +cxx_compiler=g++ + +[env] +CONAN_CMAKE_FIND_ROOT_PATH=$toolchain +CHOST=$target_host +AR=$target_host-ar +AS=$target_host-as +RANLIB=$target_host-ranlib +CC=$target_host-$cc_compiler +CXX=$target_host-$cxx_compiler +STRIP=$target_host-strip +RC=$target_host-windres + +[settings] +os=Windows +arch=x86_64 +compiler=gcc + +compiler.version=10.1 +compiler.libcxx=libstdc++11 +build_type=Release diff --git a/init.py b/init.py index aa972b6..e506f15 100755 --- a/init.py +++ b/init.py @@ -4,55 +4,174 @@ import sys import os import itertools import multiprocessing +from dataclasses import dataclass, field -OPTIONS = { - # compiler - 'gcc': {'vars': {'CMAKE_CXX_COMPILER': 'g++', 'CMAKE_C_COMPILER': 'gcc'}}, - 'clang': {'vars': {'CMAKE_CXX_COMPILER': 'clang++-10', 'CMAKE_C_COMPILER': 'clang-10'}}, +from typing import Dict, List, Optional - # profile - 'debug': { - 'vars': {'CMAKE_BUILD_TYPE': 'Debug', 'LTO': 'OFF'}, - #'args': ['-G', '"Unix Makefiles"'], - #'build': [ "make", f"-j{multiprocessing.cpu_count()}" ], +CLANG_VERSION = 10 + + +@dataclass +class Option: + vars: Dict[str, str] = field(default_factory=dict) + compiler: str = None + config: str = None + require: List[str] = field(default_factory=list) + args: List[str] = field(default_factory=list) + build: List[str] = field(default_factory=list) + platform: str = None + arch: str = None + + +OPTIONS = dict() + + +def merge_option(a: Option, b: Option) -> Option: + + res = Option( + vars={**a.vars, **b.vars}, + require=a.require+b.require, + ) + + replace = ( + 'args', 'build', 'platform', 'compiler', 'config', 'arch', + ) + + for f in replace: + setattr( + res, + f, + getattr(b, f, None) or getattr(a, f, None) + ) + + return res + + +############################################################################### +OPTIONS['gcc'] = Option( + vars={ + 'CMAKE_CXX_COMPILER': 'g++', + 'CMAKE_C_COMPILER': 'gcc' }, - 'release': {'vars': {'CMAKE_BUILD_TYPE': 'Release', 'LTO': 'ON'}}, + compiler='gcc', +) - # sanitizer - 'sanitizer': {'vars': {'SANITIZER': 'ON'}, 'require': {'PROFILE': 'release'}}, +OPTIONS['clang'] = Option( + vars={ + 'CMAKE_CXX_COMPILER': f'clang++-{CLANG_VERSION}', + 'CMAKE_C_COMPILER': f'clang-{CLANG_VERSION}', + #'CMAKE_CXX_FLAGS': '-fuse-ld=lld', + }, + compiler='clang', +) - # wrappers - 'iwyu': { 'vars': { 'CMAKE_CXX_INCLUDE_WHAT_YOU_USE': 'include-what-you-use' } }, - 'tidy': { 'vars': { 'CMAKE_CXX_CLANG_TIDY': ';'.join(['clang-tidy', '--quiet', '-checks=bugprone-*,clang-analyzer-*,performance-*,portability-*,readability-,', '-warnings-as-errors=' ]) } }, - # build tools - 'make': { - 'args': [ '-G', '"Unix Makefiles"' ], - 'build': [ "make", f"-j{multiprocessing.cpu_count()}" ], +# ----------------------------------------------------------------------------- +OPTIONS['debug'] = Option( + vars={ + 'CMAKE_BUILD_TYPE': 'Debug', + 'LTO': 'OFF' + }, + config='debug', + #'args': ['-G', '"Unix Makefiles"'], + #'build': [ "make", f"-j{multiprocessing.cpu_count()}" ], +) + + +OPTIONS['release'] = Option( + vars={ + 'CMAKE_BUILD_TYPE': 'Release', + 'LTO': 'ON' + }, + config='release', +) + + +OPTIONS['sanitizer'] = Option( + vars={ + 'SANITIZER': 'ON', + }, + require=[ + 'release' + ], +) + + +# ----------------------------------------------------------------------------- +OPTIONS['iwyu'] = Option( + vars={ + 'CMAKE_CXX_INCLUDE_WHAT_YOU_USE': 'include-what-you-use' + } +) + + +TIDY_CHECKS=[ + 'bugprone-*', + 'clang-analyzer-*', + 'performance-*', + 'portability-*', + 'readability-', +] + + +OPTIONS['tidy'] = Option( + vars={ + 'CMAKE_CXX_CLANG_TIDY': ';'.join([ + 'clang-tidy', + '--quiet', + '-checks=' + ','.join(TIDY_CHECKS), + '-warnings-as-errors=', + ]) + } +) + + +# ----------------------------------------------------------------------------- +OPTIONS['make'] = Option( + args=[ '-G', '"Unix Makefiles"' ], + build=[ "make", f"-j{multiprocessing.cpu_count()}" ] +) + + +OPTIONS['ninja'] = Option( + args=['-G', 'Ninja'], + build=['ninja'] +) + + +# ----------------------------------------------------------------------------- +OPTIONS['mingw'] = Option( + vars={ + 'CMAKE_TOOLCHAIN_FILE': os.path.realpath( + os.path.join( + os.path.dirname(__file__), + 'mingw.toolchain') + ) + }, + compiler='gcc', + platform='mingw', +) + + +# ----------------------------------------------------------------------------- +DEFAULT_OPTION = Option( + vars={ + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", + "TESTS": "ON", }, - 'ninja': { - 'args': [ '-G', 'Ninja' ], - 'build': [ 'ninja' ] - }, + args=['-G', 'Ninja'], - # platforms - 'mingw': { 'vars': { 'CMAKE_TOOLCHAIN_FILE': os.path.realpath(os.path.join(os.path.dirname(__file__), 'mingw.toolchain')) }, }, + build=[ 'ninja' ], - # Default parameters - '': { - 'vars': { - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", - "TESTS": "ON", - }, - - 'args': ['-G', 'Ninja'], - - 'build': [ 'ninja' ], - }, -} + platform='linux', + compiler='gcc', + config='release', + arch='x86_64', +) +# ############################################################################# def split_all_path(path): parts = [] @@ -62,34 +181,60 @@ def split_all_path(path): path = head +# ############################################################################# if __name__ == '__main__': + # Discover where we are, and where the source and build directories are self_path = os.path.realpath(__file__) self_dir = os.path.dirname(self_path) build_dir = os.path.realpath(os.getcwd()) - source_dir = os.path.dirname(self_dir) + # Build a list of the OPTION keys we're going to combine accumulated = [] - components = [''] + components = [] components += sys.argv[1:] components += itertools.takewhile(lambda x: x in OPTIONS, split_all_path(os.getcwd())) - build = [ 'true' ] + # Fold in the dependencies of each OPTION + final_components = [] + for i in components: + req = OPTIONS[i].require + if req is not None: + final_components.extend(req) + final_components.append(i) + components = final_components - for key in components: - obj = OPTIONS[key] + build = ['true'] - vars = obj.get('vars', {}) - args = obj.get('args', []) - build = obj.get('build', build) + accum = DEFAULT_OPTION + for i in components: + accum = merge_option(accum, OPTIONS[i]) + + accumulated = [f'"-D{key}={val}"' for key, val in accum.vars.items()] + accum.args + + conan_host_profile = '-'.join([ + accum.arch, + accum.compiler, + accum.platform, + accum.config, + ]) + + conan_build_profile = conan_host_profile + if accum.platform == 'mingw': + conan_build_profile = f'x86_64-gcc-linux-{accum.config}' + + profile_dir = os.path.join(source_dir, 'build', 'conan', 'profile') + prh = os.path.join(profile_dir, conan_host_profile) + prb = os.path.join(profile_dir, conan_build_profile) - accumulated += [f'"-D{key}={val}"' for key, val in vars.items()] - accumulated += args cmds = [ + "[[ -v VIRTUAL_ENV ]]", + f"conan install {source_dir} -pr:h {prh} -pr:b {prb} --build=missing", + f"conan install {source_dir} -pr:h {prh} -pr:b {prb} --build=missing -g deploy -if deps/", f"cmake {source_dir} {' '.join(accumulated)}", - ' '.join(build) + ' '.join(accum.build) ] print(" && ".join(cmds)) diff --git a/wrapper.py.in b/wrapper.py.in index 0424d94..13817ee 100755 --- a/wrapper.py.in +++ b/wrapper.py.in @@ -111,7 +111,7 @@ def break_path_for_msys2(path): # to ever deal with... if is_really_windows(): separator = ';' - depsdir = [ 'bin', 'lib'] + depsdir = ['bin'] # Add the likely path to runtime dependencies for Linux/mingw. #