| @@ -12,7 +12,7 @@ jobs: | |||
| echo Executing Build and Tests | |||
| reg add HKLM\SYSTEM\CurrentControlSet\Control\FileSystem /v LongPathsEnabled /t REG_DWORD /d 1 /f || exit 1 | |||
| python -m pip install pytest pytest-xdist || exit 1 | |||
| python -u tools/ci.py -B download --cxx cl.exe -T tools\msvc.dds || exit 1 | |||
| python -u tools/ci.py -B build -T tools\msvc.dds || exit 1 | |||
| displayName: Full CI | |||
| - publish: _build/dds.exe | |||
| artifact: DDS Executable - Windows VS2019 | |||
| @@ -24,10 +24,10 @@ jobs: | |||
| - script: | | |||
| set -eu | |||
| sudo apt update -y | |||
| sudo apt install -y python3-minimal g++-9 ccache | |||
| sudo apt install -y python3-minimal g++-9 g++-8 ccache | |||
| python3 -m pip install pytest pytest-xdist | |||
| displayName: Prepare System | |||
| - script: python3 -u tools/ci.py -B download -T tools/gcc-9.dds | |||
| - script: python3 -u tools/ci.py -B build -T tools/gcc-9.dds | |||
| displayName: Full CI | |||
| - publish: _build/dds | |||
| artifact: DDS Executable - Linux | |||
| @@ -41,7 +41,7 @@ jobs: | |||
| - script: | | |||
| set -eu | |||
| python3 -m pip install pytest pytest-xdist | |||
| python3 -u tools/ci.py -B download -T tools/gcc-9.dds | |||
| python3 -u tools/ci.py -B build -T tools/gcc-9.dds | |||
| displayName: Build and Run Unit Tests | |||
| - publish: _build/dds | |||
| artifact: DDS Executable - macOS | |||
| @@ -2,13 +2,28 @@ import argparse | |||
| from pathlib import Path | |||
| import subprocess | |||
| import os | |||
| from typing import Sequence | |||
| from typing import Sequence, NamedTuple | |||
| import sys | |||
| import shutil | |||
| class BootstrapPhase(NamedTuple): | |||
| ref: str | |||
| nix_compiler: str | |||
| win_compiler: str | |||
| @property | |||
| def platform_compiler(self): | |||
| if os.name == 'nt': | |||
| return self.win_compiler | |||
| else: | |||
| return self.nix_compiler | |||
| BOOTSTRAP_PHASES = [ | |||
| 'bootstrap-p1', | |||
| 'bootstrap-p4', | |||
| BootstrapPhase('bootstrap-p1', 'g++-8', 'cl.exe'), | |||
| BootstrapPhase('bootstrap-p4', 'g++-8', 'cl.exe'), | |||
| BootstrapPhase('bootstrap-p5', 'g++-9', 'cl.exe'), | |||
| ] | |||
| HERE = Path(__file__).parent.absolute() | |||
| @@ -23,40 +38,43 @@ EXE_SUFFIX = '.exe' if os.name == 'nt' else '' | |||
| def _run_quiet(cmd, **kwargs) -> None: | |||
| cmd = [str(s) for s in cmd] | |||
| res = subprocess.run( | |||
| cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs) | |||
| cmd, | |||
| stdout=subprocess.PIPE, | |||
| stderr=subprocess.STDOUT, | |||
| **kwargs, | |||
| ) | |||
| if res.returncode != 0: | |||
| print(f'Subprocess command {cmd} failed ' | |||
| f'[{res.returncode}]:\n{res.stdout.decode()}') | |||
| raise subprocess.CalledProcessError(res.returncode, cmd) | |||
| def _clone_bootstrap_phase(ph: str) -> Path: | |||
| print(f'Clone revision: {ph}') | |||
| bts_dir = BOOTSTRAP_BASE_DIR / ph | |||
| def _clone_bootstrap_phase(ref: str) -> Path: | |||
| print(f'Clone revision: {ref}') | |||
| bts_dir = BOOTSTRAP_BASE_DIR / ref | |||
| if bts_dir.exists(): | |||
| shutil.rmtree(bts_dir) | |||
| _run_quiet([ | |||
| 'git', | |||
| 'clone', | |||
| '--depth=1', | |||
| f'--branch={ph}', | |||
| f'--branch={ref}', | |||
| f'file://{PROJECT_ROOT}', | |||
| bts_dir, | |||
| ]) | |||
| return bts_dir | |||
| def _build_bootstrap_phase(ph: str, bts_dir: Path, | |||
| args: argparse.Namespace) -> None: | |||
| print(f'Build revision: {ph} [This may take a moment]') | |||
| def _build_bootstrap_phase(ph: BootstrapPhase, bts_dir: Path) -> None: | |||
| print(f'Build revision: {ph.ref} [This may take a moment]') | |||
| env = os.environ.copy() | |||
| env['DDS_BOOTSTRAP_PREV_EXE'] = str(PREBUILT_DIR / 'dds') | |||
| env['DDS_BOOTSTRAP_PREV_EXE'] = str(PREBUILT_DIR / F'dds{EXE_SUFFIX}') | |||
| _run_quiet( | |||
| [ | |||
| sys.executable, | |||
| '-u', | |||
| str(bts_dir / 'tools/build.py'), | |||
| f'--cxx={args.cxx}', | |||
| f'--cxx={ph.platform_compiler}', | |||
| ], | |||
| env=env, | |||
| cwd=bts_dir, | |||
| @@ -76,20 +94,16 @@ def _pull_executable(bts_dir: Path) -> Path: | |||
| return dest | |||
| def _run_boot_phase(phase: str, args: argparse.Namespace) -> Path: | |||
| bts_dir = _clone_bootstrap_phase(phase) | |||
| _build_bootstrap_phase(phase, bts_dir, args) | |||
| def _run_boot_phase(phase: BootstrapPhase) -> Path: | |||
| bts_dir = _clone_bootstrap_phase(phase.ref) | |||
| _build_bootstrap_phase(phase, bts_dir) | |||
| return _pull_executable(bts_dir) | |||
| def main(argv: Sequence[str]) -> int: | |||
| parser = argparse.ArgumentParser() | |||
| parser.add_argument( | |||
| '--cxx', help='The C++ compiler to use for the build', required=True) | |||
| args = parser.parse_args(argv) | |||
| for idx, phase in enumerate(BOOTSTRAP_PHASES): | |||
| print(f'Bootstrap phase [{idx+1}/{len(BOOTSTRAP_PHASES)}]') | |||
| exe = _run_boot_phase(phase, args) | |||
| exe = _run_boot_phase(phase) | |||
| print(f'A bootstrapped DDS executable has been generated: {exe}') | |||
| return 0 | |||
| @@ -1,14 +1,11 @@ | |||
| #!/usr/bin/env python3 | |||
| import argparse | |||
| from contextlib import contextmanager | |||
| import os | |||
| from pathlib import Path | |||
| from typing import Sequence | |||
| import subprocess | |||
| import sys | |||
| import shutil | |||
| import tempfile | |||
| from dds_ci import paths | |||
| from self_build import self_build | |||
| @@ -19,40 +16,11 @@ ROOT = Path(__file__).parent.parent.absolute() | |||
| BUILD_DIR = ROOT / '_build' | |||
| @contextmanager | |||
| def _generate_toolchain(cxx: str): | |||
| with tempfile.NamedTemporaryFile( | |||
| suffix='-dds-toolchain.dds', mode='wb', delete=False) as f: | |||
| comp_id = 'GNU' | |||
| flags = '' | |||
| link_flags = '' | |||
| if cxx in ('cl', 'cl.exe'): | |||
| comp_id = 'MSVC' | |||
| flags += '/experimental:preprocessor ' | |||
| link_flags += 'rpcrt4.lib ' | |||
| else: | |||
| flags += '-fconcepts' | |||
| flags += ' -DSPDLOG_COMPILED_LIB' | |||
| content = f''' | |||
| Compiler-ID: {comp_id} | |||
| C++-Compiler: {cxx} | |||
| C++-Version: C++17 | |||
| Debug: True | |||
| Flags: {flags} | |||
| Link-Flags: {link_flags} | |||
| ''' | |||
| print('Using generated toolchain file: ' + content) | |||
| f.write(content.encode('utf-8')) | |||
| f.close() | |||
| yield Path(f.name) | |||
| os.unlink(f.name) | |||
| def main(argv: Sequence[str]) -> int: | |||
| # Prior versions of this script took a --cxx argument, but we don't care anymore | |||
| parser = argparse.ArgumentParser() | |||
| parser.add_argument( | |||
| '--cxx', help='Path/name of the C++ compiler to use.', required=True) | |||
| args = parser.parse_args(argv) | |||
| parser.add_argument('--cxx', help=argparse.SUPPRESS) | |||
| parser.parse_args(argv) | |||
| dds_bootstrap_env_key = 'DDS_BOOTSTRAP_PREV_EXE' | |||
| if dds_bootstrap_env_key not in os.environ: | |||
| @@ -67,10 +35,14 @@ def main(argv: Sequence[str]) -> int: | |||
| print(f'Using previously built DDS executable: {dds_exe}') | |||
| self_deps_get(dds_exe, paths.SELF_TEST_REPO_DIR) | |||
| with _generate_toolchain(args.cxx) as tc_fpath: | |||
| self_deps_build(dds_exe, tc_fpath, paths.SELF_TEST_REPO_DIR, | |||
| ROOT / 'remote.dds') | |||
| self_build(dds_exe, toolchain=tc_fpath, dds_flags=['--apps']) | |||
| if os.name == 'nt': | |||
| tc_fpath = ROOT / 'tools/msvc.dds' | |||
| else: | |||
| tc_fpath = ROOT / 'tools/gcc-9.dds' | |||
| self_deps_build(dds_exe, str(tc_fpath), paths.SELF_TEST_REPO_DIR, | |||
| ROOT / 'remote.dds') | |||
| self_build(dds_exe, toolchain=str(tc_fpath), dds_flags=['--apps']) | |||
| return 0 | |||
| @@ -15,9 +15,7 @@ from dds_ci import paths, proc | |||
| class CIOptions(NamedTuple): | |||
| cxx: Path | |||
| toolchain: str | |||
| skip_deps: bool | |||
| def _do_bootstrap_build(opts: CIOptions) -> None: | |||
| @@ -26,7 +24,6 @@ def _do_bootstrap_build(opts: CIOptions) -> None: | |||
| sys.executable, | |||
| '-u', | |||
| str(paths.TOOLS_DIR / 'bootstrap.py'), | |||
| f'--cxx={opts.cxx}', | |||
| ]) | |||
| @@ -67,29 +64,16 @@ def main(argv: Sequence[str]) -> int: | |||
| choices=('download', 'build', 'skip'), | |||
| required=True, | |||
| ) | |||
| parser.add_argument( | |||
| '--cxx', help='The name/path of the C++ compiler to use.') | |||
| parser.add_argument( | |||
| '--toolchain', | |||
| '-T', | |||
| help='The toolchain to use for the CI process', | |||
| required=True) | |||
| parser.add_argument( | |||
| '--skip-deps', | |||
| action='store_true', | |||
| help='If specified, will skip getting and building ' | |||
| 'dependencies. (They must already be present)') | |||
| args = parser.parse_args(argv) | |||
| opts = CIOptions( | |||
| cxx=Path(args.cxx or 'unspecified'), | |||
| toolchain=args.toolchain, | |||
| skip_deps=args.skip_deps) | |||
| opts = CIOptions(toolchain=args.toolchain) | |||
| if args.bootstrap_with == 'build': | |||
| if args.cxx is None: | |||
| raise RuntimeError( | |||
| '`--cxx` must be given when using `--bootstrap-with=build`') | |||
| _do_bootstrap_build(opts) | |||
| elif args.bootstrap_with == 'download': | |||
| _do_bootstrap_download() | |||
| @@ -98,21 +82,27 @@ def main(argv: Sequence[str]) -> int: | |||
| else: | |||
| assert False, 'impossible' | |||
| cat_path = paths.BUILD_DIR / 'catalog.db' | |||
| ci_repo_dir = paths.BUILD_DIR / '_ci-repo' | |||
| if not opts.skip_deps: | |||
| if ci_repo_dir.exists(): | |||
| shutil.rmtree(ci_repo_dir) | |||
| self_deps_get(paths.PREBUILT_DDS, ci_repo_dir) | |||
| self_deps_build(paths.PREBUILT_DDS, opts.toolchain, ci_repo_dir, | |||
| paths.PROJECT_ROOT / 'remote.dds') | |||
| if ci_repo_dir.exists(): | |||
| shutil.rmtree(ci_repo_dir) | |||
| proc.check_run([ | |||
| paths.PREBUILT_DDS, | |||
| 'catalog', | |||
| 'import', | |||
| ('--catalog', cat_path), | |||
| ('--json', paths.PROJECT_ROOT / 'catalog.json'), | |||
| ]) | |||
| self_build( | |||
| paths.PREBUILT_DDS, | |||
| toolchain=opts.toolchain, | |||
| dds_flags=['--warnings', '--tests', '--apps']) | |||
| dds_flags=[ | |||
| ('--catalog', cat_path), | |||
| ('--repo-dir', ci_repo_dir), | |||
| ]) | |||
| print('Main build PASSED!') | |||
| cat_path = paths.BUILD_DIR / 'catalog.db' | |||
| proc.check_run([ | |||
| paths.CUR_BUILT_DDS, | |||
| 'catalog', | |||