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
use std::path::{Path, PathBuf};

use crate::{env_var, Command};

/// Construct a new `llvm-readobj` invocation. 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()
}

/// 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,
}

crate::impl_common_helpers!(LlvmReadobj);
crate::impl_common_helpers!(LlvmProfdata);
crate::impl_common_helpers!(LlvmFilecheck);
crate::impl_common_helpers!(LlvmObjdump);

/// 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. 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);
        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
    }

    /// Pass `--file-header` to display file headers.
    pub fn file_header(&mut self) -> &mut Self {
        self.cmd.arg("--file-header");
        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.set_stdin(input.as_ref().to_vec().into_boxed_slice());
        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
    }
}

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
    }
}