Files
modulus/modulus/packaging.py
T
khodowany fb93acca1a
publish / publish (push) Successful in 11m4s
Cleanup
2026-10-08 01:31:14 -05:00

250 lines
8.5 KiB
Python

import argparse
import pathlib
import shutil
import sys
import modulus.artifacts
import modulus.build
import modulus.conan
import modulus.container
import modulus.process
import modulus.publishing
import modulus.python
def create_package(context, target, update, no_remote=False):
command = [
"conan",
"create",
".",
f"--profile:host={target.profile}",
f"--profile:build={context.default_target.profile}",
f"--build={modulus.publishing.package_name(context)}/*",
*modulus.build.dependency_build_args(context),
"-o",
"&:with_tests=False",
]
if update:
command.append("--update")
if no_remote:
command.append("--no-remote")
return modulus.process.run(context, command)
def upload_all_packages(context, remote, force=False):
command = [
"conan",
"upload",
f"{modulus.publishing.package_name(context)}/latest:*",
"-r",
remote,
"--confirm",
]
if force:
command.append("--force")
return modulus.process.run(context, command)
def upload_packages(context, replace=False):
if replace and not modulus.publishing.allow_package_replace():
print("--replace requires MODULUS_ALLOW_PACKAGE_REPLACE=1", file=sys.stderr)
return 1
remote = modulus.conan.conan_remote(context)
try:
status = modulus.conan.configure_conan_remote(context, authenticate=True)
if status == 0:
status = upload_all_packages(context, remote, force=replace)
finally:
logout_status = modulus.process.run(context, ["conan", "remote", "logout", remote])
status = status or logout_status
if status != 0 and not replace:
print(f"upload failed for {modulus.publishing.package_name(context)}/latest. Use --replace with MODULUS_ALLOW_PACKAGE_REPLACE=1 to intentionally replace an existing version.", file=sys.stderr)
return status
def package_artifact_conan_option_args(context):
result = []
for name, value in context.artifact["conan_options"].items():
result.extend(["-o", f"&:{name}={modulus.build.option_value(value)}"])
return result
def package_artifact_cmake_definition_args(context):
return modulus.build.cmake_definition_args(context.artifact["cmake_definitions"])
def package_artifact_targets(context, names, all_targets):
if all_targets or names:
return context.selected_targets(names, all_targets)
configured = context.artifact["targets"]
if configured:
return [context.target(name) for name in configured]
return [context.default_target]
def package_artifact_build_root(context, target):
return context.common_dir / "artifacts" / target.name
def package_artifact_dist_dir(context):
return context.build_root / "packages"
def package_artifact_install_prefix(context, stage_root):
install_prefix = pathlib.Path(str(context.artifact["install_prefix"]))
if str(install_prefix) == ".":
return stage_root
return stage_root / install_prefix
def create_artifact_package(context, target, update, no_remote=False):
modulus.python.interpreter(context, install=True, offline=no_remote)
root = package_artifact_build_root(context, target)
build_dir = root / "build"
stage_root = root / "stage"
conan_dir = build_dir / "conan"
if root.exists():
shutil.rmtree(root)
build_dir.mkdir(parents=True)
stage_root.mkdir(parents=True)
install_prefix = package_artifact_install_prefix(context, stage_root)
install_prefix.mkdir(parents=True, exist_ok=True)
conan_command = [
"conan",
"install",
".",
f"--output-folder={conan_dir}",
f"--profile:host={target.profile}",
f"--profile:build={context.default_target.profile}",
*modulus.build.dependency_build_args(context),
"-o",
"&:with_tests=False",
*package_artifact_conan_option_args(context),
]
if update:
conan_command.append("--update")
if no_remote:
conan_command.append("--no-remote")
status = modulus.process.run(context, conan_command)
if status != 0:
return status, []
status = modulus.process.run(
context,
[
"cmake",
"-S",
".",
"-B",
build_dir,
"-G",
"Ninja",
f"-DCMAKE_TOOLCHAIN_FILE={conan_dir / 'conan_toolchain.cmake'}",
f"-DCMAKE_BUILD_TYPE={target.build_type}",
"-DBUILD_TESTING=OFF",
f"-DCMAKE_PREFIX_PATH={conan_dir}",
f"-DCMAKE_INSTALL_PREFIX={install_prefix}",
*modulus.build.cmake_definition_args(modulus.python.cmake_definitions(context)),
*package_artifact_cmake_definition_args(context),
],
)
if status != 0:
return status, []
status = modulus.process.run(
context,
[
"cmake",
"--build",
build_dir,
"--parallel",
"2",
],
)
if status != 0:
return status, []
status = modulus.process.run(
context,
[
"cmake",
"--install",
build_dir,
],
)
if status != 0:
return status, []
dist_dir = package_artifact_dist_dir(context)
dist_dir.mkdir(parents=True, exist_ok=True)
archive = dist_dir / modulus.artifacts.archive_name(modulus.publishing.package_name(context), target.name)
if archive.exists():
archive.unlink()
modulus.artifacts.create_archive(stage_root, archive)
checksum = modulus.artifacts.write_checksum(archive)
print(f"Created {archive}")
print(f"Created {checksum}")
return 0, [archive, checksum]
def package_artifacts(context, argv):
if not modulus.process.in_docker(context):
return modulus.container.docker_run(
context,
[
f"./{context.tools_rel}/package.py",
*argv,
],
)
parser = argparse.ArgumentParser(description=f"Create {context.package['name']} application tarballs.")
parser.add_argument(
"--target",
action="append",
default=[],
choices=sorted(context.targets),
help="Target to package. May be passed more than once. Defaults to package.artifact.targets.",
)
parser.add_argument("--all", action="store_true", help="Package all supported targets.")
parser.add_argument("--update", action="store_true", help="Check Conan remotes for newer dependency revisions.")
parser.add_argument("--no-remote", action="store_true", help="Do not query Conan remotes or upload package artifacts.")
args = parser.parse_args(argv)
context.conan_cache_dir.mkdir(parents=True, exist_ok=True)
if modulus.publishing.should_upload_artifacts(context) and not args.no_remote and modulus.publishing.package_registry_credentials() is None:
return 1
if not args.no_remote:
status = modulus.conan.configure_conan_remote(context)
if status != 0:
return status
artifacts = []
for target in package_artifact_targets(context, args.target, args.all):
status, created = create_artifact_package(context, target, args.update, no_remote=args.no_remote)
if status != 0:
return status
artifacts.extend(created)
if modulus.publishing.should_upload_artifacts(context) and not args.no_remote:
return modulus.artifacts.main(context, [])
return 0
def package(context, argv):
if modulus.publishing.artifact_packaging_enabled(context):
return package_artifacts(context, argv)
if not modulus.process.in_docker(context):
return modulus.container.docker_run(
context,
[
f"./{context.tools_rel}/package.py",
*argv,
],
)
parser = argparse.ArgumentParser(description=f"Create {context.package['name']} Conan packages.")
parser.add_argument(
"--target",
action="append",
default=[],
choices=sorted(context.targets),
help="Target to package. May be passed more than once. Defaults to the default target.",
)
parser.add_argument("--all", action="store_true", help="Package all supported targets.")
parser.add_argument("--update", action="store_true", help="Check Conan remotes for newer dependency revisions.")
parser.add_argument("--no-remote", action="store_true", help="Do not query Conan remotes. Requires all dependencies to be available locally or buildable from local recipes.")
parser.add_argument("--replace", action="store_true", help="Delete the remote package version before upload. Requires MODULUS_ALLOW_PACKAGE_REPLACE=1.")
args = parser.parse_args(argv)
context.conan_cache_dir.mkdir(parents=True, exist_ok=True)
if not args.no_remote:
status = modulus.conan.configure_conan_remote(context)
if status != 0:
return status
update = args.update
for target in context.selected_targets(args.target, args.all):
status = create_package(context, target, update, no_remote=args.no_remote)
if status != 0:
return status
if modulus.publishing.should_upload(context) and not args.no_remote:
return upload_packages(context, replace=args.replace)
return 0