Phase 1: Platform Portability¶
Parent plan: PLAN-packaging.md
Goal¶
Make the ryll codebase compile on Linux, macOS, and Windows without changing runtime behaviour on Linux (the primary platform). This phase produces no packages — it lays the groundwork for the CI and packaging phases that follow.
Current state¶
- The code compiles and runs only on Linux x86_64.
libc::signal(SIGINT, ...)insrc/main.rsis Unix-only and will fail to compile on Windows.Cargo.lockis in.gitignore— not committed.- The
--capturefeature usesopenh264,mp4,pcap-file, andetherparsecrates. Per the master plan, capture should be disabled on Windows via a Cargo feature. - No
#[cfg]guards exist anywhere in the codebase.
Changes¶
Step 1: Commit Cargo.lock¶
Generate Cargo.lock and remove it from .gitignore. This
ensures reproducible builds across CI runners and developer
machines.
Files changed:
- .gitignore — remove the Cargo.lock line
- Cargo.lock — generated by cargo generate-lockfile
(run inside the devcontainer to match the existing build
environment)
Commit: standalone, before any code changes.
Step 2: Replace libc signal handler with ctrlc crate¶
Replace the raw libc::signal(SIGINT, ...) call with the
ctrlc crate, which provides portable Ctrl+C handling on
both Unix and Windows.
Current code (src/main.rs:24-39):
pub static SHUTDOWN_REQUESTED: AtomicBool = ...;
extern "C" fn handle_sigint(_: libc::c_int) {
SHUTDOWN_REQUESTED.store(true, Ordering::SeqCst);
}
fn main() -> Result<()> {
unsafe {
libc::signal(
libc::SIGINT,
handle_sigint as *const () as libc::sighandler_t,
);
}
...
}
New code:
pub static SHUTDOWN_REQUESTED: AtomicBool = ...;
fn main() -> Result<()> {
ctrlc::set_handler(|| {
SHUTDOWN_REQUESTED.store(true, Ordering::SeqCst);
})
.expect("failed to set Ctrl+C handler");
...
}
Cargo.toml changes:
- Add: ctrlc = "3"
- Remove: libc = "0.2" (nothing else uses it)
Files changed:
- Cargo.toml — swap libc for ctrlc
- src/main.rs — replace signal handler, remove
handle_sigint function, remove unsafe block
Commit: standalone.
Step 3: Feature-gate capture for Windows¶
Add a Cargo feature capture that is enabled by default.
The --capture CLI flag and all capture code are compiled
only when this feature is active. On Windows, CI will build
with --no-default-features to exclude capture.
Cargo.toml changes:
[features]
default = ["capture"]
capture = [
"dep:pcap-file",
"dep:etherparse",
"dep:openh264",
"dep:mp4",
]
[dependencies]
# Capture mode (--capture) — optional, not available on
# Windows
pcap-file = { version = "2", optional = true }
etherparse = { version = "0.16", optional = true }
openh264 = { version = "0.6", optional = true }
mp4 = { version = "0.14", optional = true }
Source code changes:
All references to the capture module and CaptureSession
need #[cfg(feature = "capture")] guards. The files that
need changes are:
-
src/main.rs— gatemod capture, theuse crate::capture::CaptureSessionimport, the--captureargument handling, and thecaptureparameter onrun_headless/run_gui. When the feature is off,captureis alwaysNone. -
src/capture.rs— gate the entire file with#. -
src/config.rs— gate thecapturefield onArgswith#[cfg(feature = "capture")]. -
src/app.rs— gate allCaptureSessionimports and uses. Thecaptureparameter onRyllApp::new,run_headless, andrun_connectionbecomesOption<Arc<CaptureSession>>when the feature is on, and is elided (alwaysNone) when off. The cleanest approach is to always acceptOption<Arc<CaptureSession>>but use a type alias:
#[cfg(feature = "capture")]
use crate::capture::CaptureSession;
#[cfg(feature = "capture")]
type CaptureRef = Option<Arc<CaptureSession>>;
#[cfg(not(feature = "capture"))]
type CaptureRef = ();
However, this adds complexity to every call site. A
simpler approach: keep the Option<Arc<...>> parameter
everywhere, but when the feature is off, define a
minimal stub:
This lets all function signatures remain unchanged —
they just always receive None. The only gated code is
the mod capture import and the --capture CLI flag.
Recommended: use the stub approach for minimal diff and no signature changes across the codebase.
src/channels/display.rs,cursor.rs,main_channel.rs,inputs.rs— these all acceptOption<Arc<CaptureSession>>and call methods on it conditionally. With the stub approach, these files need no changes because they always checkif let Some(ref c) = self.captureand the value will simply always beNoneon Windows.
Commit: standalone.
Step 4: Verify cross-compilation¶
Attempt to compile for all three target triples. This can
be done locally with rustup target add or we can defer
full verification to Phase 2 (CI). At minimum, verify:
Note: cargo check for foreign targets may fail on
linking but will catch Rust compilation errors. Full
build verification will happen in CI (Phase 2) on native
runners.
If cross-checks surface additional issues (unlikely given the dependency set, but possible), fix them in this step.
Commit: only if fixes are needed.
Step summary¶
| Step | Description | Files | Commit |
|---|---|---|---|
| 1 | Commit Cargo.lock | .gitignore, Cargo.lock |
Yes |
| 2 | Replace libc with ctrlc | Cargo.toml, src/main.rs |
Yes |
| 3 | Feature-gate capture | Cargo.toml, src/main.rs, src/config.rs, src/capture.rs |
Yes |
| 4 | Verify cross-compilation | Fix any issues found | Only if needed |
Risks and mitigations¶
-
ctrlc crate behaviour difference: On Unix,
ctrlcusessignal(SIGINT)internally, so behaviour is identical to today. On Windows it usesSetConsoleCtrlHandler. TheAtomicBoolapproach remains the same on both platforms. -
Feature-gated capture breaks something: The stub approach means all function signatures stay the same and
Noneflows through everywhere. Risk is low. We verify withcargo build --no-default-featureson Linux before pushing. -
openh264 C compilation on macOS: The
openh264crate bundles C source and builds viacc. On macOS with Xcode CLT, this should work. We'll confirm in Phase 2 CI. No mitigation needed in Phase 1.
Documentation updates¶
After completing all steps:
- Update docs/portability.md to reflect cross-platform
support and the capture feature gate.
- Update AGENTS.md to mention the capture Cargo feature
and the ctrlc dependency replacement.
- Update README.md build instructions if needed.