cabinpkg-driver 0.15.0

Compiler-dialect drivers that lower Cabin's build IR to concrete commands
Documentation
//! Semantic build-action IR.
//!
//! The planner emits these toolchain-independent action specs; the
//! [`crate::lower()`] layer turns them into concrete command argv for a
//! specific [`crate::Dialect`]. Nothing here names a compiler flag:
//! the IR records *intent* (optimization level, debug info, defines,
//! include directories, the source language) and each dialect spells
//! it (`-O2` vs `/O2`, `-c` vs `/c`, …). A new dialect is added in
//! [`crate::lower()`] without this IR changing.

use camino::Utf8PathBuf;

use cabin_core::{OptLevel, SourceLanguage};

/// A single semantic build step: compile a translation unit, archive
/// objects into a static library, or link an executable.
///
/// Backend- and toolchain-independent: the concrete command argv is
/// produced later by [`crate::lower()`], not stored here.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BuildAction {
    /// Compile (or syntax-check) one C/C++ translation unit.
    Compile(CompileAction),
    /// Archive object files into a static library.
    Archive(ArchiveAction),
    /// Link object files and static archives into an executable.
    Link(LinkAction),
}

/// What a compile action should produce.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CompileMode {
    /// Normal compile: emit the object file at [`CompileAction::object`].
    Object,
    /// Syntax/semantic check only (`cabin check`): emit no object;
    /// `stamp` is `touch`ed to record a successful check. The
    /// `object` path is retained on the action so the stamp lives
    /// beside it and the workspace-scope filter in `cabin check` can
    /// match on it.
    SyntaxOnly {
        /// Stamp file written on a successful check, in place of the
        /// object.
        stamp: Utf8PathBuf,
    },
}

/// Compile one translation unit.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompileAction {
    /// Source language; selects the standard flag and, at lowering
    /// time, the rule / lowered action kind.
    pub language: SourceLanguage,
    /// Absolute path of the translation unit to compile.
    pub source: Utf8PathBuf,
    /// Object file the normal build produces. Retained even in
    /// [`CompileMode::SyntaxOnly`] so the stamp lives beside it and
    /// the workspace-scope filter in `cabin check` can match on it.
    pub object: Utf8PathBuf,
    /// Object vs. syntax-only.
    pub mode: CompileMode,
    /// Inputs the compile depends on but that are not command
    /// arguments (e.g. a generated source produced upstream). The
    /// `source` is the sole compiled input and is not repeated here.
    pub implicit_inputs: Vec<Utf8PathBuf>,
    /// Header-dependency tracking file. `Some` records that the
    /// dialect should wire dependency discovery for this compile —
    /// the GNU/Clang lowering emits a `-MMD -MF <depfile>` Makefile
    /// depfile here, while the MSVC lowering ignores the path and
    /// relies on `/showIncludes`.
    pub depfile: Option<Utf8PathBuf>,
    /// Compiler driver executable.
    pub compiler: Utf8PathBuf,
    /// Optional compiler-cache wrapper (e.g. `ccache`) prepended to
    /// the *run* command by lowering. Never affects
    /// `compile_commands.json`, which records the underlying compiler
    /// so IDE tooling sees the real driver.
    pub compiler_wrapper: Option<Utf8PathBuf>,
    /// Semantic compile arguments (optimization, defines, includes).
    pub arguments: CompileArguments,
    /// Human-readable description for build output (`CXX foo.o`,
    /// `CHECK foo.o`).
    pub description: String,
}

/// Semantic arguments for a compile, with no flag spelled out.
///
/// The language standard is *not* stored: it is fixed per source
/// language (C → C11, C++ → C++17) and spelled by the dialect from
/// [`CompileAction::language`]. The optimization / debug / assertion
/// intent comes from the resolved profile.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompileArguments {
    /// Optimization level the active profile selected (`-O0` / `/Od`,
    /// …).
    pub opt_level: OptLevel,
    /// Emit debug information (`-g` / `/Zi`).
    pub debug_info: bool,
    /// Define `NDEBUG` (assertions disabled in the active profile).
    pub define_ndebug: bool,
    /// Include search directories. Spelled `-I <dir>` / `/I <dir>`.
    pub include_dirs: Vec<Utf8PathBuf>,
    /// Preprocessor defines, without any prefix. Spelled `-D<define>`
    /// / `/D<define>`.
    pub defines: Vec<String>,
    /// Escape-hatch compile flags appended verbatim after the
    /// include block. The user writes these in the active dialect
    /// (language-neutral first, then language-specific).
    pub extra_flags: Vec<String>,
}

/// Archive object files into a static library.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ArchiveAction {
    /// Archiver executable (`ar` / `lib`).
    pub archiver: Utf8PathBuf,
    /// Static library to produce.
    pub output: Utf8PathBuf,
    /// Object files to archive, in order.
    pub inputs: Vec<Utf8PathBuf>,
    /// Human-readable description (`AR libfoo.a`).
    pub description: String,
}

/// Link objects and static archives into an executable.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LinkAction {
    /// Link-driver executable (the C or C++ compiler, chosen per
    /// target by the planner).
    pub linker: Utf8PathBuf,
    /// Executable to produce.
    pub output: Utf8PathBuf,
    /// Link inputs (objects then static archives), in link order.
    pub inputs: Vec<Utf8PathBuf>,
    /// Inputs the link depends on but that are not command arguments.
    pub implicit_inputs: Vec<Utf8PathBuf>,
    /// Extra linker flags (`ldflags`), inserted after the inputs and
    /// before the output spelling.
    pub arguments: Vec<String>,
    /// System libraries to link, as bare names (e.g. `pthread`, `m`).
    /// Lowered per-dialect — `-l<name>` for GNU-like, `<name>.lib` for
    /// MSVC — and placed after the archive inputs so a static library's
    /// required system libraries resolve left-to-right on the GNU link
    /// line. Kept separate from `arguments` (raw `ldflags`) precisely so
    /// the dialect layer owns the spelling rather than the planner.
    pub link_libs: Vec<String>,
    /// Human-readable description (`LINK app`).
    pub description: String,
}