-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathcompiler.rs
146 lines (124 loc) · 4.35 KB
/
compiler.rs
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
//! Builder pattern around compiler invocations. This is a wrapper around [`Command`],
//! with adequate defaults and added functions or types to help make compiler invocations
//! in the testing project safer, easier and less verbose.
use std::path::Path;
use std::process::{Command, Stdio};
use crate::args::Args;
/// All *used* Rust editions
#[derive(Clone, Copy)]
pub enum Edition {
E2021,
}
impl Edition {
fn to_str(self) -> &'static str {
match self {
Edition::E2021 => "2021",
}
}
}
/// All compiler kinds used in the testsuite
#[derive(Clone, Copy)]
pub enum Kind {
Rust1,
RustcBootstrap,
}
/// All crate types used in the testsuite
#[derive(Clone, Copy)]
pub enum CrateType {
Library,
}
impl Kind {
/// Get the path associated with a specific compiler kind
fn as_path_from_args(self, args: &Args) -> &Path {
match self {
Kind::Rust1 => &args.gccrs,
Kind::RustcBootstrap => &args.rustc,
}
}
}
/// Extend the [`Command`] type with functions associated with the compiler we're going to run
trait CommandExt {
/// Set the default arguments for a specific compiler
fn default_args(&mut self, kind: Kind) -> &mut Command;
/// Set the default environment variables for a specific compiler
fn default_env(&mut self, kind: Kind) -> &mut Command;
}
impl CommandExt for Command {
fn default_args(&mut self, kind: Kind) -> &mut Command {
match kind {
// specify Rust language by default, which allows us to compile Rust files with funny extensions
// use experimental flag
Kind::Rust1 => self.arg("-frust-incomplete-and-experimental-compiler-do-not-use"),
Kind::RustcBootstrap => self,
}
}
fn default_env(&mut self, kind: Kind) -> &mut Command {
match kind {
Kind::Rust1 => self,
Kind::RustcBootstrap => self.env("RUSTC_BOOTSTRAP", "1"),
}
}
}
/// Represents a compiler invocation
pub struct Compiler {
cmd: Command,
kind: Kind,
stdout: Option<Stdio>,
stderr: Option<Stdio>,
}
impl Compiler {
/// Create a new compiler invocation
pub fn new(kind: Kind, args: &Args) -> Compiler {
Compiler {
cmd: Command::new(kind.as_path_from_args(args)),
kind,
stdout: None,
stderr: None,
}
}
fn kind(&self) -> &Kind {
&self.kind
}
/// Set the crate name to use for a compiler invocation. This is equivalent
/// to `--crate-name` for `rustc` and `-frust-crate-name` for `gccrs`
pub fn crate_name(mut self, crate_name: &str) -> Compiler {
match self.kind() {
Kind::Rust1 => self.cmd.arg("-frust-crate-name"),
Kind::RustcBootstrap => self.cmd.arg("--crate-name"),
};
self.cmd.arg(crate_name);
self
}
/// Choose which type of crate to compile. This is equivalent to --crate-type for `rustc`
/// and has no equivalent for `gccrs`
pub fn crate_type(mut self, crate_type: CrateType) -> Compiler {
if let Kind::RustcBootstrap = self.kind() {
self.cmd.arg("--crate-type").arg(match crate_type {
CrateType::Library => "lib",
});
}
self
}
/// Set the edition to use for a compiler invocation. This is equivalent to
/// `--edition` for `rustc` and `-frust-edition` for `gccrs`
pub fn edition(mut self, edition: Edition) -> Compiler {
match self.kind() {
Kind::Rust1 => self.cmd.arg("-frust-edition"),
Kind::RustcBootstrap => self.cmd.arg("--edition"),
};
self.cmd.arg(edition.to_str());
self
}
/// Access the underlaying [`Command`] of a compiler invocation. This is a destructive operation
/// and should only be done as the last step of the building process. You can then choose to pass
/// additional arguments, spawn the command, etc... as you would with a regularly built [`Command`]
pub fn command(&mut self) -> &mut Command {
let kind = self.kind;
self.cmd
.default_args(kind)
.default_env(kind)
// We want errors and output to be silent by default in the testing project
.stderr(self.stdout.take().unwrap_or_else(Stdio::null))
.stdout(self.stderr.take().unwrap_or_else(Stdio::null))
}
}