瀏覽代碼

More versatile build options and controls

default_compile_flags
vector-of-bool 5 年之前
父節點
當前提交
a7d6a094f9
共有 3 個文件被更改,包括 111 次插入51 次删除
  1. +2
    -1
      .gitignore
  2. +9
    -2
      azure-pipelines.yml
  3. +100
    -48
      build.py

+ 2
- 1
.gitignore 查看文件

@@ -1,4 +1,5 @@
_build/
__pycache__/
.peru/
.vscode/
.vscode/
.mypy_cache/

+ 9
- 2
azure-pipelines.yml 查看文件

@@ -9,8 +9,10 @@ jobs:
echo Loading VS environment
call "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise\\Common7\\Tools\\vsdevcmd" -arch=x64 || exit 1
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
- publish: _build/ddslim.exe
artifact: DDSLiM Executable - Windows

- job: Linux_GCC
pool:
@@ -19,8 +21,10 @@ jobs:
- script: |
set -eu
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
- publish: _build/ddslim
artifact: DDSLiM Executable - Linux

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

+ 100
- 48
build.py 查看文件

@@ -6,7 +6,7 @@ from pathlib import Path
import multiprocessing
import itertools
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 time
import sys
@@ -20,17 +20,32 @@ INCLUDE_DIRS = [
]


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:
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 = [
str(cxx),
str(opts.cxx),
f'-I{HERE_DIR / "src"}',
'-std=c++17',
'-static',
'-Wall',
'-Wextra',
'-Werror',
@@ -38,28 +53,26 @@ def _create_compile_command(cxx: Path, cpp_file: Path, obj_file: Path) -> List[s
'-Wconversion',
'-fdiagnostics-color',
'-pthread',
'-g',
'-c',
'-O0',
str(cpp_file),
f'-o{obj_file}',
]
if opts.static:
cmd.append('-static')
if opts.debug:
cmd.extend(('-g', '-O0'))
else:
cmd.append('-O2')
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
else:
cmd = [
str(cxx),
# '/O2',
'/Od',
'/Z7',
'/DEBUG',
str(opts.cxx),
'/W4',
'/WX',
'/MT',
'/nologo',
# '/wd2220',
'/EHsc',
'/std:c++latest',
f'/I{HERE_DIR / "src"}',
@@ -67,18 +80,25 @@ def _create_compile_command(cxx: Path, cpp_file: Path, obj_file: Path) -> List[s
'/c',
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


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'
src_dir = HERE_DIR / 'src'
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)
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}'
print(msg)
start = time.time()
@@ -98,27 +118,29 @@ def _compile_src(cxx: Path, cpp_file: Path) -> Tuple[Path, 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 {
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'
cmd = ['lib', '/nologo', f'/OUT:{lib_file}', *map(str, objects)]
return lib_file, cmd
else:
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


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():
lib_file.unlink()
print(f'Creating static library {lib_file}')
@@ -126,24 +148,29 @@ def make_library(objects: Iterable[Path]) -> Path:
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',
# 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(lib),
'-lstdc++fs',
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:
return [
str(cxx),
cmd = [
str(opts.cxx),
'/nologo',
'/W4',
'/WX',
@@ -154,16 +181,24 @@ def _create_exe_link_command(cxx: Path, obj: Path, lib: Path, out: Path) -> List
str(lib),
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:
basename = obj.stem
out = HERE_DIR / '_build/test' / (basename + '.exe')
out.parent.mkdir(exist_ok=True, parents=True)

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)
return out

@@ -179,7 +214,18 @@ def main(argv: Sequence[str]) -> int:
'--test', action='store_true', help='Build and run tests')
parser.add_argument(
'--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)

all_sources = set(HERE_DIR.glob('src/**/*.cpp'))
@@ -188,17 +234,23 @@ def main(argv: Sequence[str]) -> int:

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)
pool = ThreadPoolExecutor(args.jobs)
pool = ThreadPoolExecutor(build_opts.jobs)
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(
Path(args.cxx),
build_opts,
objects[next(iter(main_sources))],
lib,
out=HERE_DIR / '_build/ddslim')

Loading…
取消
儲存