1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
use std::path::{Path, PathBuf};

use crate::command::Command;
use crate::env::env_var;

/// Construct a new `llvm-readobj` invocation with the `GNU` output style.
/// This assumes that `llvm-readobj` is available at `$LLVM_BIN_DIR/llvm-readobj`.
#[track_caller]
pub fn llvm_readobj() -> LlvmReadobj {
    LlvmReadobj::new()
}

/// Construct a new `llvm-profdata` invocation. This assumes that `llvm-profdata` is available
/// at `$LLVM_BIN_DIR/llvm-profdata`.
#[track_caller]
pub fn llvm_profdata() -> LlvmProfdata {
    LlvmProfdata::new()
}

/// Construct a new `llvm-filecheck` invocation. This assumes that `llvm-filecheck` is available
/// at `$LLVM_FILECHECK`.
#[track_caller]
pub fn llvm_filecheck() -> LlvmFilecheck {
    LlvmFilecheck::new()
}

/// Construct a new `llvm-objdump` invocation. This assumes that `llvm-objdump` is available
/// at `$LLVM_BIN_DIR/llvm-objdump`.
pub fn llvm_objdump() -> LlvmObjdump {
    LlvmObjdump::new()
}

/// Construct a new `llvm-ar` invocation. This assumes that `llvm-ar` is available
/// at `$LLVM_BIN_DIR/llvm-ar`.
pub fn llvm_ar() -> LlvmAr {
    LlvmAr::new()
}

/// Construct a new `llvm-nm` invocation. This assumes that `llvm-nm` is available
/// at `$LLVM_BIN_DIR/llvm-nm`.
pub fn llvm_nm() -> LlvmNm {
    LlvmNm::new()
}

/// Construct a new `llvm-bcanalyzer` invocation. This assumes that `llvm-bcanalyzer` is available
/// at `$LLVM_BIN_DIR/llvm-bcanalyzer`.
pub fn llvm_bcanalyzer() -> LlvmBcanalyzer {
    LlvmBcanalyzer::new()
}

/// Construct a new `llvm-dwarfdump` invocation. This assumes that `llvm-dwarfdump` is available
/// at `$LLVM_BIN_DIR/llvm-dwarfdump`.
pub fn llvm_dwarfdump() -> LlvmDwarfdump {
    LlvmDwarfdump::new()
}

/// A `llvm-readobj` invocation builder.
#[derive(Debug)]
#[must_use]
pub struct LlvmReadobj {
    cmd: Command,
}

/// A `llvm-profdata` invocation builder.
#[derive(Debug)]
#[must_use]
pub struct LlvmProfdata {
    cmd: Command,
}

/// A `llvm-filecheck` invocation builder.
#[derive(Debug)]
#[must_use]
pub struct LlvmFilecheck {
    cmd: Command,
}

/// A `llvm-objdump` invocation builder.
#[derive(Debug)]
#[must_use]
pub struct LlvmObjdump {
    cmd: Command,
}

/// A `llvm-ar` invocation builder.
#[derive(Debug)]
#[must_use]
pub struct LlvmAr {
    cmd: Command,
}

/// A `llvm-nm` invocation builder.
#[derive(Debug)]
#[must_use]
pub struct LlvmNm {
    cmd: Command,
}

/// A `llvm-bcanalyzer` invocation builder.
#[derive(Debug)]
#[must_use]
pub struct LlvmBcanalyzer {
    cmd: Command,
}

/// A `llvm-dwarfdump` invocation builder.
#[derive(Debug)]
#[must_use]
pub struct LlvmDwarfdump {
    cmd: Command,
}

crate::macros::impl_common_helpers!(LlvmReadobj);
crate::macros::impl_common_helpers!(LlvmProfdata);
crate::macros::impl_common_helpers!(LlvmFilecheck);
crate::macros::impl_common_helpers!(LlvmObjdump);
crate::macros::impl_common_helpers!(LlvmAr);
crate::macros::impl_common_helpers!(LlvmNm);
crate::macros::impl_common_helpers!(LlvmBcanalyzer);
crate::macros::impl_common_helpers!(LlvmDwarfdump);

/// Generate the path to the bin directory of LLVM.
#[must_use]
pub fn llvm_bin_dir() -> PathBuf {
    let llvm_bin_dir = env_var("LLVM_BIN_DIR");
    PathBuf::from(llvm_bin_dir)
}

impl LlvmReadobj {
    /// Construct a new `llvm-readobj` invocation with the `GNU` output style.
    /// This assumes that `llvm-readobj` is available at `$LLVM_BIN_DIR/llvm-readobj`.
    #[track_caller]
    pub fn new() -> Self {
        let llvm_readobj = llvm_bin_dir().join("llvm-readobj");
        let cmd = Command::new(llvm_readobj);
        let mut readobj = Self { cmd };
        readobj.elf_output_style("GNU");
        readobj
    }

    /// Specify the format of the ELF information.
    ///
    /// Valid options are `LLVM` (default), `GNU`, and `JSON`.
    pub fn elf_output_style(&mut self, style: &str) -> &mut Self {
        self.cmd.arg("--elf-output-style");
        self.cmd.arg(style);
        self
    }

    /// Provide an input file.
    pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
        self.cmd.arg(path.as_ref());
        self
    }

    /// Pass `--file-header` to display file headers.
    pub fn file_header(&mut self) -> &mut Self {
        self.cmd.arg("--file-header");
        self
    }

    /// Pass `--program-headers` to display program headers.
    pub fn program_headers(&mut self) -> &mut Self {
        self.cmd.arg("--program-headers");
        self
    }

    /// Pass `--symbols` to display the symbol table, including both local
    /// and global symbols.
    pub fn symbols(&mut self) -> &mut Self {
        self.cmd.arg("--symbols");
        self
    }

    /// Pass `--dynamic-table` to display the dynamic symbol table.
    pub fn dynamic_table(&mut self) -> &mut Self {
        self.cmd.arg("--dynamic-table");
        self
    }

    /// Specify the section to display.
    pub fn section(&mut self, section: &str) -> &mut Self {
        self.cmd.arg("--string-dump");
        self.cmd.arg(section);
        self
    }
}

impl LlvmProfdata {
    /// Construct a new `llvm-profdata` invocation. This assumes that `llvm-profdata` is available
    /// at `$LLVM_BIN_DIR/llvm-profdata`.
    #[track_caller]
    pub fn new() -> Self {
        let llvm_profdata = llvm_bin_dir().join("llvm-profdata");
        let cmd = Command::new(llvm_profdata);
        Self { cmd }
    }

    /// Provide an input file.
    pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
        self.cmd.arg(path.as_ref());
        self
    }

    /// Specify the output file path.
    pub fn output<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
        self.cmd.arg("-o");
        self.cmd.arg(path.as_ref());
        self
    }

    /// Take several profile data files generated by PGO instrumentation and merge them
    /// together into a single indexed profile data file.
    pub fn merge(&mut self) -> &mut Self {
        self.cmd.arg("merge");
        self
    }
}

impl LlvmFilecheck {
    /// Construct a new `llvm-filecheck` invocation. This assumes that `llvm-filecheck` is available
    /// at `$LLVM_FILECHECK`.
    #[track_caller]
    pub fn new() -> Self {
        let llvm_filecheck = env_var("LLVM_FILECHECK");
        let cmd = Command::new(llvm_filecheck);
        Self { cmd }
    }

    /// Pipe a read file into standard input containing patterns that will be matched against the .patterns(path) call.
    pub fn stdin<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
        self.cmd.stdin(input);
        self
    }

    /// Provide the patterns that need to be matched.
    pub fn patterns<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
        self.cmd.arg(path.as_ref());
        self
    }

    /// `--input-file` option.
    pub fn input_file<P: AsRef<Path>>(&mut self, input_file: P) -> &mut Self {
        self.cmd.arg("--input-file");
        self.cmd.arg(input_file.as_ref());
        self
    }
}

impl LlvmObjdump {
    /// Construct a new `llvm-objdump` invocation. This assumes that `llvm-objdump` is available
    /// at `$LLVM_BIN_DIR/llvm-objdump`.
    pub fn new() -> Self {
        let llvm_objdump = llvm_bin_dir().join("llvm-objdump");
        let cmd = Command::new(llvm_objdump);
        Self { cmd }
    }

    /// Provide an input file.
    pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
        self.cmd.arg(path.as_ref());
        self
    }

    /// Disassemble all executable sections found in the input files.
    pub fn disassemble(&mut self) -> &mut Self {
        self.cmd.arg("-d");
        self
    }
}

impl LlvmAr {
    /// Construct a new `llvm-ar` invocation. This assumes that `llvm-ar` is available
    /// at `$LLVM_BIN_DIR/llvm-ar`.
    pub fn new() -> Self {
        let llvm_ar = llvm_bin_dir().join("llvm-ar");
        let cmd = Command::new(llvm_ar);
        Self { cmd }
    }

    /// Automatically pass the commonly used arguments `rcus`, used for combining one or more
    /// input object files into one output static library file.
    pub fn obj_to_ar(&mut self) -> &mut Self {
        self.cmd.arg("rcus");
        self
    }

    /// Like `obj_to_ar` except creating a thin archive.
    pub fn obj_to_thin_ar(&mut self) -> &mut Self {
        self.cmd.arg("rcus").arg("--thin");
        self
    }

    /// Extract archive members back to files.
    pub fn extract(&mut self) -> &mut Self {
        self.cmd.arg("x");
        self
    }

    /// Print the table of contents.
    pub fn table_of_contents(&mut self) -> &mut Self {
        self.cmd.arg("t");
        self
    }

    /// Provide an output, then an input file. Bundled in one function, as llvm-ar has
    /// no "--output"-style flag.
    pub fn output_input(&mut self, out: impl AsRef<Path>, input: impl AsRef<Path>) -> &mut Self {
        self.cmd.arg(out.as_ref());
        self.cmd.arg(input.as_ref());
        self
    }
}

impl LlvmNm {
    /// Construct a new `llvm-nm` invocation. This assumes that `llvm-nm` is available
    /// at `$LLVM_BIN_DIR/llvm-nm`.
    pub fn new() -> Self {
        let llvm_nm = llvm_bin_dir().join("llvm-nm");
        let cmd = Command::new(llvm_nm);
        Self { cmd }
    }

    /// Provide an input file.
    pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
        self.cmd.arg(path.as_ref());
        self
    }
}

impl LlvmBcanalyzer {
    /// Construct a new `llvm-bcanalyzer` invocation. This assumes that `llvm-bcanalyzer` is available
    /// at `$LLVM_BIN_DIR/llvm-bcanalyzer`.
    pub fn new() -> Self {
        let llvm_bcanalyzer = llvm_bin_dir().join("llvm-bcanalyzer");
        let cmd = Command::new(llvm_bcanalyzer);
        Self { cmd }
    }

    /// Provide an input file.
    pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
        self.cmd.arg(path.as_ref());
        self
    }
}

impl LlvmDwarfdump {
    /// Construct a new `llvm-dwarfdump` invocation. This assumes that `llvm-dwarfdump` is available
    /// at `$LLVM_BIN_DIR/llvm-dwarfdump`.
    pub fn new() -> Self {
        let llvm_dwarfdump = llvm_bin_dir().join("llvm-dwarfdump");
        let cmd = Command::new(llvm_dwarfdump);
        Self { cmd }
    }

    /// Provide an input file.
    pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
        self.cmd.arg(path.as_ref());
        self
    }
}