Skip to main content

cabin_driver/
action.rs

1//! Semantic build-action IR.
2//!
3//! The planner emits these toolchain-independent action specs; the
4//! [`crate::lower()`] layer turns them into concrete command argv for a
5//! specific [`crate::Dialect`].  Nothing here names a compiler flag:
6//! the IR records *intent* (optimization level, debug info, defines,
7//! include directories, the source language) and each dialect spells
8//! it (`-O2` vs `/O2`, `-c` vs `/c`, …).  A new dialect is added in
9//! [`crate::lower()`] without this IR changing.
10
11use camino::Utf8PathBuf;
12
13use cabin_core::{LanguageStandard, OptLevel};
14
15/// A single semantic build step: compile a translation unit, archive
16/// objects into a static library, or link an executable.
17///
18/// Backend- and toolchain-independent: the concrete command argv is
19/// produced later by [`crate::lower()`], not stored here.
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum BuildAction {
22    /// Compile (or syntax-check) one C/C++ translation unit.
23    Compile(CompileAction),
24    /// Archive object files into a static library.
25    Archive(ArchiveAction),
26    /// Link object files and static archives into an executable.
27    Link(LinkAction),
28}
29
30/// What a compile action should produce.
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub enum CompileMode {
33    /// Normal compile: emit the object file at [`CompileAction::object`].
34    Object,
35    /// Syntax/semantic check only (`cabin check`): emit no object;
36    /// `stamp` is `touch`ed to record a successful check.  The
37    /// `object` path is retained on the action so the stamp lives
38    /// beside it and the workspace-scope filter in `cabin check` can
39    /// match on it.
40    SyntaxOnly {
41        /// Stamp file written on a successful check, in place of the
42        /// object.
43        stamp: Utf8PathBuf,
44    },
45}
46
47/// Compile one translation unit.
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct CompileAction {
50    /// Effective language standard for this translation unit.  Also
51    /// determines the source language (`standard.language()`), which
52    /// selects the rule / lowered action kind, `/EHsc`, and the
53    /// `/Tp` / `/Tc` source-language flag on MSVC.
54    pub standard: LanguageStandard,
55    /// Whether this translation unit compiles with GNU language
56    /// extensions.  Strictly per-target: two compiles in one build
57    /// may differ in both level and this flag.  The GNU/Clang
58    /// lowering spells the standard `-std=gnu…` instead of
59    /// `-std=c…`; the planner rejects the flag on the MSVC dialect
60    /// before lowering (`cl.exe` has no GNU dialect mode).
61    pub gnu_extensions: bool,
62    /// Absolute path of the translation unit to compile.
63    pub source: Utf8PathBuf,
64    /// Object file the normal build produces.  Retained even in
65    /// [`CompileMode::SyntaxOnly`] so the stamp lives beside it and
66    /// the workspace-scope filter in `cabin check` can match on it.
67    pub object: Utf8PathBuf,
68    /// Object vs. syntax-only.
69    pub mode: CompileMode,
70    /// Inputs the compile depends on but that are not command
71    /// arguments (e.g. a generated source produced upstream).  The
72    /// `source` is the sole compiled input and is not repeated here.
73    pub implicit_inputs: Vec<Utf8PathBuf>,
74    /// Header-dependency tracking file.  `Some` records that the
75    /// dialect should wire dependency discovery for this compile -
76    /// the GNU/Clang lowering emits a `-MD -MF <depfile>` Makefile
77    /// depfile here (`-MD`, not `-MMD`, so headers found through a
78    /// system include dir still land in the depfile and keep
79    /// invalidating rebuilds), while the MSVC lowering ignores the
80    /// path and relies on `/showIncludes`.
81    pub depfile: Option<Utf8PathBuf>,
82    /// Compiler driver executable.
83    pub compiler: Utf8PathBuf,
84    /// Optional compiler wrapper (e.g. `ccache`) prepended to
85    /// the *run* command by lowering.  Never affects
86    /// `compile_commands.json`, which records the underlying compiler
87    /// so IDE tooling sees the real driver.
88    pub compiler_wrapper: Option<Utf8PathBuf>,
89    /// Semantic compile arguments (optimization, defines, includes).
90    pub arguments: CompileArguments,
91    /// Human-readable description for build output (`CXX foo.o`,
92    /// `CHECK foo.o`).
93    pub description: String,
94}
95
96/// Semantic arguments for a compile, with no flag spelled out.
97///
98/// The language standard lives on [`CompileAction::standard`]; each
99/// dialect spells it (`-std=c++20` vs `/std:c++20`).  The
100/// optimization / debug / assertion intent comes from the resolved
101/// profile.
102#[derive(Debug, Clone, PartialEq, Eq)]
103pub struct CompileArguments {
104    /// Optimization level the active profile selected (`-O0` / `/Od`,
105    /// …).
106    pub opt_level: OptLevel,
107    /// Emit debug information (`-g` / `/Zi`).
108    pub debug_info: bool,
109    /// Define `NDEBUG` (assertions disabled in the active profile).
110    pub define_ndebug: bool,
111    /// Include search directories.  Spelled `-I <dir>` / `/I <dir>`.
112    pub include_dirs: Vec<Utf8PathBuf>,
113    /// Include search directories marked as *system* search paths,
114    /// so diagnostics inside their headers are suppressed.  Searched
115    /// after [`Self::include_dirs`].  The planner routes third-party
116    /// contributions here (registry packages, foundation ports,
117    /// pkg-config system dependencies).  Spelled `-isystem <dir>` for
118    /// GNU/Clang; `/external:W0 /external:I <dir>` for MSVC (the
119    /// planner only populates this on MSVC-dialect builds when the
120    /// detected compiler supports the `/external:` block).
121    pub system_include_dirs: Vec<Utf8PathBuf>,
122    /// Preprocessor defines, without any prefix.  Spelled `-D<define>`
123    /// / `/D<define>`.
124    pub defines: Vec<String>,
125    /// Escape-hatch compile flags appended verbatim after the
126    /// include block.  The user writes these in the active dialect
127    /// (language-neutral first, then language-specific).
128    pub extra_flags: Vec<String>,
129}
130
131/// Archive object files into a static library.
132#[derive(Debug, Clone, PartialEq, Eq)]
133pub struct ArchiveAction {
134    /// Archiver executable (`ar` / `lib`).
135    pub archiver: Utf8PathBuf,
136    /// Static library to produce.
137    pub output: Utf8PathBuf,
138    /// Object files to archive, in order.
139    pub inputs: Vec<Utf8PathBuf>,
140    /// Human-readable description (`AR libfoo.a`).
141    pub description: String,
142}
143
144/// Link objects and static archives into an executable.
145#[derive(Debug, Clone, PartialEq, Eq)]
146pub struct LinkAction {
147    /// Link-driver executable (the C or C++ compiler, chosen per
148    /// target by the planner).
149    pub linker: Utf8PathBuf,
150    /// Executable to produce.
151    pub output: Utf8PathBuf,
152    /// Link inputs (objects then static archives), in link order.
153    pub inputs: Vec<Utf8PathBuf>,
154    /// Inputs the link depends on but that are not command arguments.
155    pub implicit_inputs: Vec<Utf8PathBuf>,
156    /// Extra linker flags (`ldflags`), inserted after the inputs and
157    /// before the output spelling.
158    pub arguments: Vec<String>,
159    /// System libraries to link, as bare names (e.g. `pthread`, `m`).
160    /// Lowered per-dialect - `-l<name>` for GNU-like, `<name>.lib` for
161    /// MSVC - and placed after the archive inputs so a static library's
162    /// required system libraries resolve left-to-right on the GNU link
163    /// line.  Kept separate from `arguments` (raw `ldflags`) precisely so
164    /// the dialect layer owns the spelling rather than the planner.
165    pub link_libs: Vec<String>,
166    /// Human-readable description (`LINK app`).
167    pub description: String,
168}