echo Loading VS environment | echo Loading VS environment | ||||
call "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\Common7\\Tools\\vsdevcmd" -arch=x64 || exit 1 | call "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\Common7\\Tools\\vsdevcmd" -arch=x64 || exit 1 | ||||
echo Executing Build and Tests | echo Executing Build and Tests | ||||
python -u tools/ci.py --cxx cl.exe -T tools\\msvc.dds || exit 1 | |||||
python -u tools/ci.py -B download --cxx cl.exe -T tools\\msvc.dds || exit 1 | |||||
displayName: Full CI | displayName: Full CI | ||||
- publish: _build/dds.exe | - publish: _build/dds.exe | ||||
artifact: DDS Executable - Windows VS2019 | artifact: DDS Executable - Windows VS2019 | ||||
steps: | steps: | ||||
- script: sudo apt update -y && sudo apt install -y python3-minimal g++-8 | - script: sudo apt update -y && sudo apt install -y python3-minimal g++-8 | ||||
displayName: Prepare System | displayName: Prepare System | ||||
- script: python3 -u tools/ci.py --cxx g++-8 -T tools/gcc-8.dds | |||||
- script: python3 -u tools/ci.py -B download --cxx g++-8 -T tools/gcc-8.dds | |||||
displayName: Full CI | displayName: Full CI | ||||
- publish: _build/dds | - publish: _build/dds | ||||
artifact: DDS Executable - Linux | artifact: DDS Executable - Linux | ||||
steps: | steps: | ||||
- script: brew install gcc@8 | - script: brew install gcc@8 | ||||
displayName: Prepare System | displayName: Prepare System | ||||
- script: python3 -u tools/ci.py --cxx g++-8 -T tools/gcc-8.dds | |||||
- script: python3 -u tools/ci.py -B download --cxx g++-8 -T tools/gcc-8.dds | |||||
displayName: Build and Run Unit Tests | displayName: Build and Run Unit Tests | ||||
- publish: _build/dds | - publish: _build/dds | ||||
artifact: DDS Executable - macOS | artifact: DDS Executable - macOS |
EXE_SUFFIX = '.exe' if os.name == 'nt' else '' | EXE_SUFFIX = '.exe' if os.name == 'nt' else '' | ||||
def _run_quiet(args) -> None: | |||||
cmd = [str(s) for s in args] | |||||
res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | |||||
def _run_quiet(cmd, **kwargs) -> None: | |||||
cmd = [str(s) for s in cmd] | |||||
res = subprocess.run( | |||||
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs) | |||||
if res.returncode != 0: | if res.returncode != 0: | ||||
print(f'Subprocess command {cmd} failed ' | print(f'Subprocess command {cmd} failed ' | ||||
f'[{res.returncode}]:\n{res.stdout.decode()}') | f'[{res.returncode}]:\n{res.stdout.decode()}') | ||||
def _clone_bootstrap_phase(ph: str) -> Path: | def _clone_bootstrap_phase(ph: str) -> Path: | ||||
print(f'Cloning: {ph}') | |||||
print(f'Clone revision: {ph}') | |||||
bts_dir = BOOTSTRAP_BASE_DIR / ph | bts_dir = BOOTSTRAP_BASE_DIR / ph | ||||
if bts_dir.exists(): | if bts_dir.exists(): | ||||
shutil.rmtree(bts_dir) | shutil.rmtree(bts_dir) | ||||
return bts_dir | return bts_dir | ||||
def _build_bootstrap_phase(ph: str, bts_dir: Path, args: argparse.Namespace) -> None: | |||||
print(f'Running build: {ph} (Please wait a moment...)') | |||||
def _build_bootstrap_phase(ph: str, bts_dir: Path, | |||||
args: argparse.Namespace) -> None: | |||||
print(f'Build revision: {ph} [This may take a moment]') | |||||
env = os.environ.copy() | env = os.environ.copy() | ||||
env['DDS_BOOTSTRAP_PREV_EXE'] = str(PREBUILT_DIR / 'dds') | env['DDS_BOOTSTRAP_PREV_EXE'] = str(PREBUILT_DIR / 'dds') | ||||
subprocess.check_call( | |||||
_run_quiet( | |||||
[ | [ | ||||
sys.executable, | sys.executable, | ||||
'-u', | '-u', | ||||
parser.add_argument( | parser.add_argument( | ||||
'--cxx', help='The C++ compiler to use for the build', required=True) | '--cxx', help='The C++ compiler to use for the build', required=True) | ||||
args = parser.parse_args(argv) | args = parser.parse_args(argv) | ||||
for phase in BOOTSTRAP_PHASES: | |||||
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, args) | ||||
print(f'A bootstrapped DDS executable has been generated: {exe}') | print(f'A bootstrapped DDS executable has been generated: {exe}') |
import argparse | import argparse | ||||
import os | |||||
import sys | import sys | ||||
from pathlib import Path | from pathlib import Path | ||||
from typing import Sequence, NamedTuple | from typing import Sequence, NamedTuple | ||||
import subprocess | import subprocess | ||||
import urllib.request | |||||
HERE = Path(__file__).parent.absolute() | HERE = Path(__file__).parent.absolute() | ||||
TOOLS_DIR = HERE | TOOLS_DIR = HERE | ||||
class CIOptions(NamedTuple): | class CIOptions(NamedTuple): | ||||
skip_bootstrap: bool | |||||
cxx: Path | cxx: Path | ||||
toolchain: str | toolchain: str | ||||
def _do_bootstrap(opts: CIOptions) -> None: | |||||
print('Running bootstrap') | |||||
def _do_bootstrap_build(opts: CIOptions) -> None: | |||||
print('Bootstrapping by a local build of prior versions...') | |||||
subprocess.check_call([ | subprocess.check_call([ | ||||
sys.executable, | sys.executable, | ||||
'-u', | '-u', | ||||
]) | ]) | ||||
def _do_bootstrap_download() -> None: | |||||
filename = { | |||||
'win32': 'dds-win-x64.exe', | |||||
'linux': 'dds-linux-x64', | |||||
'darwin': 'dds-macos-x64', | |||||
}.get(sys.platform) | |||||
if filename is None: | |||||
raise RuntimeError(f'We do not have a prebuilt DDS binary for the "{sys.platform}" platform') | |||||
url = f'https://github.com/vector-of-bool/dds/releases/download/bootstrap-p2/{filename}' | |||||
print(f'Downloading prebuilt DDS executable: {url}') | |||||
stream = urllib.request.urlopen(url) | |||||
PREBUILT_DDS.parent.mkdir(exist_ok=True, parents=True) | |||||
with PREBUILT_DDS.open('wb') as fd: | |||||
while True: | |||||
buf = stream.read(1024 * 4) | |||||
if not buf: | |||||
break | |||||
fd.write(buf) | |||||
if os.name != 'nt': | |||||
# Mark the binary executable. By default it won't be | |||||
mode = PREBUILT_DDS.stat().st_mode | |||||
mode |= 0b001_001_001 | |||||
PREBUILT_DDS.chmod(mode) | |||||
def main(argv: Sequence[str]) -> int: | def main(argv: Sequence[str]) -> int: | ||||
parser = argparse.ArgumentParser() | parser = argparse.ArgumentParser() | ||||
parser.add_argument( | parser.add_argument( | ||||
'--skip-bootstrap', | |||||
action='store_true', | |||||
'-B', | |||||
'--bootstrap-with', | |||||
help= | help= | ||||
'Skip the prebuild-bootstrap step. This requires a _prebuilt/dds to exist!', | 'Skip the prebuild-bootstrap step. This requires a _prebuilt/dds to exist!', | ||||
choices=('download', 'build'), | |||||
required=True, | |||||
) | ) | ||||
parser.add_argument( | parser.add_argument( | ||||
'--cxx', | '--cxx', | ||||
help='The toolchain to use for the CI process', | help='The toolchain to use for the CI process', | ||||
required=True) | required=True) | ||||
args = parser.parse_args(argv) | args = parser.parse_args(argv) | ||||
opts = CIOptions( | |||||
skip_bootstrap=args.skip_bootstrap, | |||||
cxx=Path(args.cxx), | |||||
toolchain=args.toolchain) | |||||
if not opts.skip_bootstrap: | |||||
_do_bootstrap(opts) | |||||
opts = CIOptions(cxx=Path(args.cxx), toolchain=args.toolchain) | |||||
if args.bootstrap_with == 'build': | |||||
_do_bootstrap_build(opts) | |||||
elif args.bootstrap_with == 'download': | |||||
_do_bootstrap_download() | |||||
else: | |||||
assert False, 'impossible' | |||||
subprocess.check_call([ | subprocess.check_call([ | ||||
str(PREBUILT_DDS), | str(PREBUILT_DDS), |