#!/usr/bin/env python3 import sys import os import itertools import multiprocessing from dataclasses import dataclass, field from typing import Dict, List, Optional CLANG_VERSION = 11 @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' }, compiler='gcc', ) 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', ) # ----------------------------------------------------------------------------- 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.join( os.path.dirname(__file__), 'cmake', 'toolchain', 'mingw' ) }, compiler='gcc', platform='mingw', ) # ----------------------------------------------------------------------------- OPTIONS['pi3'] = Option( vars={ 'CMAKE_TOOLCHAIN_FILE': os.path.join( os.path.dirname(__file__), 'cmake', 'toolchain', 'armv7a-gcc' ) }, arch="armv7", compiler='gcc', platform='linux', ) # ----------------------------------------------------------------------------- DEFAULT_OPTION = Option( 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 = [] while len(path) > 1: head,tail = os.path.split(path) yield tail 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 += sys.argv[1:] components += itertools.takewhile(lambda x: x in OPTIONS, split_all_path(os.getcwd())) # 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 build = ['true'] 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_build_profile = 'x86_64-gcc-linux-release' conan_host_profile = '-'.join([ accum.arch, accum.compiler, accum.platform, accum.config, ]) 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) cmds = [ f"{{ [[ -v VIRTUAL_ENV ]] || source {source_dir}/venv/bin/activate; }}", 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(accum.build) ] print(" && ".join(cmds))