namespace dds::cli::cmd { | namespace dds::cli::cmd { | ||||
int sdist_create(const options& opts) { | |||||
int pkg_create(const options& opts) { | |||||
dds::sdist_params params{ | dds::sdist_params params{ | ||||
.project_dir = opts.project_dir, | .project_dir = opts.project_dir, | ||||
.dest_path = {}, | .dest_path = {}, |
command build_deps; | command build_deps; | ||||
command build; | command build; | ||||
command compile_file; | command compile_file; | ||||
command pkg_create; | |||||
command pkg_get; | command pkg_get; | ||||
command pkg_import; | command pkg_import; | ||||
command pkg_ls; | command pkg_ls; | ||||
command repoman_init; | command repoman_init; | ||||
command repoman_ls; | command repoman_ls; | ||||
command repoman_remove; | command repoman_remove; | ||||
command sdist_create; | |||||
} // namespace cmd | } // namespace cmd | ||||
switch (opts.subcommand) { | switch (opts.subcommand) { | ||||
case subcommand::build: | case subcommand::build: | ||||
return cmd::build(opts); | return cmd::build(opts); | ||||
case subcommand::sdist: { | |||||
DDS_E_SCOPE(opts.sdist.subcommand); | |||||
switch (opts.sdist.subcommand) { | |||||
case sdist_subcommand::create: | |||||
return cmd::sdist_create(opts); | |||||
case sdist_subcommand::_none_:; | |||||
} | |||||
neo::unreachable(); | |||||
} | |||||
case subcommand::pkg: { | case subcommand::pkg: { | ||||
DDS_E_SCOPE(opts.pkg.subcommand); | DDS_E_SCOPE(opts.pkg.subcommand); | ||||
switch (opts.pkg.subcommand) { | switch (opts.pkg.subcommand) { | ||||
case pkg_subcommand::ls: | case pkg_subcommand::ls: | ||||
return cmd::pkg_ls(opts); | return cmd::pkg_ls(opts); | ||||
case pkg_subcommand::create: | |||||
return cmd::pkg_create(opts); | |||||
case pkg_subcommand::get: | case pkg_subcommand::get: | ||||
return cmd::pkg_get(opts); | return cmd::pkg_get(opts); | ||||
case pkg_subcommand::import: | case pkg_subcommand::import: |
.name = "pkg", | .name = "pkg", | ||||
.help = "Manage packages and package remotes", | .help = "Manage packages and package remotes", | ||||
})); | })); | ||||
setup_sdist_cmd(group.add_parser({ | |||||
.name = "sdist", | |||||
.help = "Work with source distribution packages", | |||||
})); | |||||
setup_repoman_cmd(group.add_parser({ | setup_repoman_cmd(group.add_parser({ | ||||
.name = "repoman", | .name = "repoman", | ||||
.help = "Manage a dds package repository", | .help = "Manage a dds package repository", | ||||
.valname = "<pkg-subcommand>", | .valname = "<pkg-subcommand>", | ||||
.action = put_into(opts.pkg.subcommand), | .action = put_into(opts.pkg.subcommand), | ||||
}); | }); | ||||
setup_pkg_init_db_cmd(pkg_group.add_parser({ | |||||
.name = "init-db", | |||||
.help = "Initialize a new package database file (Path specified with '--pkg-db-path')", | |||||
})); | |||||
pkg_group.add_parser({ | pkg_group.add_parser({ | ||||
.name = "ls", | .name = "ls", | ||||
.help = "List locally available packages", | .help = "List locally available packages", | ||||
}); | }); | ||||
setup_pkg_create_cmd(pkg_group.add_parser({ | |||||
.name = "create", | |||||
.help = "Create a source distribution archive of a project", | |||||
})); | |||||
setup_pkg_get_cmd(pkg_group.add_parser({ | setup_pkg_get_cmd(pkg_group.add_parser({ | ||||
.name = "get", | .name = "get", | ||||
.help = "Obtain a copy of a package from a remote", | .help = "Obtain a copy of a package from a remote", | ||||
})); | })); | ||||
setup_pkg_init_db_cmd(pkg_group.add_parser({ | |||||
.name = "init-db", | |||||
.help = "Initialize a new package database file (Path specified with '--pkg-db-path')", | |||||
})); | |||||
setup_pkg_import_cmd(pkg_group.add_parser({ | setup_pkg_import_cmd(pkg_group.add_parser({ | ||||
.name = "import", | .name = "import", | ||||
.help = "Import a source distribution archive into the local package cache", | .help = "Import a source distribution archive into the local package cache", | ||||
})); | })); | ||||
} | } | ||||
void setup_pkg_create_cmd(argument_parser& pkg_create_cmd) { | |||||
pkg_create_cmd.add_argument(project_arg.dup()).help | |||||
= "Path to the project for which to create a source distribution.\n" | |||||
"Default is the current working directory."; | |||||
pkg_create_cmd.add_argument(out_arg.dup()).help | |||||
= "Destination path for the source distributioon archive"; | |||||
pkg_create_cmd.add_argument(if_exists_arg.dup()).help | |||||
= "What to do if the destination names an existing file"; | |||||
} | |||||
void setup_pkg_get_cmd(argument_parser& pkg_get_cmd) { | void setup_pkg_get_cmd(argument_parser& pkg_get_cmd) { | ||||
pkg_get_cmd.add_argument({ | pkg_get_cmd.add_argument({ | ||||
.valname = "<pkg-id>", | .valname = "<pkg-id>", | ||||
}); | }); | ||||
} | } | ||||
void setup_sdist_cmd(argument_parser& sdist_cmd) noexcept { | |||||
auto& sdist_grp = sdist_cmd.add_subparsers({ | |||||
.valname = "<sdist-subcommand>", | |||||
.action = put_into(opts.sdist.subcommand), | |||||
}); | |||||
setup_sdist_create_cmd(sdist_grp.add_parser({ | |||||
.name = "create", | |||||
.help = "Create a source distribution from a project tree", | |||||
})); | |||||
} | |||||
void setup_sdist_create_cmd(argument_parser& sdist_create_cmd) { | |||||
sdist_create_cmd.add_argument(project_arg.dup()).help | |||||
= "Path to the project for which to create a source distribution.\n" | |||||
"Default is the current working directory."; | |||||
sdist_create_cmd.add_argument(out_arg.dup()).help | |||||
= "Destination path for the source distributnion archive"; | |||||
sdist_create_cmd.add_argument(if_exists_arg.dup()).help | |||||
= "What to do if the destination names an existing file"; | |||||
} | |||||
void setup_repoman_cmd(argument_parser& repoman_cmd) { | void setup_repoman_cmd(argument_parser& repoman_cmd) { | ||||
auto& grp = repoman_cmd.add_subparsers({ | auto& grp = repoman_cmd.add_subparsers({ | ||||
.valname = "<repoman-subcommand>", | .valname = "<repoman-subcommand>", |
compile_file, | compile_file, | ||||
build_deps, | build_deps, | ||||
pkg, | pkg, | ||||
sdist, | |||||
repoman, | repoman, | ||||
}; | }; | ||||
/** | |||||
* @brief 'dds sdist' subcommands | |||||
*/ | |||||
enum class sdist_subcommand { | |||||
_none_, | |||||
create, | |||||
}; | |||||
/** | /** | ||||
* @brief 'dds pkg' subcommands | * @brief 'dds pkg' subcommands | ||||
*/ | */ | ||||
_none_, | _none_, | ||||
ls, | ls, | ||||
get, | get, | ||||
create, | |||||
import, | import, | ||||
repo, | repo, | ||||
search, | search, | ||||
} search; | } search; | ||||
} pkg; | } pkg; | ||||
struct { | |||||
sdist_subcommand subcommand; | |||||
} sdist; | |||||
/** | /** | ||||
* @brief Parameters for 'dds repoman' | * @brief Parameters for 'dds repoman' | ||||
*/ | */ |
def test_empty_sdist_create(tmp_project: Project) -> None: | def test_empty_sdist_create(tmp_project: Project) -> None: | ||||
tmp_project.package_json = TEST_PACKAGE | tmp_project.package_json = TEST_PACKAGE | ||||
tmp_project.sdist_create() | |||||
tmp_project.pkg_create() | |||||
assert tmp_project.build_root.joinpath('test-pkg@0.2.2.tar.gz').is_file(), \ | assert tmp_project.build_root.joinpath('test-pkg@0.2.2.tar.gz').is_file(), \ | ||||
'The expected sdist tarball was not generated' | 'The expected sdist tarball was not generated' |
@pytest.fixture() | @pytest.fixture() | ||||
def test_project(project_opener: ProjectOpener) -> Project: | def test_project(project_opener: ProjectOpener) -> Project: | ||||
return project_opener.open('projects/sdist') | |||||
return project_opener.open('projects/simple') | |||||
def test_create_sdist(test_project: Project, tmp_path: Path) -> None: | |||||
def test_create_pkg(test_project: Project, tmp_path: Path) -> None: | |||||
# Create in the default location | # Create in the default location | ||||
test_project.sdist_create() | |||||
test_project.pkg_create() | |||||
sd_dir = test_project.build_root / 'foo@1.2.3.tar.gz' | sd_dir = test_project.build_root / 'foo@1.2.3.tar.gz' | ||||
assert sd_dir.is_file(), 'Did not create an sdist in the default location' | assert sd_dir.is_file(), 'Did not create an sdist in the default location' | ||||
# Create in a different location | # Create in a different location | ||||
dest = tmp_path / 'dummy.tar.gz' | dest = tmp_path / 'dummy.tar.gz' | ||||
test_project.sdist_create(dest=dest) | |||||
test_project.pkg_create(dest=dest) | |||||
assert dest.is_file(), 'Did not create an sdist in the new location' | assert dest.is_file(), 'Did not create an sdist in the new location' | ||||
@pytest.fixture() | @pytest.fixture() | ||||
def test_sdist(test_project: Project) -> Tuple[Path, Project]: | |||||
def _test_pkg(test_project: Project) -> Tuple[Path, Project]: | |||||
repo_content_path = test_project.dds.repo_dir / 'foo@1.2.3' | repo_content_path = test_project.dds.repo_dir / 'foo@1.2.3' | ||||
assert not repo_content_path.is_dir() | assert not repo_content_path.is_dir() | ||||
test_project.sdist_create() | |||||
test_project.pkg_create() | |||||
assert not repo_content_path.is_dir() | assert not repo_content_path.is_dir() | ||||
return test_project.build_root / 'foo@1.2.3.tar.gz', test_project | return test_project.build_root / 'foo@1.2.3.tar.gz', test_project | ||||
def test_import_sdist_archive(test_sdist: Tuple[Path, Project]) -> None: | |||||
sdist, project = test_sdist | |||||
def test_import_sdist_archive(_test_pkg: Tuple[Path, Project]) -> None: | |||||
sdist, project = _test_pkg | |||||
repo_content_path = project.dds.repo_dir / 'foo@1.2.3' | repo_content_path = project.dds.repo_dir / 'foo@1.2.3' | ||||
project.dds.pkg_import(sdist) | project.dds.pkg_import(sdist) | ||||
assert repo_content_path.is_dir(), \ | assert repo_content_path.is_dir(), \ | ||||
'Non-package content appeared in the package cache' | 'Non-package content appeared in the package cache' | ||||
def test_import_sdist_stdin(test_sdist: Tuple[Path, Project]) -> None: | |||||
sdist, project = test_sdist | |||||
def test_import_sdist_stdin(_test_pkg: Tuple[Path, Project]) -> None: | |||||
sdist, project = _test_pkg | |||||
repo_content_path = project.dds.repo_dir / 'foo@1.2.3' | repo_content_path = project.dds.repo_dir / 'foo@1.2.3' | ||||
pipe = subprocess.Popen( | pipe = subprocess.Popen( | ||||
list(proc.flatten_cmd([ | list(proc.flatten_cmd([ | ||||
rc = pipe.wait() | rc = pipe.wait() | ||||
assert rc == 0, 'Subprocess failed' | assert rc == 0, 'Subprocess failed' | ||||
# project.dds.pkg_import(sdist) | |||||
assert repo_content_path.is_dir(), \ | assert repo_content_path.is_dir(), \ | ||||
'The package did not appear in the local cache' | 'The package did not appear in the local cache' | ||||
assert repo_content_path.joinpath('library.jsonc').is_file(), \ | assert repo_content_path.joinpath('library.jsonc').is_file(), \ | ||||
def test_sdist_invalid_project(tmp_project: Project) -> None: | def test_sdist_invalid_project(tmp_project: Project) -> None: | ||||
with error.expect_error_marker('no-package-json5'): | with error.expect_error_marker('no-package-json5'): | ||||
tmp_project.sdist_create() | |||||
tmp_project.pkg_create() | |||||
@pytest.mark.skipif(platform.system() != 'Linux', reason='We know this fails on Linux') | @pytest.mark.skipif(platform.system() != 'Linux', reason='We know this fails on Linux') | ||||
def test_sdist_unreadable_dir(dds: DDSWrapper) -> None: | def test_sdist_unreadable_dir(dds: DDSWrapper) -> None: | ||||
with error.expect_error_marker('failed-package-json5-scan'): | with error.expect_error_marker('failed-package-json5-scan'): | ||||
dds.run(['sdist', 'create', '--project=/root']) | |||||
dds.run(['pkg', 'create', '--project=/root']) | |||||
def test_sdist_invalid_json5(tmp_project: Project) -> None: | def test_sdist_invalid_json5(tmp_project: Project) -> None: | ||||
tmp_project.write('package.json5', 'bogus json5') | tmp_project.write('package.json5', 'bogus json5') | ||||
with error.expect_error_marker('package-json5-parse-error'): | with error.expect_error_marker('package-json5-parse-error'): | ||||
tmp_project.sdist_create() | |||||
tmp_project.pkg_create() |
with tc_mod.fixup_toolchain(toolchain or tc_mod.get_default_test_toolchain()) as tc: | with tc_mod.fixup_toolchain(toolchain or tc_mod.get_default_test_toolchain()) as tc: | ||||
self.dds.compile_file(paths, toolchain=tc, out=self.build_root, project_dir=self.root) | self.dds.compile_file(paths, toolchain=tc, out=self.build_root, project_dir=self.root) | ||||
def sdist_create(self, *, dest: Optional[Pathish] = None) -> None: | |||||
def pkg_create(self, *, dest: Optional[Pathish] = None) -> None: | |||||
self.build_root.mkdir(exist_ok=True, parents=True) | self.build_root.mkdir(exist_ok=True, parents=True) | ||||
self.dds.run([ | self.dds.run([ | ||||
'sdist', | |||||
'pkg', | |||||
'create', | 'create', | ||||
self.project_dir_arg, | self.project_dir_arg, | ||||
f'--out={dest}' if dest else (), | f'--out={dest}' if dest else (), |
def spec_as_local_tgz(dds_exe: Path, 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: | with spec.remote.make_local_dir(spec.name, spec.version) as clone_dir: | ||||
out_tgz = clone_dir / 'sdist.tgz' | out_tgz = clone_dir / 'sdist.tgz' | ||||
check_call([str(dds_exe), 'sdist', 'create', f'--project={clone_dir}', f'--out={out_tgz}']) | |||||
check_call([str(dds_exe), 'pkg', 'create', f'--project={clone_dir}', f'--out={out_tgz}']) | |||||
yield out_tgz | yield out_tgz | ||||