Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,29 @@ eselect repository create crossdev
Each board lives in `boards/<name>/` with:
- `board.conf` -- variables read by Rust and bash scripts
- `genimage.cfg` -- disk image layout
- `sandbox-packages.txt` -- extra host packages for the sandbox (optional)
- `sandbox-packages.use` -- USE flags for those packages (optional)
- `target-packages.txt` -- extra packages cross-emerged into the image (optional)
- `pre-{step}.sh` -- runs before Rust default (optional)
- `post-{step}.sh` -- runs after Rust default (optional)
- `override-{step}.sh` -- replaces Rust default entirely (optional)

Steps: `deps`, `checkout`, `bootloader`, `kernel`, `assemble`, `pack`

Package lists in `defaults/` apply to every sandbox
(`defaults/sandbox-packages.txt`) and every image
(`defaults/target-packages.txt`). The effective target set is
`defaults/target-packages.txt` UNION `boards/<name>/target-packages.txt`
MINUS the board's `-atom` lines (e.g. `-app-misc/fastfetch` drops a
default) -- every part is a plain file. Subtracting an atom not in the
set warns and does nothing; `-atom` lines in the defaults file itself
are an error. Heavy extras (mold, go, cmake, rust, iw, wpa_supplicant)
stay out of the defaults -- boards opt in via their own list.
List lines are `atom [keywords]` -- a keyword override (e.g.
`sys-boot/syslinux **`) lands in `etc/portage/package.accept_keywords/`.
Portage config files under `defaults/portage/` are overlaid onto the
sandbox's `etc/portage/` during prepare.

### Build step execution

```
Expand Down
2 changes: 0 additions & 2 deletions boards/blackhole/target-packages.txt

This file was deleted.

9 changes: 0 additions & 9 deletions boards/k1/target-packages.txt

This file was deleted.

4 changes: 0 additions & 4 deletions boards/k230/target-packages.txt

This file was deleted.

16 changes: 2 additions & 14 deletions boards/k3/target-packages.txt
Original file line number Diff line number Diff line change
@@ -1,26 +1,14 @@
# Packages to cross-install into the target sysroot for the K3 image
# K3 extras on top of defaults/target-packages.txt

# Services
net-misc/openssh
app-admin/metalog
net-misc/ntp

# Initramfs / boot
sys-apps/busybox

# Dev tools
app-editors/vim
dev-vcs/git
# Dev tools (git is in defaults)
dev-debug/strace
dev-debug/gdb
net-misc/curl
net-misc/wget

# System tools
sys-process/htop
sys-process/lsof
sys-apps/file
app-misc/fastfetch
app-misc/screen

# Build systems
Expand Down
6 changes: 0 additions & 6 deletions boards/ky-x1/target-packages.txt

This file was deleted.

2 changes: 0 additions & 2 deletions boards/odroid-m1/target-packages.txt

This file was deleted.

2 changes: 0 additions & 2 deletions boards/odroid-m1s/target-packages.txt

This file was deleted.

2 changes: 0 additions & 2 deletions boards/odroid-m2/target-packages.txt

This file was deleted.

9 changes: 2 additions & 7 deletions boards/pentium-mmx/target-packages.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
# Packages cross-installed into the i586 target sysroot
# pentium-mmx extras on top of defaults/target-packages.txt

# Network & remote access (services enabled in BOOT_SERVICES)
net-misc/openssh
# Network (service enabled in BOOT_SERVICES)
net-misc/dhcpcd

# Disk utilities required by the grow-rootfs first-boot service
sys-block/parted
sys-fs/e2fsprogs

# Minimal shell utilities
sys-apps/busybox
12 changes: 11 additions & 1 deletion crossdev-stages/src/cli/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub async fn run(
ws: &Workspace,
cmd: ImageCmd,
boards_root: &Utf8Path,
defaults_root: &Utf8Path,
mirror: Option<&str>,
dry_run: bool,
) -> Result<()> {
Expand Down Expand Up @@ -72,6 +73,7 @@ pub async fn run(
sandbox.as_deref(),
&board_cfg.arch,
&board_cfg,
defaults_root,
mirror,
None,
)
Expand All @@ -93,7 +95,15 @@ pub async fn run(
} else {
Some(steps.as_slice())
};
image::build(ws, &sb, &tgt, &board_cfg, boards_root, steps_opt)?;
image::build(
ws,
&sb,
&tgt,
&board_cfg,
boards_root,
defaults_root,
steps_opt,
)?;
}
ImageCmd::Prune => {
let builds = ws.list_builds()?;
Expand Down
4 changes: 3 additions & 1 deletion crossdev-stages/src/cli/sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub async fn run(
ws: &Workspace,
cmd: SandboxCmd,
boards_root: &camino::Utf8Path,
defaults_root: &camino::Utf8Path,
mirror: Option<&str>,
) -> Result<()> {
match cmd {
Expand All @@ -33,7 +34,7 @@ pub async fn run(
SandboxCmd::Prepare { name, bare } => {
let dir = ws.resolve_sandbox(name.as_deref())?;
let sb = sandbox::Sandbox::open(dir)?;
sb.prepare(mirror, bare)?;
sb.prepare(mirror, defaults_root, bare)?;
}
SandboxCmd::Crossdev {
arch,
Expand All @@ -51,6 +52,7 @@ pub async fn run(
name.as_deref(),
&arch,
&board_cfg,
defaults_root,
mirror,
gcc_version.as_deref(),
)
Expand Down
8 changes: 7 additions & 1 deletion crossdev-stages/src/cli/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ use crate::cli::util::{default_board_config, ensure_crossdev, ensure_target};
use crate::cli::TargetCmd;
use crate::error::Result;
use crate::{container, stage, target, workspace::Workspace};
use camino::Utf8PathBuf;
use camino::{Utf8Path, Utf8PathBuf};

pub async fn run(
ws: &Workspace,
arch: Option<String>,
sandbox: Option<String>,
target_name: Option<String>,
cmd: TargetCmd,
defaults_root: &Utf8Path,
mirror: Option<&str>,
) -> Result<()> {
match cmd {
Expand Down Expand Up @@ -51,6 +52,7 @@ pub async fn run(
sandbox.as_deref(),
&resolved_arch,
&default_board_config(&resolved_arch),
defaults_root,
mirror,
None,
)
Expand All @@ -62,6 +64,7 @@ pub async fn run(
target_name.as_deref(),
arch.as_deref(),
sandbox.as_deref(),
defaults_root,
mirror,
)
.await?;
Expand All @@ -73,6 +76,7 @@ pub async fn run(
target_name.as_deref(),
arch.as_deref(),
sandbox.as_deref(),
defaults_root,
mirror,
)
.await?;
Expand All @@ -84,6 +88,7 @@ pub async fn run(
target_name.as_deref(),
arch.as_deref(),
sandbox.as_deref(),
defaults_root,
mirror,
)
.await?;
Expand All @@ -96,6 +101,7 @@ pub async fn run(
target_name.as_deref(),
arch.as_deref(),
sandbox.as_deref(),
defaults_root,
mirror,
)
.await?;
Expand Down
7 changes: 6 additions & 1 deletion crossdev-stages/src/cli/util.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use camino::Utf8Path;

use crate::error::Result;
use crate::workspace::Workspace;
use crate::{board, error, sandbox, stage, target};
Expand All @@ -9,6 +11,7 @@ pub async fn ensure_crossdev(
sandbox_name: Option<&str>,
arch: &str,
board_cfg: &board::BoardConfig,
defaults_root: &Utf8Path,
mirror: Option<&str>,
gcc_version: Option<&str>,
) -> Result<sandbox::Sandbox> {
Expand All @@ -27,7 +30,7 @@ pub async fn ensure_crossdev(
}
};
let sb = sandbox::Sandbox::open(sd)?;
sb.prepare(mirror, false)?;
sb.prepare(mirror, defaults_root, false)?;
sb.setup_crossdev(arch, board_cfg, gcc_version)?;
Ok(sb)
}
Expand All @@ -39,6 +42,7 @@ pub async fn ensure_target(
target_name: Option<&str>,
arch_override: Option<&str>,
sandbox_name: Option<&str>,
defaults_root: &Utf8Path,
mirror: Option<&str>,
) -> Result<(target::Target, sandbox::Sandbox)> {
let (tgt, resolved_arch) = match ws.resolve_target(target_name) {
Expand Down Expand Up @@ -67,6 +71,7 @@ pub async fn ensure_target(
sandbox_name,
&resolved_arch,
&default_board_config(&resolved_arch),
defaults_root,
mirror,
None,
)
Expand Down
9 changes: 9 additions & 0 deletions crossdev-stages/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ pub enum Error {
#[error("command failed (exit {code}): {reason}")]
CommandFailed { code: i32, reason: String },

#[error("required package list not found: {0}")]
PackageListNotFound(camino::Utf8PathBuf),

#[error("subtraction line '-{atom}' not allowed in defaults list {file} (only board lists may subtract)")]
SubtractionInDefaults {
file: camino::Utf8PathBuf,
atom: String,
},

#[error("io error: {0}")]
Io(#[from] std::io::Error),
}
Expand Down
94 changes: 30 additions & 64 deletions crossdev-stages/src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,74 +133,39 @@ fn default_deps(
target: &Target,
board: &BoardConfig,
boards_root: &Utf8Path,
defaults_root: &Utf8Path,
) -> Result<()> {
let sandbox_pkgs = boards_root.join(&board.name).join("sandbox-packages.txt");
if sandbox_pkgs.exists() {
// Sandbox extras: defaults are already installed during prepare; only the
// board's own extras (e.g. grub for pentium-mmx) need emerging here.
// merge() with an empty base means a `-atom` line here can only cancel
// the board's own extras, never uninstall a prepare-time default.
let board_dir = boards_root.join(&board.name);
let board_sandbox = crate::package_list::merge(
Vec::new(),
crate::package_list::read_optional(&board_dir.join("sandbox-packages.txt"))?,
);
if !board_sandbox.is_empty() {
let portage_dir = sandbox.dir.join("etc/portage");
crate::package_list::write_accept_keywords(&board_sandbox, &portage_dir)?;
// USE overrides from sandbox-packages.use, e.g. "sys-boot/grub grub_platforms_pc"
crate::package_list::write_package_use(
&board_dir.join("sandbox-packages.use"),
&portage_dir,
)?;
let host_runner = board_runner(sandbox, board);
let portage = Portage::new(&host_runner);
let content = std::fs::read_to_string(&sandbox_pkgs)?;
let lines: Vec<&str> = content
.lines()
.map(str::trim)
.filter(|l| !l.is_empty() && !l.starts_with('#'))
.collect();

// Write package.accept_keywords entries for packages with keyword overrides.
// Format: "atom [keywords]" — e.g. "sys-boot/syslinux **" or "dev-libs/foo ~amd64"
let accept_keywords_dir = sandbox.dir.join("etc/portage/package.accept_keywords");
std::fs::create_dir_all(&accept_keywords_dir)?;
let mut pkgs: Vec<&str> = Vec::new();
for line in &lines {
let parts: Vec<&str> = line.splitn(2, char::is_whitespace).collect();
let atom = parts[0];
pkgs.push(atom);
if parts.len() > 1 {
let keywords = parts[1].trim();
let safe_name = atom.replace('/', "_");
std::fs::write(
accept_keywords_dir.join(&safe_name),
format!("{atom} {keywords}\n"),
)?;
}
}

// Write package.use entries from sandbox-packages.use.
// Format: "atom use_flags..." — e.g. "sys-boot/syslinux bios -uefi"
let sandbox_use = boards_root.join(&board.name).join("sandbox-packages.use");
if sandbox_use.exists() {
let use_dir = sandbox.dir.join("etc/portage/package.use");
std::fs::create_dir_all(&use_dir)?;
let use_content = std::fs::read_to_string(&sandbox_use)?;
for line in use_content.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
let parts: Vec<&str> = line.splitn(2, char::is_whitespace).collect();
let atom = parts[0];
let safe_name = atom.replace('/', "_");
std::fs::write(use_dir.join(&safe_name), format!("{line}\n"))?;
}
}

if !pkgs.is_empty() {
portage.emerge(&pkgs)?;
}
portage.emerge(&crate::package_list::atoms(&board_sandbox))?;
}

let target_pkgs = boards_root.join(&board.name).join("target-packages.txt");
if target_pkgs.exists() {
let content = std::fs::read_to_string(&target_pkgs)?;
let pkgs: Vec<&str> = content
.lines()
.map(str::trim)
.filter(|l| !l.is_empty() && !l.starts_with('#'))
.collect();
if !pkgs.is_empty() {
let target_runner = board_runner(sandbox, board).with_target(&target.dir);
let portage = Portage::new(&target_runner);
portage.cross_emerge(&board.chost(), &pkgs)?;
}
// Target packages: defaults UNION board extras MINUS board `-atom` lines.
let target_pkgs = crate::package_list::merge(
crate::package_list::read_required(&defaults_root.join("target-packages.txt"))?,
crate::package_list::read_optional(&board_dir.join("target-packages.txt"))?,
);
if !target_pkgs.is_empty() {
let target_runner = board_runner(sandbox, board).with_target(&target.dir);
let portage = Portage::new(&target_runner);
portage.cross_emerge(&board.chost(), &crate::package_list::atoms(&target_pkgs))?;
}

Ok(())
Expand Down Expand Up @@ -407,6 +372,7 @@ pub fn build(
target: &Target,
board: &BoardConfig,
boards_root: &Utf8Path,
defaults_root: &Utf8Path,
steps: Option<&[String]>,
) -> Result<()> {
let bld = Build::create(ws, &board.name)?;
Expand Down Expand Up @@ -442,7 +408,7 @@ pub fn build(

let result = match *step {
"deps" => run_step("deps", "deps", &bld, &runner, boards_root, board, |_r| {
default_deps(_r, sandbox, target, board, boards_root)
default_deps(_r, sandbox, target, board, boards_root, defaults_root)
}),
"checkout" => run_step(
"checkout",
Expand Down
Loading
Loading