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}