OpenLogi Cross-Platform cfg-Gating Strategy: Rust Conditional Compilation for Multi-OS Support

OpenLogi uses Rust's #[cfg] attributes and Cargo's target-specific dependencies to compile platform-specific code only on relevant operating systems, enabling a single codebase to support macOS, Linux, and Windows without runtime overhead.

OpenLogi is a cross-platform automation framework that runs natively on macOS, Linux, and Windows. Managing disparate platform APIs while maintaining a unified public interface requires a disciplined approach to conditional compilation. This article examines the OpenLogi cross-platform cfg-gating strategy as implemented in the AprilNEA/OpenLogi repository, demonstrating how the project leverages Rust's compile-time configuration system to isolate OS-specific implementations.

The Four Layers of Conditional Compilation

The OpenLogi codebase organizes platform-specific logic into four distinct layers, each serving a specific architectural purpose. This hierarchy ensures that platform dependencies are both explicit and minimal.

Crate-Level Gating

At the highest level, the project uses target-specific dependencies in Cargo.toml to ensure platform-only libraries are never compiled on the wrong OS. This prevents linking errors and keeps the build graph minimal.

According to the OpenLogi source code, the configuration uses target.'cfg(...)' tables:

[target.'cfg(target_os = "macos")'.dependencies]
async-hid = "0.5"
macos-api = { git = "https://github.com/Apple/macOS-api" }

[target.'cfg(target_os = "linux")'.dependencies]
evdev = "0.12"
linux-permissions = "1.0"

[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["winuser"] }

Module-Level Gating

Large OS-specific implementations are split into separate modules within the same crate. The entry point uses #[cfg(target_os = "...")] to conditionally compile only the relevant module.

In crates/openlogi-hook/src/lib.rs, the input-hook subsystem declares three top-level modules:

#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "windows")]
mod windows;

The public API (Hook::start()) is defined once in the parent module, while the internals differ per OS. This pattern keeps the public API identical across platforms while isolating implementation details.

Item-Level Gating

Individual structs, functions, or impl blocks that call platform APIs are guarded at the item level. This fine-grained control prevents accidental use of OS-specific functions on incompatible targets.

In crates/openlogi-inject/src/inject.rs, input synthesis functions are gated as follows:

#[cfg(target_os = "windows")]
pub fn send_key_windows(vk: u16) { /* Windows API */ }

#[cfg(target_os = "macos")]
pub fn send_key_macos(keycode: u32) { /* CGEvent API */ }

Runtime Abstractions

The strategy exposes platform-agnostic traits (e.g., HidBackend) that each OS module implements. The rest of the codebase works against the trait without caring about the concrete OS, enabling compile-time polymorphism.

In crates/openlogi-hid/src/lib.rs, the trait abstraction appears as:

pub trait HidBackend {
    fn open(&self, vid: u16, pid: u16) -> Result<Box<dyn Device>, HidError>;
}

// macOS implementation (only compiled on macOS)
#[cfg(target_os = "macos")]
mod macos_impl {
    use super::HidBackend;
    pub struct MacBackend;
    impl HidBackend for MacBackend {
        fn open(&self, vid: u16, pid: u16) -> Result<Box<dyn Device>, HidError> {
            /* macOS-specific code */
        }
    }
}

Implementation Patterns in the Source Tree

OpenLogi applies these layers consistently across its crate structure. Specific files demonstrate distinct aspects of the cfg-gating strategy.

Transport Layer Selection

crates/openlogi-hid/src/transport.rs serves as the central hub that selects the correct OS transport implementation. The file uses #[cfg(target_os = "...")] to re-export a single Transport type that aliases to the platform-specific implementation:

// crates/openlogi-hid/src/transport.rs
#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "windows")]
mod windows;

// Public type used by the rest of the code base
pub use self::{macos::Transport as TransportMac,
               linux::Transport as TransportLinux,
               windows::Transport as TransportWindows};

Input Hook Entry Points

The crates/openlogi-hook/src/lib.rs file demonstrates crate-level gating for the input-hook subsystem. It contains three top-level modules (macos, linux, windows), each guarded by #[cfg(target_os = "...")]. The public API remains consistent while the internal event loop implementations vary by platform.

Desktop Platform Wrappers

crates/openlogi-desktop/src/platform/os.rs provides a thin wrapper that forwards to the appropriate macOS or Linux implementation. This file illustrates how higher-level crates delegate to platform-specific code without exposing implementation details to consumers.

System Tray Integration

The system-tray integration in crates/openlogi-agent/src/tray.rs uses cfg-gating to only compile tray code on supported platforms:

#[cfg(target_os = "macos")]
mod macos_tray;

#[cfg(target_os = "windows")]
mod windows_tray;

Linux builds exclude this functionality entirely at compile time, reducing binary size and eliminating unnecessary dependencies.

Benefits of the cfg-Gating Strategy

This compile-time approach delivers specific advantages for cross-platform Rust development:

  • Compile-Time Safety: Any accidental use of a platform-specific function on the wrong target immediately fails to compile. In crates/openlogi-permissions/src/lib.rs, permission handling logic uses #[cfg] to ensure macOS-specific entitlement checks never appear in Linux builds.
  • Single Source of Truth: The public API—traits, enums, and CLI commands—lives in shared modules with no #[cfg] attributes. All OS-specific code implements the same traits, keeping the rest of the codebase platform-oblivious.
  • Modular Testing: Each OS module can be unit-tested behind a #[cfg(test)] harness that forces the appropriate target. This facilitates CI jobs that run separately on Linux, macOS, and Windows, ensuring platform-specific logic is verified on its native target.

Summary

OpenLogi's cross-platform strategy relies on Rust's conditional compilation to maintain a clean separation between platform-specific implementations and shared business logic. Key takeaways include:

  • Crate-level gating through Cargo's target.'cfg(...)' dependency tables ensures platform libraries are only fetched and linked for their respective targets.
  • Module and item-level gating using #[cfg(target_os = "...")] isolates OS-specific code while exposing a unified public API through trait abstractions.
  • Concrete implementations in files like crates/openlogi-hid/src/transport.rs and crates/openlogi-hook/src/lib.rs demonstrate how to structure cross-platform crates for maintainability.
  • Compile-time elimination of unsupported platform code prevents runtime crashes and reduces binary size compared to runtime dispatch approaches.

Frequently Asked Questions

How does OpenLogi prevent compiling Windows-specific code on Linux?

OpenLogi uses Rust's #[cfg(target_os = "windows")] attribute to gate Windows-specific functions, modules, and dependencies. The Rust compiler evaluates these conditions at compile time; if the target OS is not Windows, the compiler ignores the gated code entirely, preventing linking errors and ensuring Linux builds contain no Windows API calls.

What is the difference between module-level and item-level cfg-gating?

Module-level gating applies the #[cfg] attribute to an entire module declaration (e.g., #[cfg(target_os = "linux")] mod linux;), which excludes all contents of that module from compilation on other platforms. Item-level gating applies to individual structs, functions, or implementations (e.g., #[cfg(target_os = "macos")] pub fn open_device(...)), allowing fine-grained control when multiple platforms share a module file but implement specific items differently.

Why use trait abstractions instead of conditional compilation everywhere?

Trait abstractions in OpenLogi (such as HidBackend) provide a single public interface that the rest of the codebase can import without knowing which platform is running. While #[cfg] selects the correct implementation at compile time, the trait ensures type safety and allows the majority of the code to remain platform-agnostic, reducing the surface area where conditional compilation is required.

How does Cargo support platform-specific dependencies?

Cargo supports platform-specific dependencies through target.'cfg(...)' tables in Cargo.toml. OpenLogi declares dependencies like async-hid under [target.'cfg(target_os = "macos")'.dependencies], ensuring the library is only downloaded and compiled when building for macOS, keeping build times and artifact sizes minimal for Linux and Windows targets.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →