-
Notifications
You must be signed in to change notification settings - Fork 85
/
build.rs
189 lines (171 loc) · 7.59 KB
/
build.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
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
use std::env;
use std::fs::OpenOptions;
#[cfg(not(feature = "hosted"))]
use std::io::Read;
use std::io::Write;
use std::path::PathBuf;
fn out_dir() -> PathBuf { PathBuf::from(env::var_os("OUT_DIR").unwrap()) }
/// Helper macro that returns a constant number of features enabled among specified list.
macro_rules! count_enabled_features {
($($feature:literal),*) => {
{
let mut enabled_features = 0;
$(
enabled_features += cfg!(feature = $feature) as u32;
)*
enabled_features
}
}
}
/// Helper macro that returns a compile-time error if multiple or none of the
/// features of some category are defined.
///
/// # Example
///
/// Given the following code:
///
/// ```
/// allow_single_feature!("feature-category-name", "a", "b", "c");
/// ```
///
/// These runs fail compilation check:
/// $ cargo check --features a,b # error msg: 'Multiple feature-category-name specified. Only one is allowed.
/// $ cargo check # error msg: 'None of the feature-category-name specified. Pick one.'
///
/// This compiles:
/// $ cargo check --feature a
macro_rules! allow_single_feature {
($name:literal, $($feature:literal),*) => {
const _: () = {
const MSG_MULTIPLE: &str = concat!("\nMultiple ", $name, " specified. Only one is allowed.");
const MSG_NONE: &str = concat!("\nNone of the ", $name, " specified. Pick one.");
match count_enabled_features!($($feature),*) {
0 => std::panic!("{}", MSG_NONE),
1 => {}
2.. => std::panic!("{}", MSG_MULTIPLE),
}
};
}
}
macro_rules! allow_single_target_feature {
($($args:tt)+) => {
allow_single_feature!("targets", $($args)+);
}
}
#[cfg(feature = "precursor")] // Gitrevs are only relevant for Precursor target
macro_rules! allow_single_gitrev_feature {
($($args:tt)+) => {
allow_single_feature!("gitrevs", $($args)+);
}
}
fn main() {
// ------ check that the feature flags are sane -----
// note on selecting "hosted" mode. An explicit "hosted" flag is provided to clarify
// the build system's intent. However, in general, most packages prefer to use this idiom:
//
// #[cfg(not(target_os = "xous"))]
//
// This flag is synonymous with feature = "hosted", and it also makes "hosted" mode the
// "default" package in the case that the code is being built in CI or in external
// packages that don't know to configure the "hosted" feature flag.
//
// This idiom breaks if Xous ever gets to the point of compiling and running code on
// its own platform; but generally, if the target binary is running on e.g. windows/linux/non-xous
// target triples, the user's intent was "hosted" mode.
//
// This script retains the use of an explicit "hosted" flag because we want to catch
// unintentional build system misconfigurations that meant to build for a target other
// than "hosted", rather than just falling back silently to defaults.
allow_single_target_feature!(
"precursor",
"hosted",
"renode",
"atsama5d27",
"cramium-soc",
"cramium-fpga"
);
#[cfg(feature = "precursor")]
allow_single_gitrev_feature!("precursor-perflib", "precursor-dvt", "precursor-pvt");
// ----- select an SVD file based on a specific revision -----
#[cfg(feature = "precursor-perflib")]
let svd_filenames = vec!["precursor/soc-perf.svd"];
#[cfg(feature = "precursor-perflib")]
let generated_filename = "src/generated/precursor_perf.rs";
#[cfg(feature = "renode")]
let svd_filenames = vec!["renode/renode.svd"];
#[cfg(feature = "renode")]
let generated_filename = "src/generated/renode.rs";
#[cfg(feature = "precursor-dvt")]
let svd_filenames = vec!["precursor/soc-dvt.svd"];
#[cfg(feature = "precursor-dvt")]
let generated_filename = "src/generated/precursor_dvt.rs";
#[cfg(feature = "precursor-pvt")]
let svd_filenames = vec!["precursor/soc-pvt.svd"];
#[cfg(feature = "precursor")]
let generated_filename = "src/generated/precursor_pvt.rs";
#[cfg(feature = "atsama5d27")]
let svd_filenames = vec!["atsama5d/ATSAMA5D27.svd"];
#[cfg(feature = "atsama5d27")]
let generated_filename = "src/generated/atsama5d27.rs";
#[cfg(feature = "cramium-soc")]
let svd_filenames = vec!["cramium/core.svd", "cramium/daric.svd"];
#[cfg(feature = "cramium-soc")]
let generated_filename = "src/generated/cramium_soc.rs";
#[cfg(feature = "cramium-fpga")]
let svd_filenames = vec!["cramium/soc.svd", "cramium/core.svd", "cramium/daric.svd"];
#[cfg(feature = "cramium-fpga")]
let generated_filename = "src/generated/cramium_fpga.rs";
// ----- control file generation and rebuild sequence -----
// check and see if the configuration has changed since the last build. This should be
// passed by the build system (e.g. xtask) if the feature is used.
//
// Debug this using:
// $env:CARGO_LOG="cargo::core::compiler::fingerprint=info"
#[cfg(not(feature = "hosted"))]
{
let mut svd_files = Vec::new();
for svd in svd_filenames.iter() {
let svd_file_path = std::path::Path::new(&svd);
println!("cargo:rerun-if-changed={}", svd_file_path.canonicalize().unwrap().display());
svd_files.push(std::fs::File::open(svd_file_path).unwrap());
}
// Regenerate the utra file in RAM.
let mut dest_vec = vec![];
svd2utra::generate(svd_files, &mut dest_vec).unwrap();
// If the file exists, check to see if it is different from what we just generated.
// If not, skip writing the new file.
// If the file doesn't exist, or if it's different, write out a new utra file.
let should_write = if let Ok(mut existing_file) = std::fs::File::open(generated_filename) {
let mut existing_file_contents = vec![];
existing_file
.read_to_end(&mut existing_file_contents)
.expect("couldn't read existing utra generated file");
existing_file_contents != dest_vec
} else {
true
};
if should_write {
let mut dest_file = std::fs::File::create(generated_filename).expect("couldn't open dest file");
dest_file.write_all(&dest_vec).expect("couldn't write contents to utra file");
}
// ----- feedback SVD path to build framework -----
// pass the computed SVD filename back to the build system, so that we can pass this
// on to the image creation program. This is necessary so we can extract all the memory
// regions and create the whitelist of memory pages allowed to the kernel; any page not
// explicitly used by the hardware model is ineligible for mapping and allocation by any
// process. This helps to prevent memory aliasing attacks by hardware blocks that partially
// decode their addresses (this would be in anticipation of potential hardware bugs; ideally
// this isn't ever a problem).
let svd_path = out_dir().join("../../SVD_PATH");
let mut svd_file = OpenOptions::new().create(true).write(true).truncate(true).open(svd_path).unwrap();
for svd in svd_filenames.iter() {
writeln!(svd_file, "utralib/{}", svd).unwrap();
}
}
#[cfg(feature = "hosted")]
{
let svd_path = out_dir().join("../../SVD_PATH");
let mut svd_file = OpenOptions::new().create(true).write(true).truncate(true).open(svd_path).unwrap();
write!(svd_file, "").unwrap(); // there is no SVD file for hosted mode
}
}