Browse Source

Update tests and CI to use HTTP repo

default_compile_flags
vector-of-bool 4 years ago
parent
commit
9b639bd187
9 changed files with 3290 additions and 6634 deletions
  1. +3161
    -2360
      catalog.json
  2. +0
    -4141
      catalog.old.json
  3. +24
    -0
      src/dds.main.cpp
  4. +19
    -12
      tests/conftest.py
  5. +10
    -4
      tests/dds.py
  6. +11
    -17
      tests/deps/deps_test.py
  7. +16
    -14
      tools/ci.py
  8. +24
    -66
      tools/gen-catalog-json.py
  9. +25
    -20
      tools/self_build.py

+ 3161
- 2360
catalog.json
File diff suppressed because it is too large
View File


+ 0
- 4141
catalog.old.json
File diff suppressed because it is too large
View File


+ 24
- 0
src/dds.main.cpp View File

{"out"}, {"out"},
dds::fs::current_path() / "_build"}; dds::fs::current_path() / "_build"};


args::ValueFlagList<std::string> add_repos{
cmd,
"<repo-url>",
"Add the given repositories to the catalog before executing (Implies '--update-repos')",
{"add-repo"}};

args::Flag update_repos{cmd,
"update-repos",
"Update repositories before building",
{"update-repos", 'U'}};

int run() { int run() {
if (!add_repos.Get().empty()) {
auto cat = cat_path.open();
for (auto& str : add_repos.Get()) {
auto repo = dds::remote_repository::connect(str);
repo.store(cat.database());
}
}

if (update_repos.Get() || !add_repos.Get().empty()) {
auto cat = cat_path.open();
dds::update_all_remotes(cat.database());
}

dds::sdist_build_params main_params = { dds::sdist_build_params main_params = {
.subdir = "", .subdir = "",
.build_tests = !no_tests.Get(), .build_tests = !no_tests.Get(),

+ 19
- 12
tests/conftest.py View File

from typing import Optional from typing import Optional
from pathlib import Path from pathlib import Path
import shutil import shutil
from subprocess import check_call


import pytest import pytest


from tests import scoped_dds, DDSFixtureParams from tests import scoped_dds, DDSFixtureParams




@pytest.fixture(scope='session')
def dds_exe() -> Path:
return Path(__file__).absolute().parent.parent / '_build/dds'


@pytest.yield_fixture(scope='session')
def dds_pizza_catalog(dds_exe: Path, tmp_path_factory) -> Path:
tmpdir: Path = tmp_path_factory.mktemp(basename='dds-catalog')
cat_path = tmpdir / 'catalog.db'
check_call([str(dds_exe), 'repo', 'add', 'https://dds.pizza/repo', '--update', f'--catalog={cat_path}'])
yield cat_path


@pytest.yield_fixture @pytest.yield_fixture
def dds(request, tmp_path: Path, worker_id: str, scope: ExitStack):
def dds(request, dds_exe: Path, tmp_path: Path, worker_id: str, scope: ExitStack):
test_source_dir = Path(request.fspath).absolute().parent test_source_dir = Path(request.fspath).absolute().parent
test_root = test_source_dir test_root = test_source_dir


project_dir = test_root / params.subdir project_dir = test_root / params.subdir


# Create the instance. Auto-clean when we're done # Create the instance. Auto-clean when we're done
yield scope.enter_context(
scoped_dds(test_root, project_dir, request.function.__name__))
yield scope.enter_context(scoped_dds(dds_exe, test_root, project_dir, request.function.__name__))




@pytest.fixture @pytest.fixture


def pytest_addoption(parser): def pytest_addoption(parser):
parser.addoption( parser.addoption(
'--test-deps',
action='store_true',
default=False,
help='Run the exhaustive and intensive dds-deps tests')
'--test-deps', action='store_true', default=False, help='Run the exhaustive and intensive dds-deps tests')




def pytest_configure(config): def pytest_configure(config):
config.addinivalue_line(
'markers', 'deps_test: Deps tests are slow. Enable with --test-deps')
config.addinivalue_line('markers', 'deps_test: Deps tests are slow. Enable with --test-deps')




def pytest_collection_modifyitems(config, items): def pytest_collection_modifyitems(config, items):
continue continue
item.add_marker( item.add_marker(
pytest.mark.skip( pytest.mark.skip(
reason=
'Exhaustive deps tests are slow and perform many Git clones. Use --test-deps to run them.'
))
reason='Exhaustive deps tests are slow and perform many Git clones. Use --test-deps to run them.'))

+ 10
- 4
tests/dds.py View File

import itertools import itertools
from contextlib import contextmanager, ExitStack from contextlib import contextmanager, ExitStack
from pathlib import Path from pathlib import Path
from typing import Iterable, Union, Any, Dict, NamedTuple, ContextManager
from typing import Iterable, Union, Any, Dict, NamedTuple, ContextManager, Optional
import subprocess import subprocess
import shutil import shutil


args, args,
]) ])


def repo_add(self, url: str) -> None:
return self.run(['repo', 'add', url, '--update', self.catalog_path_arg])

def build(self, def build(self,
*, *,
toolchain: str = None, toolchain: str = None,
apps: bool = True, apps: bool = True,
warnings: bool = True, warnings: bool = True,
catalog_path: Optional[Path] = None,
tests: bool = True, tests: bool = True,
more_args: proc.CommandLine = [],
check: bool = True) -> subprocess.CompletedProcess: check: bool = True) -> subprocess.CompletedProcess:
catalog_path = catalog_path or self.catalog_path.relative_to(self.source_root)
return self.run( return self.run(
[ [
'build', 'build',
f'--out={self.build_dir}', f'--out={self.build_dir}',
f'--toolchain={toolchain or self.default_builtin_toolchain}', f'--toolchain={toolchain or self.default_builtin_toolchain}',
f'--catalog={self.catalog_path.relative_to(self.source_root)}',
f'--catalog={catalog_path}',
f'--repo-dir={self.repo_dir.relative_to(self.source_root)}', f'--repo-dir={self.repo_dir.relative_to(self.source_root)}',
['--no-tests'] if not tests else [], ['--no-tests'] if not tests else [],
['--no-apps'] if not apps else [], ['--no-apps'] if not apps else [],
['--no-warnings'] if not warnings else [], ['--no-warnings'] if not warnings else [],
self.project_dir_arg, self.project_dir_arg,
more_args,
], ],
check=check, check=check,
) )




@contextmanager @contextmanager
def scoped_dds(test_dir: Path, project_dir: Path, name: str):
dds_exe = Path(__file__).absolute().parent.parent / '_build/dds'
def scoped_dds(dds_exe: Path, test_dir: Path, project_dir: Path, name: str):
if os.name == 'nt': if os.name == 'nt':
dds_exe = dds_exe.with_suffix('.exe') dds_exe = dds_exe.with_suffix('.exe')
with ExitStack() as scope: with ExitStack() as scope:

+ 11
- 17
tests/deps/deps_test.py View File

'depends': [self.dep], 'depends': [self.dep],
}).encode())) }).encode()))
dds.scope.enter_context( dds.scope.enter_context(
fileutil.set_contents(
dds.source_root / 'library.json',
json.dumps({
'name': 'test',
'uses': [self.usage],
}).encode()))
dds.scope.enter_context(
fileutil.set_contents(dds.source_root / 'src/test.test.cpp',
self.source.encode()))
fileutil.set_contents(dds.source_root / 'library.json',
json.dumps({
'name': 'test',
'uses': [self.usage],
}).encode()))
dds.scope.enter_context(fileutil.set_contents(dds.source_root / 'src/test.test.cpp', self.source.encode()))




CASES: List[DepsCase] = [] CASES: List[DepsCase] = []




def get_default_pkg_versions(pkg: str) -> Sequence[str]: def get_default_pkg_versions(pkg: str) -> Sequence[str]:
catalog_json = Path(
__file__).resolve().parent.parent.parent / 'catalog.json'
catalog_json = Path(__file__).resolve().parent.parent.parent / 'catalog.json'
catalog_dict = json.loads(catalog_json.read_text()) catalog_dict = json.loads(catalog_json.read_text())
return list(catalog_dict['packages'][pkg].keys()) return list(catalog_dict['packages'][pkg].keys())


## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ##
""" """
add_cases(
'fmt', 'fmt/fmt', ['auto'], r'''
add_cases('fmt', 'fmt/fmt', ['auto'], r'''
#include <fmt/core.h> #include <fmt/core.h>


int main() { int main() {
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
###### ## ######## ######## ####### ###### ###### ## ######## ######## ####### ######
""" """
add_cases(
'spdlog', 'spdlog/spdlog', ['auto'], r'''
add_cases('spdlog', 'spdlog/spdlog', ['auto'], r'''
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>


int main() { int main() {


@pytest.mark.deps_test @pytest.mark.deps_test
@pytest.mark.parametrize('case', CASES, ids=[c.dep for c in CASES]) @pytest.mark.parametrize('case', CASES, ids=[c.dep for c in CASES])
def test_dep(case: DepsCase, dds: DDS) -> None:
def test_dep(case: DepsCase, dds_pizza_catalog: Path, dds: DDS) -> None:
case.setup_root(dds) case.setup_root(dds)
dds.build()
dds.build(catalog_path=dds_pizza_catalog)

+ 16
- 14
tools/ci.py View File

import urllib.request import urllib.request
import shutil import shutil


from self_build import self_build
from self_build import self_build, dds_build
from dds_ci import paths, proc from dds_ci import paths, proc




'freebsd12': 'dds-freebsd-x64', 'freebsd12': 'dds-freebsd-x64',
}.get(sys.platform) }.get(sys.platform)
if filename is None: if filename is None:
raise RuntimeError(f'We do not have a prebuilt DDS binary for '
f'the "{sys.platform}" platform')
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}' url = f'https://github.com/vector-of-bool/dds/releases/download/0.1.0-alpha.4/{filename}'


print(f'Downloading prebuilt DDS executable: {url}') print(f'Downloading prebuilt DDS executable: {url}')
required=True, required=True,
) )
parser.add_argument( parser.add_argument(
'--build-only',
action='store_true',
help='Only build the `dds` executable. Skip second-phase and tests.')
'--build-only', action='store_true', help='Only build the `dds` executable. Skip second-phase and tests.')
parser.add_argument( parser.add_argument(
'--no-clean', '--no-clean',
action='store_false', action='store_false',
paths.PREBUILT_DDS, paths.PREBUILT_DDS,
toolchain=opts.toolchain, toolchain=opts.toolchain,
cat_path=old_cat_path, cat_path=old_cat_path,
cat_json_path=Path('catalog.old.json'),
cat_json_path=Path('catalog.json'),
dds_flags=[('--repo-dir', ci_repo_dir)]) dds_flags=[('--repo-dir', ci_repo_dir)])
print('Main build PASSED!') print('Main build PASSED!')
print(f'A `dds` executable has been generated: {paths.CUR_BUILT_DDS}') print(f'A `dds` executable has been generated: {paths.CUR_BUILT_DDS}')


if args.build_only: if args.build_only:
print(
f'`--build-only` was given, so second phase and tests will not execute'
)
print('`--build-only` was given, so second phase and tests will not execute')
return 0 return 0


print('Bootstrapping myself:') print('Bootstrapping myself:')
new_cat_path = paths.BUILD_DIR / 'catalog.db' new_cat_path = paths.BUILD_DIR / 'catalog.db'
new_repo_dir = paths.BUILD_DIR / 'ci-repo'
self_build(
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, paths.CUR_BUILT_DDS,
toolchain=opts.toolchain, toolchain=opts.toolchain,
cat_path=new_cat_path,
dds_flags=[f'--repo-dir={new_repo_dir}'])
more_flags=[
f'--repo-dir={new_repo_dir}',
f'--catalog={new_cat_path}',
'--add-repo=https://dds.pizza/repo',
])
print('Bootstrap test PASSED!') print('Bootstrap test PASSED!')


return pytest.main([ return pytest.main([

+ 24
- 66
tools/gen-catalog-json.py View File

d['auto-lib'] = self.auto_lib d['auto-lib'] = self.auto_lib
return d return d


def to_dict_2(self) -> str:
url = f'git+{self.url}'
if self.auto_lib:
url += f'?lm={self.auto_lib}'
url += f'#{self.ref}'
return url



RemoteInfo = Union[Git] RemoteInfo = Union[Git]


ret['git'] = self.remote.to_dict() ret['git'] = self.remote.to_dict()
return ret return ret


def to_dict_2(self) -> dict:
ret: dict = {
'description': self.description,
'depends': list(self.depends),
'transform': [f.to_dict() for f in self.remote.transforms],
}
ret['url'] = self.remote.to_dict_2()
return ret



class VersionSet(NamedTuple): class VersionSet(NamedTuple):
version: str version: str
raise RuntimeError(f'Request is outside of api.github.com [{url}]') raise RuntimeError(f'Request is outside of api.github.com [{url}]')
resp = request.urlopen(req) resp = request.urlopen(req)
if resp.status != 200: if resp.status != 200:
raise RuntimeError(
f'Request to [{url}] failed [{resp.status} {resp.reason}]')
raise RuntimeError(f'Request to [{url}] failed [{resp.status} {resp.reason}]')
return json5.loads(resp.read()) return json5.loads(resp.read())




return content return content




def _version_for_github_tag(pkg_name: str, desc: str, clone_url: str,
tag) -> Version:
def _version_for_github_tag(pkg_name: str, desc: str, clone_url: str, tag) -> Version:
print(f'Loading tag {tag["name"]}') print(f'Loading tag {tag["name"]}')
commit = github_http_get(tag['commit']['url']) commit = github_http_get(tag['commit']['url'])
tree = github_http_get(commit['commit']['tree']['url']) tree = github_http_get(commit['commit']['tree']['url'])
package_json_fname = cand package_json_fname = cand
break break
else: else:
raise RuntimeError(
f'No package JSON5 file in tag {tag["name"]} for {pkg_name} (One of {tree_content.keys()})'
)
raise RuntimeError(f'No package JSON5 file in tag {tag["name"]} for {pkg_name} (One of {tree_content.keys()})')


package_json = json5.loads(
_get_github_tree_file_content(tree_content[package_json_fname]['url']))
package_json = json5.loads(_get_github_tree_file_content(tree_content[package_json_fname]['url']))
version = package_json['version'] version = package_json['version']
if pkg_name != package_json['name']: if pkg_name != package_json['name']:
raise RuntimeError(f'package name in repo "{package_json["name"]}" ' raise RuntimeError(f'package name in repo "{package_json["name"]}" '
elif depends is None: elif depends is None:
pairs = [] pairs = []
else: else:
raise RuntimeError(
f'Unknown "depends" object from json file: {depends!r}')
raise RuntimeError(f'Unknown "depends" object from json file: {depends!r}')


remote = Git(url=clone_url, ref=tag['name']) remote = Git(url=clone_url, ref=tag['name'])
return Version(version,
description=desc,
depends=list(pairs),
remote=remote)
return Version(version, description=desc, depends=list(pairs), remote=remote)




def github_package(name: str, repo: str, want_tags: Iterable[str]) -> Package: def github_package(name: str, repo: str, want_tags: Iterable[str]) -> Package:


missing_tags = set(want_tags) - set(t['name'] for t in avail_tags) missing_tags = set(want_tags) - set(t['name'] for t in avail_tags)
if missing_tags: if missing_tags:
raise RuntimeError(
'One or more wanted tags do not exist in '
f'the repository "{repo}" (Missing: {missing_tags})')
raise RuntimeError('One or more wanted tags do not exist in '
f'the repository "{repo}" (Missing: {missing_tags})')


tag_items = (t for t in avail_tags if t['name'] in want_tags) tag_items = (t for t in avail_tags if t['name'] in want_tags)


versions = HTTP_POOL.map(
lambda tag: _version_for_github_tag(name, desc, repo_data['clone_url'],
tag), tag_items)
versions = HTTP_POOL.map(lambda tag: _version_for_github_tag(name, desc, repo_data['clone_url'], tag), tag_items)


return Package(name, list(versions)) return Package(name, list(versions))


*, *,
tag_fmt: str = '{}') -> Package: tag_fmt: str = '{}') -> Package:
return Package(name, [ return Package(name, [
Version(ver.version,
description=description,
remote=Git(
git_url, tag_fmt.format(ver.version), auto_lib=auto_lib),
depends=ver.depends) for ver in versions
Version(
ver.version,
description=description,
remote=Git(git_url, tag_fmt.format(ver.version), auto_lib=auto_lib),
depends=ver.depends) for ver in versions
]) ])




transforms: Sequence[FSTransform] = (), transforms: Sequence[FSTransform] = (),
description='(No description was provided)') -> Package: description='(No description was provided)') -> Package:
return Package(name, [ return Package(name, [
Version(ver,
description='\n'.join(textwrap.wrap(description)),
remote=Git(url=git_url,
ref=tag_fmt.format(ver),
auto_lib=auto_lib,
transforms=transforms)) for ver in versions
Version(
ver,
description='\n'.join(textwrap.wrap(description)),
remote=Git(url=git_url, ref=tag_fmt.format(ver), auto_lib=auto_lib, transforms=transforms))
for ver in versions
]) ])




PACKAGES = [ PACKAGES = [
github_package('neo-buffer', 'vector-of-bool/neo-buffer', github_package('neo-buffer', 'vector-of-bool/neo-buffer',
['0.2.1', '0.3.0', '0.4.0', '0.4.1', '0.4.2']), ['0.2.1', '0.3.0', '0.4.0', '0.4.1', '0.4.2']),
github_package('neo-compress', 'vector-of-bool/neo-compress', ['0.1.0', '0.1.1']),
github_package('neo-compress', 'vector-of-bool/neo-compress', ['0.1.0', '0.1.1', '0.2.0']),
github_package('neo-url', 'vector-of-bool/neo-url', github_package('neo-url', 'vector-of-bool/neo-url',
['0.1.0', '0.1.1', '0.1.2', '0.2.0', '0.2.1', '0.2.2']), ['0.1.0', '0.1.1', '0.1.2', '0.2.0', '0.2.1', '0.2.2']),
github_package('neo-sqlite3', 'vector-of-bool/neo-sqlite3', github_package('neo-sqlite3', 'vector-of-bool/neo-sqlite3',
args = parser.parse_args() args = parser.parse_args()


data = { data = {
'version': 2,
'packages': {
pkg.name: {ver.version: ver.to_dict_2()
for ver in pkg.versions}
for pkg in PACKAGES
}
}
old_data = {
'version': 1, 'version': 1,
'packages': {
pkg.name: {ver.version: ver.to_dict()
for ver in pkg.versions}
for pkg in PACKAGES
}
'packages': {pkg.name: {ver.version: ver.to_dict()
for ver in pkg.versions}
for pkg in PACKAGES}
} }
json_str = json.dumps(data, indent=2, sort_keys=True)
Path('catalog.json').write_text(json_str)
Path('catalog.old.json').write_text(
json.dumps(old_data, indent=2, sort_keys=True))
Path('catalog.json').write_text(json.dumps(data, indent=2, sort_keys=True))

+ 25
- 20
tools/self_build.py View File

ROOT = Path(__file__).parent.parent.absolute() 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, def self_build(exe: Path,
*, *,
toolchain: str, toolchain: str,
dds_flags: proc.CommandLine = ()): dds_flags: proc.CommandLine = ()):
# Copy the exe to another location, as windows refuses to let a binary be # Copy the exe to another location, as windows refuses to let a binary be
# replaced while it is executing # replaced while it is executing
new_exe = ROOT / '_dds.bootstrap-test.exe'
shutil.copy2(exe, new_exe)
try:
proc.check_run(
new_exe,
'catalog',
'import',
f'--catalog={cat_path}',
f'--json={cat_json_path}',
)
proc.check_run(
new_exe,
'build',
f'--catalog={cat_path}',
f'--repo-dir={ROOT}/_build/ci-repo',
dds_flags,
('--toolchain', toolchain),
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 (), ('-I', lmi_path) if lmi_path else (),
)
finally:
new_exe.unlink()
f'--repo-dir={ROOT}/_build/ci-repo',
f'--catalog={cat_path}',
*dds_flags,
],
)




def main(argv: List[str]) -> int: def main(argv: List[str]) -> int:

Loading…
Cancel
Save