mirror of
https://github.com/ryanvolz/radioconda.git
synced 2024-11-08 18:36:02 -05:00
d4224406b8
1. Never clear the full pkg dir 2. Delete environment variables set in registry by PothosSDR
381 lines
12 KiB
Python
Executable File
381 lines
12 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import pathlib
|
|
import shutil
|
|
from typing import List, Optional
|
|
|
|
import conda_lock
|
|
import yaml
|
|
|
|
|
|
def name_from_pkg_spec(spec: str):
|
|
return spec.split(sep=None, maxsplit=1)[0].split(sep="=", maxsplit=1)[0]
|
|
|
|
|
|
def lock_env_spec(
|
|
lock_spec: conda_lock.src_parser.LockSpecification, conda_exe: str
|
|
) -> conda_lock.src_parser.LockSpecification:
|
|
create_env_dict = conda_lock.conda_lock.solve_specs_for_arch(
|
|
conda=conda_exe,
|
|
channels=lock_spec.channels,
|
|
specs=lock_spec.specs,
|
|
platform=lock_spec.platform,
|
|
)
|
|
pkgs = create_env_dict["actions"]["LINK"]
|
|
locked_specs = ["{name}={version}={build_string}".format(**pkg) for pkg in pkgs]
|
|
|
|
locked_env_spec = conda_lock.src_parser.LockSpecification(
|
|
specs=sorted(locked_specs),
|
|
channels=lock_spec.channels,
|
|
platform=lock_spec.platform,
|
|
virtual_package_repo=conda_lock.virtual_package.default_virtual_package_repodata(),
|
|
)
|
|
|
|
return locked_env_spec
|
|
|
|
|
|
def write_env_file(
|
|
env_spec: conda_lock.src_parser.LockSpecification,
|
|
file_path: pathlib.Path,
|
|
name: Optional[str] = None,
|
|
version: Optional[str] = None,
|
|
variables: Optional[dict] = None,
|
|
):
|
|
env_dict = dict(
|
|
name=name,
|
|
version=version,
|
|
platform=env_spec.platform,
|
|
channels=env_spec.channels,
|
|
dependencies=env_spec.specs,
|
|
)
|
|
if name:
|
|
env_dict["name"] = name
|
|
if version:
|
|
env_dict["version"] = version
|
|
if variables:
|
|
env_dict["variables"] = variables
|
|
with file_path.open("w") as f:
|
|
yaml.safe_dump(env_dict, stream=f)
|
|
|
|
return env_dict
|
|
|
|
|
|
def write_lock_file(
|
|
lock_spec: conda_lock.src_parser.LockSpecification,
|
|
file_path: pathlib.Path,
|
|
conda_exe: str,
|
|
):
|
|
lockfile_contents = conda_lock.conda_lock.create_lockfile_from_spec(
|
|
conda=conda_exe, spec=lock_spec, kind="explicit"
|
|
)
|
|
|
|
def sanitize_lockfile_line(line):
|
|
line = line.strip()
|
|
if line == "":
|
|
return "#"
|
|
else:
|
|
return line
|
|
|
|
lockfile_contents = [sanitize_lockfile_line(ln) for ln in lockfile_contents]
|
|
|
|
with file_path.open("w") as f:
|
|
f.write("\n".join(lockfile_contents) + "\n")
|
|
|
|
return lockfile_contents
|
|
|
|
|
|
def render_constructor(
|
|
lock_spec: conda_lock.src_parser.LockSpecification,
|
|
name: str,
|
|
version: str,
|
|
company: str,
|
|
license_file: pathlib.Path,
|
|
output_dir: pathlib.Path,
|
|
) -> dict:
|
|
platform = lock_spec.platform
|
|
constructor_name = f"{name}-{platform}"
|
|
|
|
construct_dict = dict(
|
|
name=name,
|
|
version=version,
|
|
company=company,
|
|
channels=lock_spec.channels,
|
|
specs=lock_spec.specs,
|
|
initialize_by_default=False if platform.startswith("win") else True,
|
|
installer_type="all",
|
|
keep_pkgs=True,
|
|
license_file="LICENSE",
|
|
register_python_default=False,
|
|
write_condarc=True,
|
|
)
|
|
if platform.startswith("win"):
|
|
construct_dict["post_install"] = "post_install.bat"
|
|
# point to template that we generate at build time with a patch over default
|
|
construct_dict["nsis_template"] = "main.nsi.tmpl"
|
|
else:
|
|
construct_dict["post_install"] = "post_install.sh"
|
|
|
|
constructor_dir = output_dir / constructor_name
|
|
if constructor_dir.exists():
|
|
shutil.rmtree(constructor_dir)
|
|
constructor_dir.mkdir(parents=True)
|
|
|
|
# copy license to the constructor directory
|
|
shutil.copy(license_file, constructor_dir / "LICENSE")
|
|
|
|
# write the post_install scripts referenced in the construct dict
|
|
if platform.startswith("win"):
|
|
with (constructor_dir / "post_install.bat").open("w") as f:
|
|
f.write(
|
|
"\n".join(
|
|
(
|
|
r'echo {"env_vars": {"GR_PREFIX": "", "GRC_BLOCKS_PATH": "", "UHD_PKG_PATH": "", "VOLK_PREFIX": ""}}>%PREFIX%\conda-meta\state',
|
|
r"del /q %PREFIX%\pkgs\*.tar.bz2",
|
|
"exit 0",
|
|
"",
|
|
)
|
|
)
|
|
)
|
|
else:
|
|
with (constructor_dir / "post_install.sh").open("w") as f:
|
|
f.write(
|
|
"\n".join(
|
|
(
|
|
"#!/bin/sh",
|
|
f'PREFIX="${{PREFIX:-$2/{name}}}"',
|
|
r"rm -f $PREFIX/pkgs/*.tar.bz2",
|
|
"exit 0",
|
|
"",
|
|
)
|
|
)
|
|
)
|
|
|
|
construct_yaml_path = constructor_dir / "construct.yaml"
|
|
with construct_yaml_path.open("w") as f:
|
|
yaml.safe_dump(construct_dict, stream=f)
|
|
|
|
return construct_dict
|
|
|
|
|
|
def render_platforms(
|
|
environment_file: pathlib.Path,
|
|
installer_environment_file: pathlib.Path,
|
|
version: str,
|
|
company: str,
|
|
license_file: pathlib.Path,
|
|
output_dir: pathlib.Path,
|
|
conda_exe: str,
|
|
) -> dict:
|
|
with environment_file.open("r") as f:
|
|
env_yaml_data = yaml.safe_load(f)
|
|
|
|
env_name = env_yaml_data["name"]
|
|
platforms = env_yaml_data["platforms"]
|
|
|
|
if not license_file.exists():
|
|
raise ValueError(f"Cannot find license file: {license_file}")
|
|
|
|
if output_dir.exists():
|
|
shutil.rmtree(output_dir)
|
|
output_dir.mkdir(parents=True)
|
|
|
|
rendered_platforms = {}
|
|
|
|
for platform in platforms:
|
|
output_name = f"{env_name}-{platform}"
|
|
|
|
# get the environment specification for the list of packages from the env file
|
|
env_spec = conda_lock.conda_lock.parse_environment_file(
|
|
environment_file=environment_file, platform=platform
|
|
)
|
|
|
|
# lock the full environment specification to specific versions and builds
|
|
locked_env_spec = lock_env_spec(env_spec, conda_exe)
|
|
|
|
if platform.startswith("win"):
|
|
variables = dict(
|
|
GR_PREFIX="", GRC_BLOCKS_PATH="", UHD_PKG_PATH="", VOLK_PREFIX=""
|
|
)
|
|
else:
|
|
variables = None
|
|
|
|
# write the full environment specification to a yaml file (to build metapackage)
|
|
locked_env_dict = write_env_file(
|
|
env_spec=locked_env_spec,
|
|
file_path=output_dir / f"{output_name}.yml",
|
|
name=env_name,
|
|
version=version,
|
|
variables=variables,
|
|
)
|
|
|
|
# write the full environment specification to a lock file (to install from file)
|
|
lockfile_contents = write_lock_file(
|
|
lock_spec=locked_env_spec,
|
|
file_path=output_dir / f"{output_name}.lock",
|
|
conda_exe=conda_exe,
|
|
)
|
|
|
|
# add installer-only (base environment) packages and lock those too
|
|
installer_pkg_spec = conda_lock.conda_lock.parse_environment_file(
|
|
environment_file=installer_environment_file, platform=platform
|
|
)
|
|
installer_spec = conda_lock.src_parser.LockSpecification(
|
|
specs=sorted(locked_env_spec.specs + installer_pkg_spec.specs),
|
|
channels=sorted(
|
|
set(locked_env_spec.channels) | set(installer_pkg_spec.channels)
|
|
),
|
|
platform=locked_env_spec.platform,
|
|
)
|
|
locked_installer_spec = lock_env_spec(installer_spec, conda_exe)
|
|
|
|
# get a set of only the packages to put in the constructor specification
|
|
# taken from the installer-only list and those explicitly selected originally
|
|
constructor_pkg_names = set(
|
|
name_from_pkg_spec(spec)
|
|
for spec in env_spec.specs + installer_pkg_spec.specs
|
|
)
|
|
|
|
# filter the installer spec by the constructor package names
|
|
constructor_pkg_specs = [
|
|
spec
|
|
for spec in locked_installer_spec.specs
|
|
if name_from_pkg_spec(spec) in constructor_pkg_names
|
|
]
|
|
constructor_spec = conda_lock.src_parser.LockSpecification(
|
|
specs=constructor_pkg_specs,
|
|
channels=locked_installer_spec.channels,
|
|
platform=locked_installer_spec.platform,
|
|
)
|
|
|
|
# create the rendered constructor directory
|
|
constructor_dict = render_constructor(
|
|
lock_spec=constructor_spec,
|
|
name=env_name,
|
|
version=version,
|
|
company=company,
|
|
license_file=license_file,
|
|
output_dir=output_dir,
|
|
)
|
|
|
|
# aggregate output
|
|
rendered_platforms[output_name] = dict(
|
|
locked_env_spec=locked_env_spec,
|
|
locked_env_dict=locked_env_dict,
|
|
lockfile_contents=lockfile_contents,
|
|
locked_installer_spec=locked_installer_spec,
|
|
constructor_dict=constructor_dict,
|
|
)
|
|
|
|
return rendered_platforms
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import argparse
|
|
import datetime
|
|
import os
|
|
|
|
cwd = pathlib.Path(".").absolute()
|
|
here = pathlib.Path(__file__).parent.absolute().relative_to(cwd)
|
|
distname = os.getenv("DISTNAME", "radioconda")
|
|
source = "/".join(
|
|
(
|
|
os.getenv("GITHUB_SERVER_URL", "https://github.com"),
|
|
os.getenv("GITHUB_REPOSITORY", "ryanvolz/radioconda"),
|
|
)
|
|
)
|
|
|
|
dt = datetime.datetime.now()
|
|
version = dt.strftime("%Y.%m.%d")
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description=(
|
|
"Re-render installer specification directories to be used by conda"
|
|
" constructor."
|
|
)
|
|
)
|
|
parser.add_argument(
|
|
"environment_file",
|
|
type=pathlib.Path,
|
|
nargs="?",
|
|
default=here / f"{distname}.yaml",
|
|
help=(
|
|
"YAML file defining a distribution, with a 'name' string and"
|
|
" 'channels', 'platforms', and 'dependencies' lists."
|
|
" (default: %(default)s)"
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"installer_environment_file",
|
|
type=pathlib.Path,
|
|
nargs="?",
|
|
default=here / f"{distname}_installer.yaml",
|
|
help=(
|
|
"YAML file defining additional packages for the installer, with a 'name'"
|
|
" string and 'channels' and 'dependencies' lists."
|
|
" (default: %(default)s)"
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"-v",
|
|
"--version",
|
|
type=str,
|
|
default=version,
|
|
help=(
|
|
"Version tag for the installer, defaults to the current date."
|
|
" (default: %(default)s)"
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--company",
|
|
type=str,
|
|
default=source,
|
|
help=(
|
|
"Name of the company/entity who is responsible for the installer."
|
|
" (default: %(default)s)"
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"-l",
|
|
"--license_file",
|
|
type=pathlib.Path,
|
|
default=here / "LICENSE",
|
|
help=(
|
|
"File containing the license that applies to the installer."
|
|
" (default: %(default)s)"
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"-o",
|
|
"--output_dir",
|
|
type=pathlib.Path,
|
|
default=here / "installer_specs",
|
|
help=(
|
|
"Output directory in which the installer specifications will be rendered."
|
|
" (default: %(default)s)"
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
"--conda-exe",
|
|
type=str,
|
|
default=None,
|
|
help=(
|
|
"Path to the conda (or mamba or micromamba) executable to use."
|
|
" (default: search for conda/mamba/micromamba)"
|
|
),
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
conda_exe = conda_lock.conda_lock.determine_conda_executable(
|
|
conda_executable=args.conda_exe, mamba=True, micromamba=True
|
|
)
|
|
|
|
constructor_specs = render_platforms(
|
|
environment_file=args.environment_file,
|
|
installer_environment_file=args.installer_environment_file,
|
|
version=args.version,
|
|
company=args.company,
|
|
license_file=args.license_file,
|
|
output_dir=args.output_dir,
|
|
conda_exe=conda_exe,
|
|
)
|