Browse Source

More versatile build options and controls

default_compile_flags
vector-of-bool 5 years ago
parent
commit
a7d6a094f9
3 changed files with 111 additions and 51 deletions
  1. +2
    -1
      .gitignore
  2. +9
    -2
      azure-pipelines.yml
  3. +100
    -48
      build.py

+ 2
- 1
.gitignore View File

_build/ _build/
__pycache__/ __pycache__/
.peru/ .peru/
.vscode/
.vscode/
.mypy_cache/

+ 9
- 2
azure-pipelines.yml View File

echo Loading VS environment echo Loading VS environment
call "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise\\Common7\\Tools\\vsdevcmd" -arch=x64 || exit 1 call "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise\\Common7\\Tools\\vsdevcmd" -arch=x64 || exit 1
echo Executing build/test script echo Executing build/test script
python -u build.py --cxx cl.exe --test || exit 1
python -u build.py --cxx cl.exe --test --static || exit 1
displayName: Build and Run Tests displayName: Build and Run Tests
- publish: _build/ddslim.exe
artifact: DDSLiM Executable - Windows


- job: Linux_GCC - job: Linux_GCC
pool: pool:
- script: | - script: |
set -eu set -eu
sudo apt install -y python3-minimal g++-8 sudo apt install -y python3-minimal g++-8
python3 -u build.py --cxx g++-8 --test
python3 -u build.py --cxx g++-8 --test --static
displayName: Build and Run Tests displayName: Build and Run Tests
- publish: _build/ddslim
artifact: DDSLiM Executable - Linux


- job: macOS - job: macOS
pool: pool:
set -eu set -eu
brew install gcc@8 brew install gcc@8
python3 -u build.py --cxx g++-8 --test python3 -u build.py --cxx g++-8 --test
displayName: Build and Run Tests
- publish: _build/ddslim
artifact: DDSLiM Executable - macOS

+ 100
- 48
build.py View File

import multiprocessing import multiprocessing
import itertools import itertools
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from typing import Sequence, Iterable, Dict, Tuple,List
from typing import Sequence, Iterable, Dict, Tuple, List, NamedTuple
import subprocess import subprocess
import time import time
import sys import sys
] ]




class BuildOptions(NamedTuple):
cxx: Path
jobs: int
static: bool
debug: bool

@property
def is_msvc(self) -> bool:
return is_msvc(self.cxx)

@property
def obj_suffix(self) -> str:
return '.obj' if self.is_msvc else '.o'


def is_msvc(cxx: Path) -> bool: def is_msvc(cxx: Path) -> bool:
return (not 'clang' in cxx.name) and 'cl' in cxx.name return (not 'clang' in cxx.name) and 'cl' in cxx.name




def _create_compile_command(cxx: Path, cpp_file: Path, obj_file: Path) -> List[str]:
if not is_msvc(cxx):
def _create_compile_command(opts: BuildOptions, cpp_file: Path,
obj_file: Path) -> List[str]:
if not opts.is_msvc:
cmd = [ cmd = [
str(cxx),
str(opts.cxx),
f'-I{HERE_DIR / "src"}', f'-I{HERE_DIR / "src"}',
'-std=c++17', '-std=c++17',
'-static',
'-Wall', '-Wall',
'-Wextra', '-Wextra',
'-Werror', '-Werror',
'-Wconversion', '-Wconversion',
'-fdiagnostics-color', '-fdiagnostics-color',
'-pthread', '-pthread',
'-g',
'-c', '-c',
'-O0',
str(cpp_file), str(cpp_file),
f'-o{obj_file}', f'-o{obj_file}',
] ]
if opts.static:
cmd.append('-static')
if opts.debug:
cmd.extend(('-g', '-O0'))
else:
cmd.append('-O2')
cmd.extend( cmd.extend(
itertools.chain.from_iterable(
('-isystem', str(HERE_DIR / subdir)) for subdir in INCLUDE_DIRS))
itertools.chain.from_iterable(('-isystem', str(HERE_DIR / subdir))
for subdir in INCLUDE_DIRS))
return cmd return cmd
else: else:
cmd = [ cmd = [
str(cxx),
# '/O2',
'/Od',
'/Z7',
'/DEBUG',
str(opts.cxx),
'/W4', '/W4',
'/WX', '/WX',
'/MT',
'/nologo', '/nologo',
# '/wd2220',
'/EHsc', '/EHsc',
'/std:c++latest', '/std:c++latest',
f'/I{HERE_DIR / "src"}', f'/I{HERE_DIR / "src"}',
'/c', '/c',
f'/Fo{obj_file}', f'/Fo{obj_file}',
] ]
cmd.extend(
f'/I{HERE_DIR / subdir}' for subdir in INCLUDE_DIRS)
if opts.debug:
cmd.extend(('/Od', '/DEBUG', '/Z7'))
else:
cmd.append('/O2')
if opts.static:
cmd.append('/MT')
else:
cmd.append('/MD')
cmd.extend(f'/I{HERE_DIR / subdir}' for subdir in INCLUDE_DIRS)
return cmd return cmd




def _compile_src(cxx: Path, cpp_file: Path) -> Tuple[Path, Path]:
def _compile_src(opts: BuildOptions, cpp_file: Path) -> Tuple[Path, Path]:
build_dir = HERE_DIR / '_build' build_dir = HERE_DIR / '_build'
src_dir = HERE_DIR / 'src' src_dir = HERE_DIR / 'src'
relpath = cpp_file.relative_to(src_dir) relpath = cpp_file.relative_to(src_dir)
obj_path = build_dir / relpath.with_name(relpath.name + ('.obj' if is_msvc(cxx) else '.o'))
obj_path = build_dir / relpath.with_name(relpath.name + opts.obj_suffix)
obj_path.parent.mkdir(exist_ok=True, parents=True) obj_path.parent.mkdir(exist_ok=True, parents=True)
cmd = _create_compile_command(cxx, cpp_file, obj_path)
cmd = _create_compile_command(opts, cpp_file, obj_path)
msg = f'Compile C++ file: {cpp_file}' msg = f'Compile C++ file: {cpp_file}'
print(msg) print(msg)
start = time.time() start = time.time()
return cpp_file, obj_path return cpp_file, obj_path




def compile_sources(cxx: Path, sources: Iterable[Path], *, jobs: int) -> Dict[Path, Path]:
pool = ThreadPoolExecutor(jobs)
def compile_sources(opts: BuildOptions,
sources: Iterable[Path]) -> Dict[Path, Path]:
pool = ThreadPoolExecutor(opts.jobs)
return { return {
src: obj src: obj
for src, obj in pool.map(lambda s: _compile_src(cxx, s), sources)
for src, obj in pool.map(lambda s: _compile_src(opts, s), sources)
} }




def _create_archive_command(objects: Iterable[Path]) -> List[str]:
if os.name == 'nt':
def _create_archive_command(opts: BuildOptions,
objects: Iterable[Path]) -> Tuple[Path, List[str]]:
if opts.is_msvc:
lib_file = HERE_DIR / '_build/libddslim.lib' lib_file = HERE_DIR / '_build/libddslim.lib'
cmd = ['lib', '/nologo', f'/OUT:{lib_file}', *map(str, objects)] cmd = ['lib', '/nologo', f'/OUT:{lib_file}', *map(str, objects)]
return lib_file, cmd return lib_file, cmd
else: else:
lib_file = HERE_DIR / '_build/libddslim.a' lib_file = HERE_DIR / '_build/libddslim.a'
cmd = ['ar', 'rsc', str(lib_file), *objects]
cmd = ['ar', 'rsc', str(lib_file), *map(str, objects)]
return lib_file, cmd return lib_file, cmd




def make_library(objects: Iterable[Path]) -> Path:
lib_file, cmd = _create_archive_command(objects)
def make_library(opts: BuildOptions, objects: Iterable[Path]) -> Path:
lib_file, cmd = _create_archive_command(opts, objects)
if lib_file.exists(): if lib_file.exists():
lib_file.unlink() lib_file.unlink()
print(f'Creating static library {lib_file}') print(f'Creating static library {lib_file}')
return lib_file return lib_file




def _create_exe_link_command(cxx: Path, obj: Path, lib: Path, out: Path) -> List[str]:
if not is_msvc(cxx):
return [
str(cxx),
'-static',
def _create_exe_link_command(opts: BuildOptions, obj: Path, lib: Path,
out: Path) -> List[str]:
if not opts.is_msvc:
cmd = [
str(opts.cxx),
'-pthread', '-pthread',
# See: https://stackoverflow.com/questions/35116327/when-g-static-link-pthread-cause-segmentation-fault-why
'-Wl,--whole-archive',
'-lpthread',
'-Wl,--no-whole-archive',
str(obj), str(obj),
str(lib), str(lib),
'-lstdc++fs', '-lstdc++fs',
f'-o{out}', f'-o{out}',
] ]
if opts.static:
cmd.extend((
'-static',
# See: https://stackoverflow.com/questions/35116327/when-g-static-link-pthread-cause-segmentation-fault-why
'-Wl,--whole-archive',
'-lpthread',
'-Wl,--no-whole-archive',
))
return cmd
else: else:
return [
str(cxx),
cmd = [
str(opts.cxx),
'/nologo', '/nologo',
'/W4', '/W4',
'/WX', '/WX',
str(lib), str(lib),
str(obj), str(obj),
] ]
if opts.debug:
cmd.append('/DEBUG')
if opts.static:
cmd.append('/MT')
else:
cmd.append('/MD')
return cmd




def link_exe(cxx: Path, obj: Path, lib: Path, *, out: Path = None) -> Path:
def link_exe(opts: BuildOptions, obj: Path, lib: Path, *,
out: Path = None) -> Path:
if out is None: if out is None:
basename = obj.stem basename = obj.stem
out = HERE_DIR / '_build/test' / (basename + '.exe') out = HERE_DIR / '_build/test' / (basename + '.exe')
out.parent.mkdir(exist_ok=True, parents=True) out.parent.mkdir(exist_ok=True, parents=True)


print(f'Linking executable {out}') print(f'Linking executable {out}')
cmd = _create_exe_link_command(cxx, obj, lib, out)
cmd = _create_exe_link_command(opts, obj, lib, out)
subprocess.check_call(cmd) subprocess.check_call(cmd)
return out return out


'--test', action='store_true', help='Build and run tests') '--test', action='store_true', help='Build and run tests')
parser.add_argument( parser.add_argument(
'--cxx', help='Path/name of the C++ compiler to use.', required=True) '--cxx', help='Path/name of the C++ compiler to use.', required=True)
parser.add_argument('--jobs', '-j', type=int, help='Set number of parallel build threads', default=multiprocessing.cpu_count() + 2)
parser.add_argument(
'--jobs',
'-j',
type=int,
help='Set number of parallel build threads',
default=multiprocessing.cpu_count() + 2)
parser.add_argument(
'--debug',
action='store_true',
help='Build with debug information and disable optimizations')
parser.add_argument(
'--static', action='store_true', help='Build a static executable')
args = parser.parse_args(argv) args = parser.parse_args(argv)


all_sources = set(HERE_DIR.glob('src/**/*.cpp')) all_sources = set(HERE_DIR.glob('src/**/*.cpp'))


lib_sources = (all_sources - test_sources) - main_sources lib_sources = (all_sources - test_sources) - main_sources


objects = compile_sources(Path(args.cxx), all_sources, jobs=args.jobs)
build_opts = BuildOptions(
cxx=Path(args.cxx),
jobs=args.jobs,
static=args.static,
debug=args.debug)

objects = compile_sources(build_opts, all_sources)


lib = make_library(objects[p] for p in lib_sources)
lib = make_library(build_opts, (objects[p] for p in lib_sources))


test_objs = (objects[p] for p in test_sources) test_objs = (objects[p] for p in test_sources)
pool = ThreadPoolExecutor(args.jobs)
pool = ThreadPoolExecutor(build_opts.jobs)
test_exes = list( test_exes = list(
pool.map(lambda o: link_exe(Path(args.cxx), o, lib), test_objs))
pool.map(lambda o: link_exe(build_opts, o, lib), test_objs))


main_exe = link_exe( main_exe = link_exe(
Path(args.cxx),
build_opts,
objects[next(iter(main_sources))], objects[next(iter(main_sources))],
lib, lib,
out=HERE_DIR / '_build/ddslim') out=HERE_DIR / '_build/ddslim')

Loading…
Cancel
Save