mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
feat: add Perl to development environment and enhance toolchain accessibility
This commit is contained in:
6 files changed
+59
-2
No files matched your search
@@ -23,6 +23,7 @@ RUN apt-get update -qq \
|
|||||||
mold \
|
mold \
|
||||||
numactl \
|
numactl \
|
||||||
openjdk-17-jre-headless \
|
openjdk-17-jre-headless \
|
||||||
|
perl \
|
||||||
python3 \
|
python3 \
|
||||||
python3-pip \
|
python3-pip \
|
||||||
python3-venv \
|
python3-venv \
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ remote-bitbang debug acceptance flow. Docker, Nix, and manual Ubuntu installs
|
|||||||
provide Java 17 for SBT. Docker and Nix use Python 3.10 to match the
|
provide Java 17 for SBT. Docker and Nix use Python 3.10 to match the
|
||||||
hash-pinned Ubuntu 22.04 requirement sets. The environment also installs the
|
hash-pinned Ubuntu 22.04 requirement sets. The environment also installs the
|
||||||
locked Python build and quality dependencies, clang-format-14, GNU Make,
|
locked Python build and quality dependencies, clang-format-14, GNU Make,
|
||||||
C/C++ build tools, and runtime libraries required by those binaries.
|
C/C++ build tools, Perl for Verilator, and runtime libraries required by those
|
||||||
|
binaries.
|
||||||
|
|
||||||
The environment intentionally does not include PDK repositories, managed RTL,
|
The environment intentionally does not include PDK repositories, managed RTL,
|
||||||
application archives, build output, or compiler caches. These inputs are
|
application archives, build output, or compiler caches. These inputs are
|
||||||
@@ -33,6 +34,9 @@ Python requirements, and writes:
|
|||||||
|
|
||||||
Bootstrap is idempotent. It reinstalls tools or Python packages when the
|
Bootstrap is idempotent. It reinstalls tools or Python packages when the
|
||||||
dependency lock, selected tool set, or Python requirement hashes change.
|
dependency lock, selected tool set, or Python requirement hashes change.
|
||||||
|
Installed public tool archives are made readable and their directories
|
||||||
|
traversable by all users. This is required when Docker builds the image as root
|
||||||
|
and runs regressions with the caller's user ID.
|
||||||
|
|
||||||
~~~sh
|
~~~sh
|
||||||
python3 scripts/development_environment.py bootstrap
|
python3 scripts/development_environment.py bootstrap
|
||||||
|
|||||||
@@ -65,6 +65,7 @@
|
|||||||
ncurses5
|
ncurses5
|
||||||
numactl
|
numactl
|
||||||
jdk17_headless
|
jdk17_headless
|
||||||
|
perl
|
||||||
python310Full
|
python310Full
|
||||||
unzip
|
unzip
|
||||||
which
|
which
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ SYSTEM_EXECUTABLES = (
|
|||||||
"make",
|
"make",
|
||||||
"mold",
|
"mold",
|
||||||
"numactl",
|
"numactl",
|
||||||
|
"perl",
|
||||||
)
|
)
|
||||||
STAMP_NAME = "development-environment.json"
|
STAMP_NAME = "development-environment.json"
|
||||||
ACTIVATE_NAME = "activate.sh"
|
ACTIVATE_NAME = "activate.sh"
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ import argparse
|
|||||||
import os
|
import os
|
||||||
import posixpath
|
import posixpath
|
||||||
import shutil
|
import shutil
|
||||||
|
import stat
|
||||||
import sys
|
import sys
|
||||||
import tarfile
|
import tarfile
|
||||||
import tempfile
|
import tempfile
|
||||||
|
from itertools import chain
|
||||||
from pathlib import Path, PurePosixPath
|
from pathlib import Path, PurePosixPath
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
@@ -38,6 +40,22 @@ def safe_extract(archive: Path, destination: Path) -> None:
|
|||||||
bundle.extractall(destination)
|
bundle.extractall(destination)
|
||||||
|
|
||||||
|
|
||||||
|
def make_tree_world_readable(root: Path) -> None:
|
||||||
|
"""Make a public tool archive usable by accounts other than its installer."""
|
||||||
|
|
||||||
|
for path in chain((root,), root.rglob("*")):
|
||||||
|
if path.is_symlink():
|
||||||
|
continue
|
||||||
|
mode = stat.S_IMODE(path.stat().st_mode)
|
||||||
|
if path.is_dir():
|
||||||
|
path.chmod(mode | 0o555)
|
||||||
|
elif path.is_file():
|
||||||
|
shared_mode = mode | 0o444
|
||||||
|
if mode & 0o111:
|
||||||
|
shared_mode |= 0o111
|
||||||
|
path.chmod(shared_mode)
|
||||||
|
|
||||||
|
|
||||||
def install(name: str, spec: dict[str, Any], cache: Path, update: bool) -> Path:
|
def install(name: str, spec: dict[str, Any], cache: Path, update: bool) -> Path:
|
||||||
downloads = cache / "downloads"
|
downloads = cache / "downloads"
|
||||||
archive = downloads / spec["archive"]
|
archive = downloads / spec["archive"]
|
||||||
@@ -53,6 +71,7 @@ def install(name: str, spec: dict[str, Any], cache: Path, update: bool) -> Path:
|
|||||||
destination = cache / "toolchains" / f"{name}-{spec['version']}"
|
destination = cache / "toolchains" / f"{name}-{spec['version']}"
|
||||||
marker = destination / ".complete"
|
marker = destination / ".complete"
|
||||||
if marker.is_file() and marker.read_text(encoding="utf-8").strip() == spec["sha256"]:
|
if marker.is_file() and marker.read_text(encoding="utf-8").strip() == spec["sha256"]:
|
||||||
|
make_tree_world_readable(destination)
|
||||||
return destination / spec["path"]
|
return destination / spec["path"]
|
||||||
|
|
||||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||||
@@ -61,6 +80,7 @@ def install(name: str, spec: dict[str, Any], cache: Path, update: bool) -> Path:
|
|||||||
extracted.mkdir()
|
extracted.mkdir()
|
||||||
safe_extract(archive, extracted)
|
safe_extract(archive, extracted)
|
||||||
atomic_write(extracted / ".complete", spec["sha256"] + "\n")
|
atomic_write(extracted / ".complete", spec["sha256"] + "\n")
|
||||||
|
make_tree_world_readable(extracted)
|
||||||
if destination.exists():
|
if destination.exists():
|
||||||
shutil.rmtree(destination)
|
shutil.rmtree(destination)
|
||||||
os.replace(extracted, destination)
|
os.replace(extracted, destination)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ from scripts.development_environment import ( # noqa: E402
|
|||||||
write_activation,
|
write_activation,
|
||||||
)
|
)
|
||||||
from scripts.generate_mpw import render_active_manifest, validate_extension_bindings # noqa: E402
|
from scripts.generate_mpw import render_active_manifest, validate_extension_bindings # noqa: E402
|
||||||
from scripts.install_toolchain import safe_extract # noqa: E402
|
from scripts.install_toolchain import make_tree_world_readable, safe_extract # noqa: E402
|
||||||
from scripts.package import make_sbom # noqa: E402
|
from scripts.package import make_sbom # noqa: E402
|
||||||
from scripts.prepare_mpw import patch_serv # noqa: E402
|
from scripts.prepare_mpw import patch_serv # noqa: E402
|
||||||
from scripts import regress # noqa: E402
|
from scripts import regress # noqa: E402
|
||||||
@@ -1193,10 +1193,12 @@ def test_container_and_nix_environment_files_use_locked_inputs() -> None:
|
|||||||
|
|
||||||
assert f"ubuntu@{lock['container_images']['ubuntu_22_04']['digest']}" in dockerfile
|
assert f"ubuntu@{lock['container_images']['ubuntu_22_04']['digest']}" in dockerfile
|
||||||
assert "openjdk-17-jre-headless" in dockerfile
|
assert "openjdk-17-jre-headless" in dockerfile
|
||||||
|
assert " perl \\\n" in dockerfile
|
||||||
assert "scripts/development_environment.py" in dockerfile
|
assert "scripts/development_environment.py" in dockerfile
|
||||||
assert "scripts/development_environment.py" in flake
|
assert "scripts/development_environment.py" in flake
|
||||||
assert "buildFHSEnv" in flake
|
assert "buildFHSEnv" in flake
|
||||||
assert "jdk17_headless" in flake
|
assert "jdk17_headless" in flake
|
||||||
|
assert " perl\n" in flake
|
||||||
assert "python310Full" in flake
|
assert "python310Full" in flake
|
||||||
assert "python3Full" not in flake
|
assert "python3Full" not in flake
|
||||||
assert "retrosoc-development retrosoc-dev" in flake
|
assert "retrosoc-development retrosoc-dev" in flake
|
||||||
@@ -2317,6 +2319,34 @@ def test_safe_extract_rejects_parent_traversal(tmp_path: Path) -> None:
|
|||||||
assert not (tmp_path / "outside").exists()
|
assert not (tmp_path / "outside").exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_toolchain_tree_is_accessible_to_runtime_users(tmp_path: Path) -> None:
|
||||||
|
toolchain = tmp_path / "toolchain"
|
||||||
|
binary_directory = toolchain / "bundle/bin"
|
||||||
|
binary_directory.mkdir(parents=True)
|
||||||
|
executable = binary_directory / "tool"
|
||||||
|
executable.write_text("#!/bin/sh\n", encoding="utf-8")
|
||||||
|
data = toolchain / "bundle/data"
|
||||||
|
data.write_text("public tool data\n", encoding="utf-8")
|
||||||
|
marker = toolchain / ".complete"
|
||||||
|
marker.write_text("digest\n", encoding="utf-8")
|
||||||
|
|
||||||
|
toolchain.chmod(0o700)
|
||||||
|
(toolchain / "bundle").chmod(0o700)
|
||||||
|
binary_directory.chmod(0o700)
|
||||||
|
executable.chmod(0o700)
|
||||||
|
data.chmod(0o600)
|
||||||
|
marker.chmod(0o600)
|
||||||
|
|
||||||
|
make_tree_world_readable(toolchain)
|
||||||
|
|
||||||
|
assert toolchain.stat().st_mode & 0o777 == 0o755
|
||||||
|
assert (toolchain / "bundle").stat().st_mode & 0o777 == 0o755
|
||||||
|
assert binary_directory.stat().st_mode & 0o777 == 0o755
|
||||||
|
assert executable.stat().st_mode & 0o777 == 0o755
|
||||||
|
assert data.stat().st_mode & 0o777 == 0o644
|
||||||
|
assert marker.stat().st_mode & 0o777 == 0o644
|
||||||
|
|
||||||
|
|
||||||
def test_fatfs_update_reextracts_downloaded_archive(tmp_path: Path) -> None:
|
def test_fatfs_update_reextracts_downloaded_archive(tmp_path: Path) -> None:
|
||||||
module_path = ROOT / "app/setup.py"
|
module_path = ROOT / "app/setup.py"
|
||||||
spec = importlib.util.spec_from_file_location("retrosoc_app_setup", module_path)
|
spec = importlib.util.spec_from_file_location("retrosoc_app_setup", module_path)
|
||||||
|
|||||||
Reference in new issue
Block a user