Compare commits
2 Commits
71e7a5eab9
...
f396ad1def
Author | SHA1 | Date |
---|---|---|
Ryan Volz | f396ad1def | |
Ryan Volz | 33b03ddbcb |
|
@ -1,6 +1,4 @@
|
||||||
# radioconda
|
# ![radioconda](static/radioconda_logo.png)
|
||||||
|
|
||||||
![Build radioconda](https://github.com/ryanvolz/radioconda/actions/workflows/build_radioconda.yml/badge.svg)
|
|
||||||
|
|
||||||
This repository holds cross-platform installers for a collection of open source **software radio** packages bundled with the [conda](https://conda.io/) package manager, including
|
This repository holds cross-platform installers for a collection of open source **software radio** packages bundled with the [conda](https://conda.io/) package manager, including
|
||||||
|
|
||||||
|
@ -89,7 +87,7 @@ You will mostly use radioconda through the command line, although on Windows som
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
Launch a terminal by running "Conda Prompt" in the "radioconda" directory in the Start menu. From this command line, you can run `mamba` to install/upgrade packages or run any of the applications installed with radioconda. Some applications can also be launched through shortcuts added to the Start menu.
|
Launch "Conda Prompt" from the Start menu (you can find it under the "radioconda" directory). From this command line, you can run `mamba` to install/upgrade packages or run any of the applications installed with radioconda. Some applications can also be launched through shortcuts added to the Start menu.
|
||||||
|
|
||||||
### Linux and macOS
|
### Linux and macOS
|
||||||
|
|
||||||
|
@ -324,6 +322,8 @@ Many USB devices use libusb and need a WinUSB driver installed on Windows. Follo
|
||||||
|
|
||||||
## Developers
|
## Developers
|
||||||
|
|
||||||
|
![Build radioconda](https://github.com/ryanvolz/radioconda/actions/workflows/build_radioconda.yml/badge.svg)
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
Each installer package is built from a specification directory in [installer_specs](https://github.com/ryanvolz/radioconda/tree/master/installer_specs) using [conda constructor](https://github.com/conda/constructor). An installer can be built manually using the [build_installer.py](https://github.com/ryanvolz/radioconda/blob/master/build_installer.py) script. The specification directories set the exact versions of the included packages so that `constructor` will produce a predictable result that can be tracked by git for each release. In turn, the specification directories are created/updated by _re-rendering_ the radioconda [environment specification file](https://github.com/ryanvolz/radioconda/blob/master/radioconda.yaml) using the [rerender.py](https://github.com/ryanvolz/radioconda/blob/master/rerender.py) script.
|
Each installer package is built from a specification directory in [installer_specs](https://github.com/ryanvolz/radioconda/tree/master/installer_specs) using [conda constructor](https://github.com/conda/constructor). An installer can be built manually using the [build_installer.py](https://github.com/ryanvolz/radioconda/blob/master/build_installer.py) script. The specification directories set the exact versions of the included packages so that `constructor` will produce a predictable result that can be tracked by git for each release. In turn, the specification directories are created/updated by _re-rendering_ the radioconda [environment specification file](https://github.com/ryanvolz/radioconda/blob/master/radioconda.yaml) using the [rerender.py](https://github.com/ryanvolz/radioconda/blob/master/rerender.py) script.
|
||||||
|
|
75
rerender.py
75
rerender.py
|
@ -1,14 +1,35 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import math
|
||||||
import pathlib
|
import pathlib
|
||||||
import shutil
|
import shutil
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
import conda_lock
|
import conda_lock
|
||||||
from conda_package_streaming.package_streaming import stream_conda_component
|
|
||||||
from conda_package_streaming.url import conda_reader_for_url
|
|
||||||
import diff_match_patch
|
import diff_match_patch
|
||||||
import yaml
|
import yaml
|
||||||
|
from conda_package_streaming.package_streaming import stream_conda_component
|
||||||
|
from conda_package_streaming.url import conda_reader_for_url
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
def resize_contain(image, size, resample=Image.LANCZOS, bg_color=(255, 255, 255, 0)):
|
||||||
|
"""
|
||||||
|
Resize image according to size.
|
||||||
|
image: a Pillow image instance
|
||||||
|
size: a list of two integers [width, height]
|
||||||
|
"""
|
||||||
|
img_format = image.format
|
||||||
|
img = image.copy()
|
||||||
|
img.thumbnail((size[0], size[1]), resample)
|
||||||
|
background = Image.new("RGBA", (size[0], size[1]), bg_color)
|
||||||
|
img_position = (
|
||||||
|
int(math.ceil((size[0] - img.size[0]) / 2)),
|
||||||
|
int(math.ceil((size[1] - img.size[1]) / 2)),
|
||||||
|
)
|
||||||
|
background.paste(img, img_position)
|
||||||
|
background.format = img_format
|
||||||
|
return background.convert("RGBA")
|
||||||
|
|
||||||
|
|
||||||
def name_from_pkg_spec(spec: str):
|
def name_from_pkg_spec(spec: str):
|
||||||
|
@ -98,6 +119,7 @@ def render_constructors(
|
||||||
license_file: pathlib.Path,
|
license_file: pathlib.Path,
|
||||||
output_dir: pathlib.Path,
|
output_dir: pathlib.Path,
|
||||||
builder_lockfile_path: pathlib.Path,
|
builder_lockfile_path: pathlib.Path,
|
||||||
|
logo_path: Optional[pathlib.Path] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
lock_content = conda_lock.conda_lock.parse_conda_lock_file(lockfile_path)
|
lock_content = conda_lock.conda_lock.parse_conda_lock_file(lockfile_path)
|
||||||
lock_work_dir = lockfile_path.parent
|
lock_work_dir = lockfile_path.parent
|
||||||
|
@ -119,10 +141,18 @@ def render_constructors(
|
||||||
extras=("installer",),
|
extras=("installer",),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if logo_path is not None:
|
||||||
|
logo = Image.open(logo_path)
|
||||||
|
|
||||||
for platform_env_yaml_path in lock_work_dir.glob("*.constructor.yml"):
|
for platform_env_yaml_path in lock_work_dir.glob("*.constructor.yml"):
|
||||||
constructor_name = platform_env_yaml_path.name.partition(".")[0]
|
constructor_name = platform_env_yaml_path.name.partition(".")[0]
|
||||||
platform = constructor_name.split(sep="-", maxsplit=1)[1]
|
platform = constructor_name.split(sep="-", maxsplit=1)[1]
|
||||||
|
|
||||||
|
constructor_dir = output_dir / constructor_name
|
||||||
|
if constructor_dir.exists():
|
||||||
|
shutil.rmtree(constructor_dir)
|
||||||
|
constructor_dir.mkdir(parents=True)
|
||||||
|
|
||||||
with platform_env_yaml_path.open("r") as f:
|
with platform_env_yaml_path.open("r") as f:
|
||||||
platform_env_dict = yaml.safe_load(f)
|
platform_env_dict = yaml.safe_load(f)
|
||||||
|
|
||||||
|
@ -152,6 +182,29 @@ def render_constructors(
|
||||||
channel_priority="strict",
|
channel_priority="strict",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
if logo_path is not None:
|
||||||
|
if platform.startswith("win"):
|
||||||
|
# convert to RGB (no transparency) and set white background
|
||||||
|
# because constructor eventually converts to bmp without transparency
|
||||||
|
welcome_image = resize_contain(
|
||||||
|
logo.convert("RGB"), (164, 314), bg_color=(255, 255, 255, 255)
|
||||||
|
)
|
||||||
|
header_image = resize_contain(
|
||||||
|
logo.convert("RGB"), (150, 57), bg_color=(255, 255, 255, 255)
|
||||||
|
)
|
||||||
|
icon_image = resize_contain(logo, (256, 256))
|
||||||
|
|
||||||
|
welcome_image.save(constructor_dir / "welcome.png")
|
||||||
|
header_image.save(constructor_dir / "header.png")
|
||||||
|
icon_image.save(constructor_dir / "icon.png")
|
||||||
|
|
||||||
|
construct_dict["welcome_image"] = "welcome.png"
|
||||||
|
construct_dict["header_image"] = "header.png"
|
||||||
|
construct_dict["icon_image"] = "icon.png"
|
||||||
|
elif platform.startswith("osx"):
|
||||||
|
welcome_image = resize_contain(logo, (1227, 600))
|
||||||
|
welcome_image.save(constructor_dir / "welcome.png")
|
||||||
|
construct_dict["welcome_image"] = "welcome.png"
|
||||||
if platform.startswith("win"):
|
if platform.startswith("win"):
|
||||||
construct_dict["post_install"] = "post_install.bat"
|
construct_dict["post_install"] = "post_install.bat"
|
||||||
# point to template that we generate at build time with a patch over default
|
# point to template that we generate at build time with a patch over default
|
||||||
|
@ -159,11 +212,6 @@ def render_constructors(
|
||||||
else:
|
else:
|
||||||
construct_dict["post_install"] = "post_install.sh"
|
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
|
# copy license to the constructor directory
|
||||||
shutil.copy(license_file, constructor_dir / "LICENSE")
|
shutil.copy(license_file, constructor_dir / "LICENSE")
|
||||||
|
|
||||||
|
@ -263,6 +311,7 @@ def render(
|
||||||
license_file: pathlib.Path,
|
license_file: pathlib.Path,
|
||||||
output_dir: pathlib.Path,
|
output_dir: pathlib.Path,
|
||||||
conda_exe: pathlib.Path,
|
conda_exe: pathlib.Path,
|
||||||
|
logo_path: Optional[pathlib.Path] = None,
|
||||||
dirty: Optional[bool] = False,
|
dirty: Optional[bool] = False,
|
||||||
keep_workdir: Optional[bool] = False,
|
keep_workdir: Optional[bool] = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -337,6 +386,7 @@ def render(
|
||||||
license_file=license_file,
|
license_file=license_file,
|
||||||
output_dir=output_dir,
|
output_dir=output_dir,
|
||||||
builder_lockfile_path=builder_lockfile_path,
|
builder_lockfile_path=builder_lockfile_path,
|
||||||
|
logo_path=logo_path,
|
||||||
)
|
)
|
||||||
|
|
||||||
# clean up conda-lock work dir
|
# clean up conda-lock work dir
|
||||||
|
@ -431,6 +481,16 @@ if __name__ == "__main__":
|
||||||
" (default: %(default)s)"
|
" (default: %(default)s)"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--logo",
|
||||||
|
dest="logo_path",
|
||||||
|
type=pathlib.Path,
|
||||||
|
default=here / "static" / f"{distname}_logo.png",
|
||||||
|
help=(
|
||||||
|
"File containing the logo image to include in the installer."
|
||||||
|
" (default: %(default)s)"
|
||||||
|
),
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-o",
|
"-o",
|
||||||
"--output_dir",
|
"--output_dir",
|
||||||
|
@ -477,6 +537,7 @@ if __name__ == "__main__":
|
||||||
license_file=args.license_file,
|
license_file=args.license_file,
|
||||||
output_dir=args.output_dir,
|
output_dir=args.output_dir,
|
||||||
conda_exe=args.conda_exe,
|
conda_exe=args.conda_exe,
|
||||||
|
logo_path=args.logo_path,
|
||||||
dirty=args.dirty,
|
dirty=args.dirty,
|
||||||
keep_workdir=args.keep_workdir,
|
keep_workdir=args.keep_workdir,
|
||||||
)
|
)
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 550 KiB |
Loading…
Reference in New Issue