| @@ -7,4 +7,10 @@ _prebuilt/ | |||
| .pytest_cache/ | |||
| .vagrant/ | |||
| external/OpenSSL | |||
| ## Generate by CI scripts: | |||
| # A copy of OpenSSL for Windows: | |||
| external/OpenSSL | |||
| .docker-ccache/ | |||
| *.egg-info | |||
| *.stamp | |||
| @@ -1,16 +1,15 @@ | |||
| .SILENT: | |||
| .PHONY: \ | |||
| docs docs-server docs-watch docs-sync-server nix-ci linux-ci macos-ci \ | |||
| vagrant-freebsd-ci site alpine-static-ci | |||
| docs docs-server docs-watch docs-sync-server linux-ci macos-ci \ | |||
| vagrant-freebsd-ci site alpine-static-ci _alpine-static-ci poetry-setup \ | |||
| full-ci dev-build release-build | |||
| _invalid: | |||
| echo "Specify a target name to execute" | |||
| exit 1 | |||
| clean: | |||
| rm -f -r -- $(shell find -name __pycache__ -type d) | |||
| rm -f -r -- _build/ _prebuilt/ | |||
| rm -f -vr -- $(shell find -name __pycache__ -type d) | |||
| rm -f -vr -- _build/ _prebuilt/ | |||
| rm -f -v -- $(shell find -name "*.stamp" -type f) | |||
| docs: | |||
| sphinx-build -b html \ | |||
| @@ -38,41 +37,57 @@ docs-sync-server: | |||
| --reload-delay 300 \ | |||
| --watch **/*.html | |||
| macos-ci: | |||
| python3 -u tools/ci.py \ | |||
| -B download \ | |||
| -T tools/gcc-9-rel-macos.jsonc | |||
| .poetry.stamp: poetry.lock | |||
| poetry install --no-dev | |||
| touch .poetry.stamp | |||
| poetry-setup: .poetry.stamp | |||
| full-ci: poetry-setup | |||
| poetry run dds-ci --clean | |||
| dev-build: poetry-setup | |||
| poetry run dds-ci --rapid | |||
| release-build: poetry-setup | |||
| poetry run dds-ci --no-test | |||
| macos-ci: full-ci | |||
| mv _build/dds _build/dds-macos-x64 | |||
| linux-ci: | |||
| python3 -u tools/ci.py \ | |||
| -B download \ | |||
| -T tools/gcc-9-static-rel.jsonc | |||
| linux-ci: full-ci | |||
| mv _build/dds _build/dds-linux-x64 | |||
| nix-ci: | |||
| python3 -u tools/ci.py \ | |||
| -B download \ | |||
| -T tools/gcc-9-rel.jsonc | |||
| _alpine-static-ci: | |||
| poetry install --no-dev | |||
| # Alpine Linux does not ship with ASan nor UBSan, so we can't use them in | |||
| # our test-build. Just use the same build for both. CCache will also speed this up. | |||
| poetry run dds-ci \ | |||
| --bootstrap-with=lazy \ | |||
| --test-toolchain=tools/gcc-9-static-rel.jsonc \ | |||
| --main-toolchain=tools/gcc-9-static-rel.jsonc | |||
| mv _build/dds _build/dds-linux-x64 | |||
| alpine-static-ci: | |||
| docker build -t dds-builder -f tools/Dockerfile.alpine tools/ | |||
| docker build \ | |||
| --build-arg DDS_USER_UID=$(shell id -u) \ | |||
| -t dds-builder \ | |||
| -f tools/Dockerfile.alpine \ | |||
| tools/ | |||
| docker run \ | |||
| -t --rm \ | |||
| -u $(shell id -u) \ | |||
| -v $(PWD):/host -w /host \ | |||
| --privileged \ | |||
| -e CCACHE_DIR=/host/.docker-ccache \ | |||
| dds-builder \ | |||
| make linux-ci | |||
| make _alpine-static-ci | |||
| vagrant-freebsd-ci: | |||
| vagrant up freebsd11 | |||
| vagrant rsync | |||
| vagrant ssh freebsd11 -c '\ | |||
| cd /vagrant && \ | |||
| python3.7 tools/ci.py \ | |||
| -B download \ | |||
| -T tools/freebsd-gcc-10.jsonc \ | |||
| make full-ci \ | |||
| ' | |||
| mkdir -p _build/ | |||
| vagrant scp freebsd11:/vagrant/_build/dds _build/dds-freebsd-x64 | |||
| @@ -19,15 +19,14 @@ stages: | |||
| steps: | |||
| - pwsh: tools\get-win-openssl.ps1 | |||
| displayName: Get OpenSSL for Windows | |||
| - script: python -m pip install pytest pytest-xdist | |||
| pytest-asyncio semver typing-extensions | |||
| - script: python -m pip install poetry && poetry install --no-dev | |||
| displayName: Install Python deps | |||
| - script: | | |||
| echo Loading VS environment | |||
| call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\vsdevcmd" -arch=x64 || exit 1 | |||
| echo Executing Build and Tests | |||
| reg add HKLM\SYSTEM\CurrentControlSet\Control\FileSystem /v LongPathsEnabled /t REG_DWORD /d 1 /f || exit 1 | |||
| python -u tools/ci.py -B download -T tools\msvc.jsonc || exit 1 | |||
| poetry run dds-ci || exit 1 | |||
| move _build\dds.exe _build\dds-win-x64.exe || exit 1 | |||
| displayName: Build and Test | |||
| - publish: _build\dds-win-x64.exe | |||
| @@ -54,7 +53,7 @@ stages: | |||
| displayName: Get GCC 9 | |||
| - script: brew install openssl@1.1 | |||
| displayName: Install OpenSSL | |||
| - script: python3 -m pip install pytest pytest-xdist pytest-asyncio semver typing-extensions | |||
| - script: python3 -m pip install poetry | |||
| displayName: Get Python Dependencies | |||
| - script: make macos-ci | |||
| displayName: Build and Test | |||
| @@ -0,0 +1,548 @@ | |||
| [[package]] | |||
| category = "main" | |||
| description = "apipkg: namespace control and lazy-import mechanism" | |||
| name = "apipkg" | |||
| optional = false | |||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | |||
| version = "1.5" | |||
| [[package]] | |||
| category = "dev" | |||
| description = "An abstract syntax tree for Python with inference support." | |||
| name = "astroid" | |||
| optional = false | |||
| python-versions = ">=3.5" | |||
| version = "2.4.2" | |||
| [package.dependencies] | |||
| lazy-object-proxy = ">=1.4.0,<1.5.0" | |||
| six = ">=1.12,<2.0" | |||
| wrapt = ">=1.11,<2.0" | |||
| [package.dependencies.typed-ast] | |||
| python = "<3.8" | |||
| version = ">=1.4.0,<1.5" | |||
| [[package]] | |||
| category = "main" | |||
| description = "Atomic file writes." | |||
| marker = "sys_platform == \"win32\"" | |||
| name = "atomicwrites" | |||
| optional = false | |||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | |||
| version = "1.4.0" | |||
| [[package]] | |||
| category = "main" | |||
| description = "Classes Without Boilerplate" | |||
| name = "attrs" | |||
| optional = false | |||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | |||
| version = "20.3.0" | |||
| [package.extras] | |||
| dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] | |||
| docs = ["furo", "sphinx", "zope.interface"] | |||
| tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] | |||
| tests_no_zope = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] | |||
| [[package]] | |||
| category = "main" | |||
| description = "Cross-platform colored terminal text." | |||
| marker = "sys_platform == \"win32\"" | |||
| name = "colorama" | |||
| optional = false | |||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" | |||
| version = "0.4.4" | |||
| [[package]] | |||
| category = "main" | |||
| description = "execnet: rapid multi-Python deployment" | |||
| name = "execnet" | |||
| optional = false | |||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | |||
| version = "1.7.1" | |||
| [package.dependencies] | |||
| apipkg = ">=1.4" | |||
| [package.extras] | |||
| testing = ["pre-commit"] | |||
| [[package]] | |||
| category = "main" | |||
| description = "Read metadata from Python packages" | |||
| marker = "python_version < \"3.8\"" | |||
| name = "importlib-metadata" | |||
| optional = false | |||
| python-versions = ">=3.6" | |||
| version = "3.1.1" | |||
| [package.dependencies] | |||
| zipp = ">=0.5" | |||
| [package.extras] | |||
| docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] | |||
| testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] | |||
| [[package]] | |||
| category = "main" | |||
| description = "iniconfig: brain-dead simple config-ini parsing" | |||
| name = "iniconfig" | |||
| optional = false | |||
| python-versions = "*" | |||
| version = "1.1.1" | |||
| [[package]] | |||
| category = "dev" | |||
| description = "A Python utility / library to sort Python imports." | |||
| name = "isort" | |||
| optional = false | |||
| python-versions = ">=3.6,<4.0" | |||
| version = "5.6.4" | |||
| [package.extras] | |||
| colors = ["colorama (>=0.4.3,<0.5.0)"] | |||
| pipfile_deprecated_finder = ["pipreqs", "requirementslib"] | |||
| requirements_deprecated_finder = ["pipreqs", "pip-api"] | |||
| [[package]] | |||
| category = "main" | |||
| description = "A Python implementation of the JSON5 data format." | |||
| name = "json5" | |||
| optional = false | |||
| python-versions = "*" | |||
| version = "0.9.5" | |||
| [package.extras] | |||
| dev = ["hypothesis"] | |||
| [[package]] | |||
| category = "dev" | |||
| description = "A fast and thorough lazy object proxy." | |||
| name = "lazy-object-proxy" | |||
| optional = false | |||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | |||
| version = "1.4.3" | |||
| [[package]] | |||
| category = "dev" | |||
| description = "McCabe checker, plugin for flake8" | |||
| name = "mccabe" | |||
| optional = false | |||
| python-versions = "*" | |||
| version = "0.6.1" | |||
| [[package]] | |||
| category = "dev" | |||
| description = "Optional static typing for Python" | |||
| name = "mypy" | |||
| optional = false | |||
| python-versions = ">=3.5" | |||
| version = "0.790" | |||
| [package.dependencies] | |||
| mypy-extensions = ">=0.4.3,<0.5.0" | |||
| typed-ast = ">=1.4.0,<1.5.0" | |||
| typing-extensions = ">=3.7.4" | |||
| [package.extras] | |||
| dmypy = ["psutil (>=4.0)"] | |||
| [[package]] | |||
| category = "dev" | |||
| description = "Experimental type system extensions for programs checked with the mypy typechecker." | |||
| name = "mypy-extensions" | |||
| optional = false | |||
| python-versions = "*" | |||
| version = "0.4.3" | |||
| [[package]] | |||
| category = "main" | |||
| description = "Core utilities for Python packages" | |||
| name = "packaging" | |||
| optional = false | |||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | |||
| version = "20.7" | |||
| [package.dependencies] | |||
| pyparsing = ">=2.0.2" | |||
| [[package]] | |||
| category = "main" | |||
| description = "plugin and hook calling mechanisms for python" | |||
| name = "pluggy" | |||
| optional = false | |||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | |||
| version = "0.13.1" | |||
| [package.dependencies] | |||
| [package.dependencies.importlib-metadata] | |||
| python = "<3.8" | |||
| version = ">=0.12" | |||
| [package.extras] | |||
| dev = ["pre-commit", "tox"] | |||
| [[package]] | |||
| category = "main" | |||
| description = "library with cross-python path, ini-parsing, io, code, log facilities" | |||
| name = "py" | |||
| optional = false | |||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | |||
| version = "1.9.0" | |||
| [[package]] | |||
| category = "dev" | |||
| description = "python code static checker" | |||
| name = "pylint" | |||
| optional = false | |||
| python-versions = ">=3.5.*" | |||
| version = "2.6.0" | |||
| [package.dependencies] | |||
| astroid = ">=2.4.0,<=2.5" | |||
| colorama = "*" | |||
| isort = ">=4.2.5,<6" | |||
| mccabe = ">=0.6,<0.7" | |||
| toml = ">=0.7.1" | |||
| [[package]] | |||
| category = "main" | |||
| description = "Python parsing module" | |||
| name = "pyparsing" | |||
| optional = false | |||
| python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" | |||
| version = "2.4.7" | |||
| [[package]] | |||
| category = "main" | |||
| description = "pytest: simple powerful testing with Python" | |||
| name = "pytest" | |||
| optional = false | |||
| python-versions = ">=3.5" | |||
| version = "6.1.2" | |||
| [package.dependencies] | |||
| atomicwrites = ">=1.0" | |||
| attrs = ">=17.4.0" | |||
| colorama = "*" | |||
| iniconfig = "*" | |||
| packaging = "*" | |||
| pluggy = ">=0.12,<1.0" | |||
| py = ">=1.8.2" | |||
| toml = "*" | |||
| [package.dependencies.importlib-metadata] | |||
| python = "<3.8" | |||
| version = ">=0.12" | |||
| [package.extras] | |||
| checkqa_mypy = ["mypy (0.780)"] | |||
| testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] | |||
| [[package]] | |||
| category = "main" | |||
| description = "Pytest support for asyncio." | |||
| name = "pytest-asyncio" | |||
| optional = false | |||
| python-versions = ">= 3.5" | |||
| version = "0.14.0" | |||
| [package.dependencies] | |||
| pytest = ">=5.4.0" | |||
| [package.extras] | |||
| testing = ["async-generator (>=1.3)", "coverage", "hypothesis (>=5.7.1)"] | |||
| [[package]] | |||
| category = "main" | |||
| description = "run tests in isolated forked subprocesses" | |||
| name = "pytest-forked" | |||
| optional = false | |||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" | |||
| version = "1.3.0" | |||
| [package.dependencies] | |||
| py = "*" | |||
| pytest = ">=3.10" | |||
| [[package]] | |||
| category = "main" | |||
| description = "pytest xdist plugin for distributed testing and loop-on-failing modes" | |||
| name = "pytest-xdist" | |||
| optional = false | |||
| python-versions = ">=3.5" | |||
| version = "2.1.0" | |||
| [package.dependencies] | |||
| execnet = ">=1.1" | |||
| pytest = ">=6.0.0" | |||
| pytest-forked = "*" | |||
| [package.extras] | |||
| psutil = ["psutil (>=3.0)"] | |||
| testing = ["filelock"] | |||
| [[package]] | |||
| category = "dev" | |||
| description = "a python refactoring library..." | |||
| name = "rope" | |||
| optional = false | |||
| python-versions = "*" | |||
| version = "0.18.0" | |||
| [package.extras] | |||
| dev = ["pytest"] | |||
| [[package]] | |||
| category = "main" | |||
| description = "Python helper for Semantic Versioning (http://semver.org/)" | |||
| name = "semver" | |||
| optional = false | |||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | |||
| version = "2.13.0" | |||
| [[package]] | |||
| category = "dev" | |||
| description = "Python 2 and 3 compatibility utilities" | |||
| name = "six" | |||
| optional = false | |||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" | |||
| version = "1.15.0" | |||
| [[package]] | |||
| category = "main" | |||
| description = "Python Library for Tom's Obvious, Minimal Language" | |||
| name = "toml" | |||
| optional = false | |||
| python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" | |||
| version = "0.10.2" | |||
| [[package]] | |||
| category = "dev" | |||
| description = "a fork of Python 2 and 3 ast modules with type comment support" | |||
| name = "typed-ast" | |||
| optional = false | |||
| python-versions = "*" | |||
| version = "1.4.1" | |||
| [[package]] | |||
| category = "main" | |||
| description = "Backported and Experimental Type Hints for Python 3.5+" | |||
| name = "typing-extensions" | |||
| optional = false | |||
| python-versions = "*" | |||
| version = "3.7.4.3" | |||
| [[package]] | |||
| category = "dev" | |||
| description = "Module for decorators, wrappers and monkey patching." | |||
| name = "wrapt" | |||
| optional = false | |||
| python-versions = "*" | |||
| version = "1.12.1" | |||
| [[package]] | |||
| category = "dev" | |||
| description = "A formatter for Python code." | |||
| name = "yapf" | |||
| optional = false | |||
| python-versions = "*" | |||
| version = "0.30.0" | |||
| [[package]] | |||
| category = "main" | |||
| description = "Backport of pathlib-compatible object wrapper for zip files" | |||
| marker = "python_version < \"3.8\"" | |||
| name = "zipp" | |||
| optional = false | |||
| python-versions = ">=3.6" | |||
| version = "3.4.0" | |||
| [package.extras] | |||
| docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] | |||
| testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] | |||
| [metadata] | |||
| content-hash = "bb7d048748c946ac4f6196a339a149d8060b048968853cb281d83207e324a61b" | |||
| python-versions = "^3.6" | |||
| [metadata.files] | |||
| apipkg = [ | |||
| {file = "apipkg-1.5-py2.py3-none-any.whl", hash = "sha256:58587dd4dc3daefad0487f6d9ae32b4542b185e1c36db6993290e7c41ca2b47c"}, | |||
| {file = "apipkg-1.5.tar.gz", hash = "sha256:37228cda29411948b422fae072f57e31d3396d2ee1c9783775980ee9c9990af6"}, | |||
| ] | |||
| astroid = [ | |||
| {file = "astroid-2.4.2-py3-none-any.whl", hash = "sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386"}, | |||
| {file = "astroid-2.4.2.tar.gz", hash = "sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703"}, | |||
| ] | |||
| atomicwrites = [ | |||
| {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, | |||
| {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, | |||
| ] | |||
| attrs = [ | |||
| {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, | |||
| {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, | |||
| ] | |||
| colorama = [ | |||
| {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, | |||
| {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, | |||
| ] | |||
| execnet = [ | |||
| {file = "execnet-1.7.1-py2.py3-none-any.whl", hash = "sha256:d4efd397930c46415f62f8a31388d6be4f27a91d7550eb79bc64a756e0056547"}, | |||
| {file = "execnet-1.7.1.tar.gz", hash = "sha256:cacb9df31c9680ec5f95553976c4da484d407e85e41c83cb812aa014f0eddc50"}, | |||
| ] | |||
| importlib-metadata = [ | |||
| {file = "importlib_metadata-3.1.1-py3-none-any.whl", hash = "sha256:6112e21359ef8f344e7178aa5b72dc6e62b38b0d008e6d3cb212c5b84df72013"}, | |||
| {file = "importlib_metadata-3.1.1.tar.gz", hash = "sha256:b0c2d3b226157ae4517d9625decf63591461c66b3a808c2666d538946519d170"}, | |||
| ] | |||
| iniconfig = [ | |||
| {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, | |||
| {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, | |||
| ] | |||
| isort = [ | |||
| {file = "isort-5.6.4-py3-none-any.whl", hash = "sha256:dcab1d98b469a12a1a624ead220584391648790275560e1a43e54c5dceae65e7"}, | |||
| {file = "isort-5.6.4.tar.gz", hash = "sha256:dcaeec1b5f0eca77faea2a35ab790b4f3680ff75590bfcb7145986905aab2f58"}, | |||
| ] | |||
| json5 = [ | |||
| {file = "json5-0.9.5-py2.py3-none-any.whl", hash = "sha256:af1a1b9a2850c7f62c23fde18be4749b3599fd302f494eebf957e2ada6b9e42c"}, | |||
| {file = "json5-0.9.5.tar.gz", hash = "sha256:703cfee540790576b56a92e1c6aaa6c4b0d98971dc358ead83812aa4d06bdb96"}, | |||
| ] | |||
| lazy-object-proxy = [ | |||
| {file = "lazy-object-proxy-1.4.3.tar.gz", hash = "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp27-cp27m-win32.whl", hash = "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp34-cp34m-win32.whl", hash = "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp34-cp34m-win_amd64.whl", hash = "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp38-cp38-win32.whl", hash = "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd"}, | |||
| {file = "lazy_object_proxy-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239"}, | |||
| ] | |||
| mccabe = [ | |||
| {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, | |||
| {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, | |||
| ] | |||
| mypy = [ | |||
| {file = "mypy-0.790-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:bd03b3cf666bff8d710d633d1c56ab7facbdc204d567715cb3b9f85c6e94f669"}, | |||
| {file = "mypy-0.790-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:2170492030f6faa537647d29945786d297e4862765f0b4ac5930ff62e300d802"}, | |||
| {file = "mypy-0.790-cp35-cp35m-win_amd64.whl", hash = "sha256:e86bdace26c5fe9cf8cb735e7cedfe7850ad92b327ac5d797c656717d2ca66de"}, | |||
| {file = "mypy-0.790-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e97e9c13d67fbe524be17e4d8025d51a7dca38f90de2e462243ab8ed8a9178d1"}, | |||
| {file = "mypy-0.790-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0d34d6b122597d48a36d6c59e35341f410d4abfa771d96d04ae2c468dd201abc"}, | |||
| {file = "mypy-0.790-cp36-cp36m-win_amd64.whl", hash = "sha256:72060bf64f290fb629bd4a67c707a66fd88ca26e413a91384b18db3876e57ed7"}, | |||
| {file = "mypy-0.790-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:eea260feb1830a627fb526d22fbb426b750d9f5a47b624e8d5e7e004359b219c"}, | |||
| {file = "mypy-0.790-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c614194e01c85bb2e551c421397e49afb2872c88b5830e3554f0519f9fb1c178"}, | |||
| {file = "mypy-0.790-cp37-cp37m-win_amd64.whl", hash = "sha256:0a0d102247c16ce93c97066443d11e2d36e6cc2a32d8ccc1f705268970479324"}, | |||
| {file = "mypy-0.790-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf4e7bf7f1214826cf7333627cb2547c0db7e3078723227820d0a2490f117a01"}, | |||
| {file = "mypy-0.790-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:af4e9ff1834e565f1baa74ccf7ae2564ae38c8df2a85b057af1dbbc958eb6666"}, | |||
| {file = "mypy-0.790-cp38-cp38-win_amd64.whl", hash = "sha256:da56dedcd7cd502ccd3c5dddc656cb36113dd793ad466e894574125945653cea"}, | |||
| {file = "mypy-0.790-py3-none-any.whl", hash = "sha256:2842d4fbd1b12ab422346376aad03ff5d0805b706102e475e962370f874a5122"}, | |||
| {file = "mypy-0.790.tar.gz", hash = "sha256:2b21ba45ad9ef2e2eb88ce4aeadd0112d0f5026418324176fd494a6824b74975"}, | |||
| ] | |||
| mypy-extensions = [ | |||
| {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, | |||
| {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, | |||
| ] | |||
| packaging = [ | |||
| {file = "packaging-20.7-py2.py3-none-any.whl", hash = "sha256:eb41423378682dadb7166144a4926e443093863024de508ca5c9737d6bc08376"}, | |||
| {file = "packaging-20.7.tar.gz", hash = "sha256:05af3bb85d320377db281cf254ab050e1a7ebcbf5410685a9a407e18a1f81236"}, | |||
| ] | |||
| pluggy = [ | |||
| {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, | |||
| {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, | |||
| ] | |||
| py = [ | |||
| {file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"}, | |||
| {file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"}, | |||
| ] | |||
| pylint = [ | |||
| {file = "pylint-2.6.0-py3-none-any.whl", hash = "sha256:bfe68f020f8a0fece830a22dd4d5dddb4ecc6137db04face4c3420a46a52239f"}, | |||
| {file = "pylint-2.6.0.tar.gz", hash = "sha256:bb4a908c9dadbc3aac18860550e870f58e1a02c9f2c204fdf5693d73be061210"}, | |||
| ] | |||
| pyparsing = [ | |||
| {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, | |||
| {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, | |||
| ] | |||
| pytest = [ | |||
| {file = "pytest-6.1.2-py3-none-any.whl", hash = "sha256:4288fed0d9153d9646bfcdf0c0428197dba1ecb27a33bb6e031d002fa88653fe"}, | |||
| {file = "pytest-6.1.2.tar.gz", hash = "sha256:c0a7e94a8cdbc5422a51ccdad8e6f1024795939cc89159a0ae7f0b316ad3823e"}, | |||
| ] | |||
| pytest-asyncio = [ | |||
| {file = "pytest-asyncio-0.14.0.tar.gz", hash = "sha256:9882c0c6b24429449f5f969a5158b528f39bde47dc32e85b9f0403965017e700"}, | |||
| {file = "pytest_asyncio-0.14.0-py3-none-any.whl", hash = "sha256:2eae1e34f6c68fc0a9dc12d4bea190483843ff4708d24277c41568d6b6044f1d"}, | |||
| ] | |||
| pytest-forked = [ | |||
| {file = "pytest-forked-1.3.0.tar.gz", hash = "sha256:6aa9ac7e00ad1a539c41bec6d21011332de671e938c7637378ec9710204e37ca"}, | |||
| {file = "pytest_forked-1.3.0-py2.py3-none-any.whl", hash = "sha256:dc4147784048e70ef5d437951728825a131b81714b398d5d52f17c7c144d8815"}, | |||
| ] | |||
| pytest-xdist = [ | |||
| {file = "pytest-xdist-2.1.0.tar.gz", hash = "sha256:82d938f1a24186520e2d9d3a64ef7d9ac7ecdf1a0659e095d18e596b8cbd0672"}, | |||
| {file = "pytest_xdist-2.1.0-py3-none-any.whl", hash = "sha256:7c629016b3bb006b88ac68e2b31551e7becf173c76b977768848e2bbed594d90"}, | |||
| ] | |||
| rope = [ | |||
| {file = "rope-0.18.0.tar.gz", hash = "sha256:786b5c38c530d4846aa68a42604f61b4e69a493390e3ca11b88df0fbfdc3ed04"}, | |||
| ] | |||
| semver = [ | |||
| {file = "semver-2.13.0-py2.py3-none-any.whl", hash = "sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4"}, | |||
| {file = "semver-2.13.0.tar.gz", hash = "sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f"}, | |||
| ] | |||
| six = [ | |||
| {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, | |||
| {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, | |||
| ] | |||
| toml = [ | |||
| {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, | |||
| {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, | |||
| ] | |||
| typed-ast = [ | |||
| {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, | |||
| {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, | |||
| {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, | |||
| {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, | |||
| {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, | |||
| {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, | |||
| {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, | |||
| {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, | |||
| {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, | |||
| {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, | |||
| {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, | |||
| {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, | |||
| {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, | |||
| {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, | |||
| {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, | |||
| {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, | |||
| {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, | |||
| {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, | |||
| {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, | |||
| {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, | |||
| {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, | |||
| ] | |||
| typing-extensions = [ | |||
| {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, | |||
| {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, | |||
| {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, | |||
| ] | |||
| wrapt = [ | |||
| {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, | |||
| ] | |||
| yapf = [ | |||
| {file = "yapf-0.30.0-py2.py3-none-any.whl", hash = "sha256:3abf61ba67cf603069710d30acbc88cfe565d907e16ad81429ae90ce9651e0c9"}, | |||
| {file = "yapf-0.30.0.tar.gz", hash = "sha256:3000abee4c28daebad55da6c85f3cd07b8062ce48e2e9943c8da1b9667d48427"}, | |||
| ] | |||
| zipp = [ | |||
| {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"}, | |||
| {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"}, | |||
| ] | |||
| @@ -0,0 +1,33 @@ | |||
| [tool.poetry] | |||
| name = "dds" | |||
| version = "0.0.0" | |||
| description = "" | |||
| authors = ["vector-of-bool <vectorofbool@gmail.com>"] | |||
| license = "MPL-2.0" | |||
| packages = [ | |||
| { include = "dds_ci", from = "tools/" }, | |||
| ] | |||
| [tool.poetry.dependencies] | |||
| python = "^3.6" | |||
| semver = "^2.13.0" | |||
| pytest = "^6.1.2" | |||
| pytest-xdist = "^2.1.0" | |||
| pytest-asyncio = "^0.14.0" | |||
| typing-extensions = "^3.7.4" | |||
| json5 = "^0.9.5" | |||
| [tool.poetry.dev-dependencies] | |||
| # Only needed for development | |||
| pylint = "^2.6.0" | |||
| yapf = "^0.30.0" | |||
| mypy = "^0.790" | |||
| rope = "^0.18.0" | |||
| [tool.poetry.scripts] | |||
| dds-ci = "dds_ci.main:start" | |||
| [build-system] | |||
| requires = ["poetry>=0.12"] | |||
| build-backend = "poetry.masonry.api" | |||
| @@ -16,7 +16,6 @@ class DirectoryServingHTTPRequestHandler(SimpleHTTPRequestHandler): | |||
| """ | |||
| A simple HTTP request handler that simply serves files from a directory given to the constructor. | |||
| """ | |||
| def __init__(self, *args, **kwargs) -> None: | |||
| self.dir = kwargs.pop('dir') | |||
| super().__init__(*args, **kwargs) | |||
| @@ -68,7 +67,6 @@ class RepoFixture: | |||
| """ | |||
| A fixture handle to a dds HTTP repository, including a path and URL. | |||
| """ | |||
| def __init__(self, dds_exe: Path, info: ServerInfo) -> None: | |||
| self.server = info | |||
| self.url = info.base_url | |||
| @@ -2,12 +2,20 @@ FROM alpine:3.12.1 | |||
| # Base build dependencies | |||
| RUN apk add "gcc=9.3.0-r2" "g++=9.3.0-r2" make python3 py3-pip \ | |||
| git openssl-libs-static openssl-dev | |||
| git openssl-libs-static openssl-dev ccache lld curl python3-dev | |||
| # We use version-qualified names for compiler executables | |||
| RUN ln -s $(type -P gcc) /usr/local/bin/gcc-9 && \ | |||
| ln -s $(type -P g++) /usr/local/bin/g++-9 | |||
| # Some Python test dependencies | |||
| RUN python3 -m pip install pytest pytest-xdist \ | |||
| pytest-asyncio semver typing-extensions | |||
| # We want the UID in the container to match the UID on the outside, for minimal | |||
| # fuss with file permissions | |||
| ARG DDS_USER_UID=1000 | |||
| RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py \ | |||
| | env POETRY_HOME=/opt/poetry python3 -u - --no-modify-path && \ | |||
| ln -s /opt/poetry/bin/poetry /usr/local/bin/poetry && \ | |||
| chmod a+x /opt/poetry/bin/poetry && \ | |||
| adduser --disabled-password --uid=${DDS_USER_UID} dds | |||
| USER dds | |||
| @@ -1,115 +0,0 @@ | |||
| import argparse | |||
| from pathlib import Path | |||
| import subprocess | |||
| import os | |||
| 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 = [ | |||
| BootstrapPhase('bootstrap-p1.2', 'g++-8', 'cl.exe'), | |||
| BootstrapPhase('bootstrap-p4.2', 'g++-8', 'cl.exe'), | |||
| BootstrapPhase('bootstrap-p5.2', 'g++-9', 'cl.exe'), | |||
| BootstrapPhase('0.1.0-alpha.3', 'g++-9', 'cl.exe'), | |||
| BootstrapPhase('0.1.0-alpha.4', 'g++-9', 'cl.exe'), | |||
| ] | |||
| HERE = Path(__file__).parent.absolute() | |||
| PROJECT_ROOT = HERE.parent | |||
| BUILD_DIR = PROJECT_ROOT / '_build' | |||
| BOOTSTRAP_BASE_DIR = BUILD_DIR / '_bootstrap' | |||
| PREBUILT_DIR = PROJECT_ROOT / '_prebuilt' | |||
| 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, | |||
| ) | |||
| 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(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={ref}', | |||
| f'file://{PROJECT_ROOT}', | |||
| bts_dir, | |||
| ]) | |||
| return bts_dir | |||
| 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 / F'dds{EXE_SUFFIX}') | |||
| _run_quiet( | |||
| [ | |||
| sys.executable, | |||
| '-u', | |||
| str(bts_dir / 'tools/build.py'), | |||
| f'--cxx={ph.platform_compiler}', | |||
| ], | |||
| env=env, | |||
| cwd=bts_dir, | |||
| ) | |||
| def _pull_executable(bts_dir: Path) -> Path: | |||
| prebuild_dir = (PROJECT_ROOT / '_prebuilt') | |||
| prebuild_dir.mkdir(exist_ok=True) | |||
| generated = list(bts_dir.glob(f'_build/dds{EXE_SUFFIX}')) | |||
| assert len(generated) == 1, repr(generated) | |||
| exe, = generated | |||
| dest = prebuild_dir / exe.name | |||
| if dest.exists(): | |||
| dest.unlink() | |||
| exe.rename(dest) | |||
| return dest | |||
| 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() -> int: | |||
| for idx, phase in enumerate(BOOTSTRAP_PHASES): | |||
| print(f'Bootstrap phase [{idx+1}/{len(BOOTSTRAP_PHASES)}]') | |||
| exe = _run_boot_phase(phase) | |||
| print(f'A bootstrapped DDS executable has been generated: {exe}') | |||
| return 0 | |||
| if __name__ == "__main__": | |||
| sys.exit(main()) | |||
| @@ -1,49 +0,0 @@ | |||
| #!/usr/bin/env python3 | |||
| import argparse | |||
| import os | |||
| from pathlib import Path | |||
| from typing import Sequence | |||
| import sys | |||
| import shutil | |||
| from dds_ci import paths | |||
| from self_build import self_build | |||
| ROOT = Path(__file__).parent.parent.absolute() | |||
| BUILD_DIR = ROOT / '_build' | |||
| 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=argparse.SUPPRESS) | |||
| parser.parse_args(argv) | |||
| dds_bootstrap_env_key = 'DDS_BOOTSTRAP_PREV_EXE' | |||
| if dds_bootstrap_env_key not in os.environ: | |||
| raise RuntimeError('A previous-phase bootstrapped executable ' | |||
| 'must be available via $DDS_BOOTSTRAP_PREV_EXE') | |||
| dds_exe = Path(os.environ[dds_bootstrap_env_key]) | |||
| if BUILD_DIR.exists(): | |||
| shutil.rmtree(BUILD_DIR) | |||
| print(f'Using previously built DDS executable: {dds_exe}') | |||
| if os.name == 'nt': | |||
| tc_fpath = ROOT / 'tools/msvc.jsonc' | |||
| elif sys.platform.startswith('freebsd'): | |||
| tc_fpath = ROOT / 'tools/freebsd-gcc-9.jsonc' | |||
| else: | |||
| tc_fpath = ROOT / 'tools/gcc-9.jsonc' | |||
| self_build(dds_exe, | |||
| cat_json_path=ROOT / 'catalog.old.json', | |||
| toolchain=str(tc_fpath)) | |||
| return 0 | |||
| if __name__ == "__main__": | |||
| sys.exit(main(sys.argv[1:])) | |||
| @@ -1,144 +0,0 @@ | |||
| import argparse | |||
| import os | |||
| import sys | |||
| import pytest | |||
| from pathlib import Path | |||
| from typing import Sequence, NamedTuple | |||
| import multiprocessing | |||
| import subprocess | |||
| import urllib.request | |||
| import shutil | |||
| from self_build import self_build, dds_build | |||
| from dds_ci import paths, proc | |||
| class CIOptions(NamedTuple): | |||
| toolchain: str | |||
| def _do_bootstrap_build(opts: CIOptions) -> None: | |||
| print('Bootstrapping by a local build of prior versions...') | |||
| subprocess.check_call([ | |||
| sys.executable, | |||
| '-u', | |||
| str(paths.TOOLS_DIR / 'bootstrap.py'), | |||
| ]) | |||
| def _do_bootstrap_download() -> None: | |||
| filename = { | |||
| 'win32': 'dds-win-x64.exe', | |||
| 'linux': 'dds-linux-x64', | |||
| 'darwin': 'dds-macos-x64', | |||
| 'freebsd11': 'dds-freebsd-x64', | |||
| 'freebsd12': 'dds-freebsd-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/0.1.0-alpha.4/{filename}' | |||
| print(f'Downloading prebuilt DDS executable: {url}') | |||
| stream = urllib.request.urlopen(url) | |||
| paths.PREBUILT_DDS.parent.mkdir(exist_ok=True, parents=True) | |||
| with paths.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 = paths.PREBUILT_DDS.stat().st_mode | |||
| mode |= 0b001_001_001 | |||
| paths.PREBUILT_DDS.chmod(mode) | |||
| def main(argv: Sequence[str]) -> int: | |||
| parser = argparse.ArgumentParser() | |||
| parser.add_argument( | |||
| '-B', | |||
| '--bootstrap-with', | |||
| help='How are we to obtain a bootstrapped DDS executable?', | |||
| choices=('download', 'build', 'skip'), | |||
| required=True, | |||
| ) | |||
| parser.add_argument( | |||
| '--toolchain', | |||
| '-T', | |||
| help='The toolchain to use for the CI process', | |||
| required=True, | |||
| ) | |||
| parser.add_argument( | |||
| '--build-only', action='store_true', help='Only build the `dds` executable. Skip second-phase and tests.') | |||
| parser.add_argument( | |||
| '--no-clean', | |||
| action='store_false', | |||
| dest='clean', | |||
| help='Don\'t remove prior build/deps results', | |||
| ) | |||
| args = parser.parse_args(argv) | |||
| opts = CIOptions(toolchain=args.toolchain) | |||
| if args.bootstrap_with == 'build': | |||
| _do_bootstrap_build(opts) | |||
| elif args.bootstrap_with == 'download': | |||
| _do_bootstrap_download() | |||
| elif args.bootstrap_with == 'skip': | |||
| pass | |||
| else: | |||
| assert False, 'impossible' | |||
| old_cat_path = paths.PREBUILT_DIR / 'catalog.db' | |||
| if old_cat_path.is_file() and args.clean: | |||
| old_cat_path.unlink() | |||
| ci_repo_dir = paths.PREBUILT_DIR / 'ci-repo' | |||
| if ci_repo_dir.exists() and args.clean: | |||
| shutil.rmtree(ci_repo_dir) | |||
| self_build( | |||
| paths.PREBUILT_DDS, | |||
| toolchain=opts.toolchain, | |||
| cat_path=old_cat_path, | |||
| cat_json_path=Path('old-catalog.json'), | |||
| dds_flags=[('--repo-dir', ci_repo_dir)]) | |||
| print('Main build PASSED!') | |||
| print(f'A `dds` executable has been generated: {paths.CUR_BUILT_DDS}') | |||
| if args.build_only: | |||
| print('`--build-only` was given, so second phase and tests will not execute') | |||
| return 0 | |||
| print('Bootstrapping myself:') | |||
| new_cat_path = paths.BUILD_DIR / 'catalog.db' | |||
| new_repo_dir = paths.BUILD_DIR / 'ci-repo-2' | |||
| if new_cat_path.is_file(): | |||
| new_cat_path.unlink() | |||
| if new_repo_dir.is_dir(): | |||
| shutil.rmtree(new_repo_dir) | |||
| dds_build(paths.CUR_BUILT_DDS, | |||
| toolchain=opts.toolchain, | |||
| more_flags=[ | |||
| f'--repo-dir={new_repo_dir}', | |||
| f'--catalog={new_cat_path}', | |||
| '--add-repo=https://dds.pizza/repo', | |||
| ]) | |||
| print('Bootstrap test PASSED!') | |||
| basetemp = Path('/tmp/dds-ci') | |||
| basetemp.mkdir(exist_ok=True, parents=True) | |||
| return pytest.main([ | |||
| '-v', | |||
| '--durations=10', | |||
| '-n', | |||
| str(multiprocessing.cpu_count() + 2), | |||
| f'--basetemp={basetemp}', # Force to use a top-level /tmp dir. On Windows this prevents paths from begin too long | |||
| 'tests/', | |||
| ]) | |||
| if __name__ == "__main__": | |||
| sys.exit(main(sys.argv[1:])) | |||
| @@ -0,0 +1,86 @@ | |||
| import enum | |||
| from pathlib import Path | |||
| from contextlib import contextmanager | |||
| from typing import Iterator, ContextManager | |||
| import sys | |||
| import urllib.request | |||
| import shutil | |||
| import tempfile | |||
| from . import paths | |||
| from .dds import DDSWrapper | |||
| from .paths import new_tempdir | |||
| class BootstrapMode(enum.Enum): | |||
| """How should be bootstrap our prior DDS executable?""" | |||
| #: Downlaod one from GitHub | |||
| Download = 'download' | |||
| #: Build one from source | |||
| Build = 'build' | |||
| #: Skip bootstrapping. Assume it already exists. | |||
| Skip = 'skip' | |||
| #: If the prior executable exists, skip, otherwise download | |||
| Lazy = 'lazy' | |||
| def _do_bootstrap_download() -> Path: | |||
| filename = { | |||
| 'win32': 'dds-win-x64.exe', | |||
| 'linux': 'dds-linux-x64', | |||
| 'darwin': 'dds-macos-x64', | |||
| 'freebsd11': 'dds-freebsd-x64', | |||
| 'freebsd12': 'dds-freebsd-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/0.1.0-alpha.4/{filename}' | |||
| print(f'Downloading prebuilt DDS executable: {url}') | |||
| stream = urllib.request.urlopen(url) | |||
| paths.PREBUILT_DDS.parent.mkdir(exist_ok=True, parents=True) | |||
| with paths.PREBUILT_DDS.open('wb') as fd: | |||
| while True: | |||
| buf = stream.read(1024 * 4) | |||
| if not buf: | |||
| break | |||
| fd.write(buf) | |||
| if sys.platform != 'win32': | |||
| # Mark the binary executable. By default it won't be | |||
| mode = paths.PREBUILT_DDS.stat().st_mode | |||
| mode |= 0b001_001_001 | |||
| paths.PREBUILT_DDS.chmod(mode) | |||
| return paths.PREBUILT_DDS | |||
| @contextmanager | |||
| def pin_exe(fpath: Path) -> Iterator[Path]: | |||
| """ | |||
| Create a copy of 'fpath' at an unspecified location, and yield that path. | |||
| This is needed if the executable would overwrite itself. | |||
| """ | |||
| with new_tempdir() as tdir: | |||
| tfile = tdir / 'previous-dds.exe' | |||
| shutil.copy2(fpath, tfile) | |||
| yield tfile | |||
| @contextmanager | |||
| def get_bootstrap_exe(mode: BootstrapMode) -> Iterator[DDSWrapper]: | |||
| """Context manager that yields a DDSWrapper around a prior 'dds' executable""" | |||
| if mode is BootstrapMode.Lazy: | |||
| f = paths.PREBUILT_DDS | |||
| if not f.exists(): | |||
| _do_bootstrap_download() | |||
| elif mode is BootstrapMode.Download: | |||
| f = _do_bootstrap_download() | |||
| elif mode is BootstrapMode.Build: | |||
| f = _do_bootstrap_build() | |||
| elif mode is BootstrapMode.Skip: | |||
| f = paths.PREBUILT_DDS | |||
| with pin_exe(f) as dds: | |||
| yield DDSWrapper(dds) | |||
| @@ -1,19 +0,0 @@ | |||
| from argparse import ArgumentParser | |||
| from dds_ci import paths | |||
| def add_tc_arg(parser: ArgumentParser, *, required=True) -> None: | |||
| parser.add_argument( | |||
| '--toolchain', | |||
| '-T', | |||
| help='The DDS toolchain to use', | |||
| required=required) | |||
| def add_dds_exe_arg(parser: ArgumentParser, *, required=True) -> None: | |||
| parser.add_argument( | |||
| '--exe', | |||
| '-e', | |||
| help='Path to a DDS executable to use', | |||
| required=required) | |||
| @@ -0,0 +1,67 @@ | |||
| from pathlib import Path | |||
| import multiprocessing | |||
| import shutil | |||
| from . import proc | |||
| from . import paths | |||
| class DDSWrapper: | |||
| """ | |||
| Wraps a 'dds' executable with some convenience APIs that invoke various | |||
| 'dds' subcommands. | |||
| """ | |||
| def __init__(self, path: Path) -> None: | |||
| self.path = path | |||
| self.repo_dir = paths.PREBUILT_DIR / 'ci-repo' | |||
| self.catalog_path = paths.PREBUILT_DIR / 'ci-catalog.db' | |||
| @property | |||
| def catalog_path_arg(self): | |||
| """The arguments for --catalog""" | |||
| return f'--catalog={self.catalog_path}' | |||
| @property | |||
| def repo_dir_arg(self): | |||
| """The arguments for --repo-dir""" | |||
| return f'--repo-dir={self.repo_dir}' | |||
| def clean(self, *, build_dir: Path = None, repo=True, catalog=True): | |||
| """ | |||
| Clean out prior executable output, including repos, catalog, and | |||
| the build results at 'build_dir', if given. | |||
| """ | |||
| if build_dir and build_dir.exists(): | |||
| shutil.rmtree(build_dir) | |||
| if repo and self.repo_dir.exists(): | |||
| shutil.rmtree(self.repo_dir) | |||
| if catalog and self.catalog_path.exists(): | |||
| self.catalog_path.unlink() | |||
| def run(self, args: proc.CommandLine) -> None: | |||
| """Execute the 'dds' executable with the given arguments""" | |||
| proc.check_run([self.path, args]) # type: ignore | |||
| def catalog_json_import(self, path: Path) -> None: | |||
| """Run 'catalog import' to import the given JSON. Only applicable to older 'dds'""" | |||
| self.run(['catalog', 'import', self.catalog_path_arg, f'--json={path}']) | |||
| def build(self, *, toolchain: Path, root: Path, build_root: Path = None, jobs: int = None) -> None: | |||
| """ | |||
| Run 'dds build' with the given arguments. | |||
| :param toolchain: The toolchain to use for the build. | |||
| :param root: The root project directory. | |||
| :param build_root: The root directory where the output will be written. | |||
| :param jobs: The number of jobs to use. Default is CPU-count + 2 | |||
| """ | |||
| jobs = jobs or multiprocessing.cpu_count() + 2 | |||
| self.run([ | |||
| 'build', | |||
| f'--toolchain={toolchain}', | |||
| self.repo_dir_arg, | |||
| self.catalog_path_arg, | |||
| f'--jobs={jobs}', | |||
| f'--project-dir={root}', | |||
| f'--out={build_root}', | |||
| ]) | |||
| @@ -0,0 +1,216 @@ | |||
| import argparse | |||
| import json | |||
| from contextlib import contextmanager | |||
| import enum | |||
| import multiprocessing | |||
| import pytest | |||
| from pathlib import Path | |||
| from concurrent import futures | |||
| import sys | |||
| import os | |||
| from typing import NoReturn, Sequence, Optional, Iterator | |||
| from typing_extensions import Protocol | |||
| import subprocess | |||
| import json5 | |||
| from . import paths | |||
| from .dds import DDSWrapper | |||
| from .bootstrap import BootstrapMode, get_bootstrap_exe | |||
| def make_argparser() -> argparse.ArgumentParser: | |||
| """Create an argument parser for the dds-ci command-line""" | |||
| parser = argparse.ArgumentParser() | |||
| parser.add_argument('-B', | |||
| '--bootstrap-with', | |||
| help='How are we to obtain a bootstrapped DDS executable?', | |||
| metavar='{download,build,skip,lazy}', | |||
| type=BootstrapMode, | |||
| default=BootstrapMode.Lazy) | |||
| parser.add_argument('--rapid', help='Run CI for fast development iterations', action='store_true') | |||
| parser.add_argument('--test-toolchain', | |||
| '-TT', | |||
| type=Path, | |||
| metavar='<toolchain-file>', | |||
| help='The toolchain to use for the first build, which will be passed through the tests') | |||
| parser.add_argument('--main-toolchain', | |||
| '-T', | |||
| type=Path, | |||
| dest='toolchain', | |||
| metavar='<toolchain-file>', | |||
| help='The toolchain to use for the final build') | |||
| parser.add_argument('--jobs', | |||
| '-j', | |||
| type=int, | |||
| help='Number of parallel jobs to use when building and testing', | |||
| default=multiprocessing.cpu_count() + 2) | |||
| parser.add_argument('--build-only', action='store_true', help='Only build the dds executable, do not run tests') | |||
| parser.add_argument('--clean', action='store_true', help="Don't remove prior build/deps results") | |||
| parser.add_argument('--no-test', | |||
| action='store_false', | |||
| dest='do_test', | |||
| help='Skip testing and just build the final result') | |||
| return parser | |||
| class CommandArguments(Protocol): | |||
| """ | |||
| The result of parsing argv with the dds-ci argument parser. | |||
| """ | |||
| #: Whether the user wants us to clean result before building | |||
| clean: bool | |||
| #: The bootstrap method the user has requested | |||
| bootstrap_with: BootstrapMode | |||
| #: The toolchain to use when building the 'dds' executable that will be tested. | |||
| test_toolchain: Optional[Path] | |||
| #: The toolchain to use when building the main 'dds' executable to publish | |||
| toolchain: Optional[Path] | |||
| #: The maximum number of parallel jobs for build and test | |||
| jobs: int | |||
| #: Whether we should run the pytest tests | |||
| do_test: bool | |||
| #: Rapid-CI is for 'dds' development purposes | |||
| rapid: bool | |||
| def parse_argv(argv: Sequence[str]) -> CommandArguments: | |||
| """Parse the given dds-ci command-line argument list""" | |||
| return make_argparser().parse_args(argv) | |||
| @contextmanager | |||
| def fixup_toolchain(json_file: Path) -> Iterator[Path]: | |||
| """ | |||
| Augment the toolchain at the given path by adding 'ccache' or -fuse-ld=lld, | |||
| if those tools are available on the system. Yields a new toolchain file | |||
| based on 'json_file' | |||
| """ | |||
| data = json5.loads(json_file.read_text()) | |||
| # Check if we can add ccache | |||
| ccache = paths.find_exe('ccache') | |||
| if ccache: | |||
| print('Found ccache:', ccache) | |||
| data['compiler_launcher'] = [str(ccache)] | |||
| # Check for lld for use with GCC/Clang | |||
| if paths.find_exe('ld.lld') and data.get('compiler_id') in ('gnu', 'clang'): | |||
| print('Linking with `-fuse-ld=lld`') | |||
| data.setdefault('link_flags', []).append('-fuse-ld=lld') | |||
| # Save the new toolchain data | |||
| with paths.new_tempdir() as tdir: | |||
| new_json = tdir / json_file.name | |||
| new_json.write_text(json.dumps(data)) | |||
| yield new_json | |||
| def get_default_test_toolchain() -> Path: | |||
| """ | |||
| Get the default toolchain that should be used for dev and test based on the | |||
| host platform. | |||
| """ | |||
| if sys.platform == 'win32': | |||
| return paths.TOOLS_DIR / 'msvc-audit.jsonc' | |||
| elif sys.platform in 'linux': | |||
| return paths.TOOLS_DIR / 'gcc-9-audit.jsonc' | |||
| elif sys.platform == 'darwin': | |||
| return paths.TOOLS_DIR / 'gcc-9-audit-macos.jsonc' | |||
| else: | |||
| raise RuntimeError(f'Unable to determine the default toolchain (sys.platform is {sys.platform!r})') | |||
| def get_default_toolchain() -> Path: | |||
| """ | |||
| Get the default toolchain that should be used to generate the release executable | |||
| based on the host platform. | |||
| """ | |||
| if sys.platform == 'win32': | |||
| return paths.TOOLS_DIR / 'msvc-rel.jsonc' | |||
| elif sys.platform == 'linux': | |||
| return paths.TOOLS_DIR / 'gcc-9-rel.jsonc' | |||
| elif sys.platform == 'darwin': | |||
| return paths.TOOLS_DIR / 'gcc-9-rel-macos.jsonc' | |||
| else: | |||
| raise RuntimeError(f'Unable to determine the default toolchain (sys.platform is {sys.platform!r})') | |||
| def test_build(dds: DDSWrapper, args: CommandArguments) -> DDSWrapper: | |||
| """ | |||
| Execute the build that generates the test-mode executable. Uses the given 'dds' | |||
| to build the new dds. Returns a DDSWrapper around the generated test executable. | |||
| """ | |||
| test_tc = args.test_toolchain or get_default_test_toolchain() | |||
| build_dir = paths.BUILD_DIR / '_ci-test' | |||
| with fixup_toolchain(test_tc) as new_tc: | |||
| dds.build(toolchain=new_tc, root=paths.PROJECT_ROOT, build_root=build_dir, jobs=args.jobs) | |||
| return DDSWrapper(build_dir / ('dds' + paths.EXE_SUFFIX)) | |||
| def run_pytest(dds: DDSWrapper, args: CommandArguments) -> int: | |||
| """ | |||
| Execute pytest, testing against the given 'test_dds' executable. Returns | |||
| the exit code of pytest. | |||
| """ | |||
| basetemp = Path('/tmp/dds-ci') | |||
| basetemp.mkdir(exist_ok=True, parents=True) | |||
| return pytest.main([ | |||
| '-v', | |||
| '--durations=10', | |||
| '-n', | |||
| str(args.jobs), | |||
| f'--basetemp={basetemp}', | |||
| f'--dds-exe={dds.path}', | |||
| str(paths.PROJECT_ROOT / 'tests/'), | |||
| ]) | |||
| def main_build(dds: DDSWrapper, args: CommandArguments) -> int: | |||
| """ | |||
| Execute the main build of dds using the given 'dds' executable to build itself. | |||
| """ | |||
| main_tc = args.toolchain or ( | |||
| # If we are in rapid-dev mode, use the test toolchain, which had audit/debug enabled | |||
| get_default_toolchain() if not args.rapid else get_default_test_toolchain()) | |||
| with fixup_toolchain(main_tc) as new_tc: | |||
| try: | |||
| dds.build(toolchain=new_tc, root=paths.PROJECT_ROOT, build_root=paths.BUILD_DIR, jobs=args.jobs) | |||
| except subprocess.CalledProcessError as e: | |||
| if args.rapid: | |||
| return e.returncode | |||
| raise | |||
| return 0 | |||
| def ci_with_dds(dds: DDSWrapper, args: CommandArguments) -> int: | |||
| """ | |||
| Execute CI using the given prior 'dds' executable. | |||
| """ | |||
| if args.clean: | |||
| dds.clean(build_dir=paths.BUILD_DIR) | |||
| dds.catalog_json_import(paths.PROJECT_ROOT / 'old-catalog.json') | |||
| pool = futures.ThreadPoolExecutor() | |||
| test_fut = pool.submit(lambda: 0) | |||
| if args.do_test and not args.rapid: | |||
| test_dds = test_build(dds, args) | |||
| test_fut = pool.submit(lambda: run_pytest(test_dds, args)) | |||
| main_fut = pool.submit(lambda: main_build(dds, args)) | |||
| for fut in futures.as_completed({test_fut, main_fut}): | |||
| if fut.result(): | |||
| return fut.result() | |||
| return 0 | |||
| def main(argv: Sequence[str]) -> int: | |||
| args = parse_argv(argv) | |||
| with get_bootstrap_exe(args.bootstrap_with) as f: | |||
| return ci_with_dds(f, args) | |||
| def start(): | |||
| sys.exit(main(sys.argv[1:])) | |||
| if __name__ == "__main__": | |||
| start() | |||
| @@ -1,12 +1,49 @@ | |||
| import os | |||
| import shutil | |||
| import itertools | |||
| import tempfile | |||
| from contextlib import contextmanager | |||
| from pathlib import Path | |||
| from typing import Iterator, Optional | |||
| TOOLS_DIR = Path(__file__).absolute().parent.parent | |||
| PROJECT_ROOT = TOOLS_DIR.parent | |||
| # The root directory of the dds project | |||
| PROJECT_ROOT = Path(__file__).absolute().parent.parent.parent | |||
| #: The <repo>/tools directory | |||
| TOOLS_DIR = PROJECT_ROOT / 'tools' | |||
| #: The default build directory | |||
| BUILD_DIR = PROJECT_ROOT / '_build' | |||
| #: The directory were w prebuild/bootstrapped results will go, and scratch space for the build | |||
| PREBUILT_DIR = PROJECT_ROOT / '_prebuilt' | |||
| #: THe suffix of executable files on this system | |||
| EXE_SUFFIX = '.exe' if os.name == 'nt' else '' | |||
| #: The path to the prebuilt 'dds' executable | |||
| PREBUILT_DDS = (PREBUILT_DIR / 'dds').with_suffix(EXE_SUFFIX) | |||
| #: The path to the main built 'dds' executable | |||
| CUR_BUILT_DDS = (BUILD_DIR / 'dds').with_suffix(EXE_SUFFIX) | |||
| EMBEDDED_REPO_DIR = PROJECT_ROOT / 'external/repo' | |||
| SELF_TEST_REPO_DIR = BUILD_DIR / '_self-repo' | |||
| @contextmanager | |||
| def new_tempdir() -> Iterator[Path]: | |||
| """ | |||
| Create and yield a new temporary directory, which will be destroyed on | |||
| context-manager exit | |||
| """ | |||
| tdir = Path(tempfile.mkdtemp()) | |||
| try: | |||
| yield tdir | |||
| finally: | |||
| shutil.rmtree(tdir) | |||
| def find_exe(name: str) -> Optional[Path]: | |||
| """ | |||
| Find a file on the system by searching through the PATH environment variable. | |||
| """ | |||
| sep = ';' if os.name == 'nt' else ':' | |||
| paths = os.environ['PATH'].split(sep) | |||
| exts = os.environ['PATHEXT'].split(';') if os.name == 'nt' else [''] | |||
| for dirpath, ext in itertools.product(paths, exts): | |||
| cand = Path(dirpath) / (name + ext) | |||
| if cand.is_file(): | |||
| return cand | |||
| return None | |||
| @@ -0,0 +1,24 @@ | |||
| { | |||
| "$schema": "../res/toolchain-schema.json", | |||
| "compiler_id": "gnu", | |||
| "c_compiler": "gcc-9", | |||
| "cxx_compiler": "g++-9", | |||
| "warning_flags": [ | |||
| "-Werror", | |||
| ], | |||
| "flags": [ | |||
| "-I/usr/local/opt/openssl@1.1/include", | |||
| /// NOTE: Asan/UBsan misbehave on macOS, so we aren't ready to use them in CI | |||
| // "-fsanitize=address,undefined", | |||
| ], | |||
| "cxx_flags": [ | |||
| "-fconcepts", | |||
| "-std=c++2a", | |||
| ], | |||
| "link_flags": [ | |||
| // "-fsanitize=address,undefined", | |||
| "/usr/local/opt/openssl@1.1/lib/libssl.a", | |||
| "/usr/local/opt/openssl@1.1/lib/libcrypto.a", | |||
| ], | |||
| "debug": true | |||
| } | |||
| @@ -7,19 +7,17 @@ | |||
| "-Werror", | |||
| ], | |||
| "flags": [ | |||
| "-fsanitize=address,undefined" | |||
| "-fsanitize=address,undefined", | |||
| ], | |||
| "cxx_flags": [ | |||
| "-fconcepts", | |||
| "-std=c++2a", | |||
| ], | |||
| "link_flags": [ | |||
| "-fuse-ld=lld", | |||
| "-fsanitize=address,undefined", | |||
| "-l:libssl.a", | |||
| "-l:libcrypto.a", | |||
| "-ldl", | |||
| ], | |||
| "debug": true, | |||
| "compiler_launcher": "ccache" | |||
| "debug": true | |||
| } | |||
| @@ -32,7 +32,7 @@ MAX_VERSION = VersionInfo(I32_MAX, I32_MAX, I32_MAX) | |||
| REPO_ROOT = Path(__file__).resolve().absolute().parent.parent | |||
| def dds_exe() -> Path: | |||
| def _get_dds_exe() -> Path: | |||
| suffix = '.exe' if os.name == 'nt' else '' | |||
| dirs = [REPO_ROOT / '_build', REPO_ROOT / '_prebuilt'] | |||
| for d in dirs: | |||
| @@ -79,12 +79,11 @@ class MoveTransform(NamedTuple): | |||
| @classmethod | |||
| def parse_data(cls: Type[T], data: Any) -> T: | |||
| return cls( | |||
| frm=data.pop('from'), | |||
| to=data.pop('to'), | |||
| include=data.pop('include', []), | |||
| strip_components=data.pop('strip-components', 0), | |||
| exclude=data.pop('exclude', [])) | |||
| return cls(frm=data.pop('from'), | |||
| to=data.pop('to'), | |||
| include=data.pop('include', []), | |||
| strip_components=data.pop('strip-components', 0), | |||
| exclude=data.pop('exclude', [])) | |||
| def apply_to(self, p: Path) -> None: | |||
| src = p / self.frm | |||
| @@ -318,12 +317,11 @@ class SpecPackage(NamedTuple): | |||
| deps = data.pop('depends', []) | |||
| desc = data.pop('description', '[No description]') | |||
| remote = ForeignPackage.parse_data(data.pop('remote')) | |||
| return SpecPackage( | |||
| name, | |||
| VersionInfo.parse(version), | |||
| description=desc, | |||
| depends=[Dependency.parse(d) for d in deps], | |||
| remote=remote) | |||
| return SpecPackage(name, | |||
| VersionInfo.parse(version), | |||
| description=desc, | |||
| depends=[Dependency.parse(d) for d in deps], | |||
| remote=remote) | |||
| def iter_spec(path: Path) -> Iterable[SpecPackage]: | |||
| @@ -370,16 +368,17 @@ def http_dl_unpack(url: str) -> Iterator[Path]: | |||
| @contextmanager | |||
| def spec_as_local_tgz(spec: SpecPackage) -> Iterator[Path]: | |||
| def spec_as_local_tgz(dds_exe: Path, spec: SpecPackage) -> Iterator[Path]: | |||
| with spec.remote.make_local_dir(spec.name, spec.version) as clone_dir: | |||
| out_tgz = clone_dir / 'sdist.tgz' | |||
| check_call([str(dds_exe()), 'sdist', 'create', f'--project-dir={clone_dir}', f'--out={out_tgz}']) | |||
| check_call([str(dds_exe), 'sdist', 'create', f'--project-dir={clone_dir}', f'--out={out_tgz}']) | |||
| yield out_tgz | |||
| class Repository: | |||
| def __init__(self, path: Path) -> None: | |||
| def __init__(self, dds_exe: Path, path: Path) -> None: | |||
| self._path = path | |||
| self._dds_exe = dds_exe | |||
| self._import_lock = Lock() | |||
| @property | |||
| @@ -387,19 +386,19 @@ class Repository: | |||
| return self._path / 'pkg' | |||
| @classmethod | |||
| def create(cls, dirpath: Path, name: str) -> 'Repository': | |||
| check_call([str(dds_exe()), 'repoman', 'init', str(dirpath), f'--name={name}']) | |||
| return Repository(dirpath) | |||
| def create(cls, dds_exe: Path, dirpath: Path, name: str) -> 'Repository': | |||
| check_call([str(dds_exe), 'repoman', 'init', str(dirpath), f'--name={name}']) | |||
| return Repository(dds_exe, dirpath) | |||
| @classmethod | |||
| def open(cls, dirpath: Path) -> 'Repository': | |||
| return Repository(dirpath) | |||
| def open(cls, dds_exe: Path, dirpath: Path) -> 'Repository': | |||
| return Repository(dds_exe, dirpath) | |||
| def import_tgz(self, path: Path) -> None: | |||
| check_call([str(dds_exe()), 'repoman', 'import', str(self._path), str(path)]) | |||
| check_call([str(self._dds_exe), 'repoman', 'import', str(self._path), str(path)]) | |||
| def remove(self, name: str) -> None: | |||
| check_call([str(dds_exe()), 'repoman', 'remove', str(self._path), name]) | |||
| check_call([str(self._dds_exe), 'repoman', 'remove', str(self._path), name]) | |||
| def spec_import(self, spec: Path) -> None: | |||
| all_specs = iter_spec(spec) | |||
| @@ -415,7 +414,7 @@ class Repository: | |||
| def _get_and_import(self, spec: SpecPackage) -> None: | |||
| print(f'Import: {spec.name}@{spec.version}') | |||
| with spec_as_local_tgz(spec) as tgz: | |||
| with spec_as_local_tgz(self._dds_exe, spec) as tgz: | |||
| with self._import_lock: | |||
| self.import_tgz(tgz) | |||
| @@ -423,19 +422,20 @@ class Repository: | |||
| class Arguments(Protocol): | |||
| dir: Path | |||
| spec: Path | |||
| dds_exe: Path | |||
| def main(argv: Sequence[str]) -> int: | |||
| parser = argparse.ArgumentParser() | |||
| parser.add_argument('--dds-exe', type=Path, help='Path to the dds executable to use', default=_get_dds_exe()) | |||
| parser.add_argument('--dir', '-d', help='Path to a repository to manage', required=True, type=Path) | |||
| parser.add_argument( | |||
| '--spec', | |||
| metavar='<spec-path>', | |||
| type=Path, | |||
| required=True, | |||
| help='Provide a JSON document specifying how to obtain an import some packages') | |||
| parser.add_argument('--spec', | |||
| metavar='<spec-path>', | |||
| type=Path, | |||
| required=True, | |||
| help='Provide a JSON document specifying how to obtain an import some packages') | |||
| args: Arguments = parser.parse_args(argv) | |||
| repo = Repository.open(args.dir) | |||
| repo = Repository.open(args.dds_exe, args.dir) | |||
| repo.spec_import(args.spec) | |||
| return 0 | |||
| @@ -0,0 +1,30 @@ | |||
| { | |||
| "$schema": "../res/toolchain-schema.json", | |||
| "compiler_id": "msvc", | |||
| "flags": [ | |||
| "/Zc:preprocessor", | |||
| "/Zc:__cplusplus", | |||
| "/std:c++latest", | |||
| "/DNOMINMAX", | |||
| // Workaround quirks in LEAF | |||
| "/DBOOST_LEAF_CONSTEXPR=", | |||
| "/DBOOST_LEAF_STD_UNCAUGHT_EXCEPTIONS=1", | |||
| // OpenSSL headers: | |||
| "/Iexternal/OpenSSL/include", | |||
| ], | |||
| "link_flags": [ | |||
| "rpcrt4.lib", | |||
| // Networking: | |||
| "Ws2_32.lib", | |||
| // Deps for OpenSSL: | |||
| "AdvApi32.lib", | |||
| "Crypt32.lib", | |||
| "User32.lib", | |||
| // Link in our external OpenSSL: | |||
| "/link", | |||
| "/LibPath:external/OpenSSL/lib", | |||
| "libssl.lib", | |||
| "libcrypto.lib", | |||
| ], | |||
| "debug": true | |||
| } | |||
| @@ -26,6 +26,5 @@ | |||
| "libssl.lib", | |||
| "libcrypto.lib", | |||
| ], | |||
| // "debug": true, | |||
| "optimize": true | |||
| } | |||
| @@ -1,61 +0,0 @@ | |||
| #!/usr/bin/env python3 | |||
| import argparse | |||
| from pathlib import Path | |||
| from typing import List, NamedTuple, Iterable | |||
| import shutil | |||
| import subprocess | |||
| import sys | |||
| from dds_ci import cli, proc | |||
| ROOT = Path(__file__).parent.parent.absolute() | |||
| def dds_build(exe: Path, *, toolchain: str, more_flags: proc.CommandLine = ()): | |||
| new_exe = ROOT / '_dds.bootstrap-test.exe' | |||
| shutil.copy2(exe, new_exe) | |||
| try: | |||
| proc.check_run(new_exe, 'build', (f'--toolchain={toolchain}'), more_flags) | |||
| finally: | |||
| new_exe.unlink() | |||
| def self_build(exe: Path, | |||
| *, | |||
| toolchain: str, | |||
| lmi_path: Path = None, | |||
| cat_path: Path = Path('_build/catalog.db'), | |||
| cat_json_path: Path = Path('catalog.json'), | |||
| dds_flags: proc.CommandLine = ()): | |||
| # Copy the exe to another location, as windows refuses to let a binary be | |||
| # replaced while it is executing | |||
| proc.check_run( | |||
| exe, | |||
| 'catalog', | |||
| 'import', | |||
| f'--catalog={cat_path}', | |||
| f'--json={cat_json_path}', | |||
| ) | |||
| dds_build( | |||
| exe, | |||
| toolchain=toolchain, | |||
| more_flags=[ | |||
| ('-I', lmi_path) if lmi_path else (), | |||
| f'--repo-dir={ROOT}/_build/ci-repo', | |||
| f'--catalog={cat_path}', | |||
| *dds_flags, | |||
| ], | |||
| ) | |||
| def main(argv: List[str]) -> int: | |||
| parser = argparse.ArgumentParser() | |||
| cli.add_tc_arg(parser) | |||
| cli.add_dds_exe_arg(parser) | |||
| args = parser.parse_args(argv) | |||
| self_build(Path(args.exe), toolchain=args.toolchain, dds_flags=['--full']) | |||
| return 0 | |||
| if __name__ == "__main__": | |||
| sys.exit(main(sys.argv[1:])) | |||