Skip to content

Commit 9f01ebd

Browse files
committed
fix stack overflow and improve logging in bootstrapping, build bootstrap into nearest target
1 parent 84e1055 commit 9f01ebd

File tree

4 files changed

+129
-19
lines changed

4 files changed

+129
-19
lines changed

crates/bevy_api_gen/Cargo.bootstrap.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ edition = "2021"
66

77
[dependencies]
88
mlua = { version = "0.9.2", features = ["lua54", "vendored", "send", "macros"] }
9-
bevy_reflect = { version = "0.14", features = ["bevy", "bevy_math"] }
9+
bevy_reflect = { version = "0.14", features = [
10+
"bevy",
11+
"glam",
12+
"petgraph",
13+
"smallvec",
14+
"uuid",
15+
] }
1016

1117
[workspace]

crates/bevy_api_gen/src/bin/main.rs

Lines changed: 85 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ use std::{
22
collections::HashMap,
33
env,
44
fs::{create_dir_all, File},
5-
io::Write,
6-
path::Path,
5+
io::{BufRead, Write},
6+
path::{Path, PathBuf},
77
process::{Command, Stdio},
88
};
99

1010
use bevy_api_gen::*;
1111
use cargo_metadata::camino::Utf8Path;
1212
use clap::Parser;
13-
use log::{debug, info};
13+
use log::{debug, error, info};
1414
use strum::VariantNames;
1515
use tera::Context;
1616

@@ -25,19 +25,34 @@ fn main() {
2525
}
2626
env_logger::init();
2727

28+
info!("Computing crate metadata");
2829
let metadata = cargo_metadata::MetadataCommand::new()
2930
.no_deps()
3031
.other_options(["--all-features".to_string(), "--offline".to_string()])
3132
.exec()
3233
.unwrap();
34+
3335
let crates = metadata
3436
.workspace_packages()
3537
.iter()
3638
.map(|p| p.name.to_owned())
3739
.collect::<Vec<_>>();
40+
41+
info!("Computing active features");
3842
let include_crates = match (&args.workspace_root, args.cmd.is_generate()) {
3943
(Some(root), true) => {
4044
let feature_graph = FeatureGraph::from_metadata(&metadata, root);
45+
info!(
46+
"Using workspace root: {}, found {} crates",
47+
feature_graph.workspace_root,
48+
feature_graph.crates.len()
49+
);
50+
51+
info!(
52+
"Computing all transitive dependencies for enabled top-level features: {}",
53+
args.features.join(",")
54+
);
55+
4156
let dependencies = feature_graph
4257
.dependencies_for_features(args.features.as_ref(), !args.no_default_features)
4358
.into_iter()
@@ -52,6 +67,8 @@ fn main() {
5267
let plugin_subdir = format!("plugin-{}", env!("RUSTC_CHANNEL"));
5368
let plugin_target_dir = metadata.target_directory.join(plugin_subdir);
5469

70+
info!("Computing wokrspace metadata");
71+
5572
// inform the deps about the workspace crates, this is going to be useful when working with meta files as we will be able to
5673
// know when to panic if a crate is not found
5774
// it's also useful to pass around the output directory for our Args default values to be able to compute them
@@ -132,14 +149,13 @@ fn main() {
132149
_ => {}
133150
}
134151

135-
let temp_dir = tempdir::TempDir::new("bevy_api_gen_bootstrap")
136-
.expect("Error occured when trying to acquire temp file");
152+
let temp_dir = find_bootstrap_dir();
137153

138-
debug!("Temporary directory: {}", &temp_dir.path().display());
154+
debug!("Bootstrap directory: {}", &temp_dir.as_path().display());
139155

140-
write_bootstrap_files(temp_dir.path());
156+
write_bootstrap_files(temp_dir.as_path());
141157

142-
let bootstrap_rlibs = build_bootstrap(temp_dir.path(), &plugin_target_dir.join("bootstrap"));
158+
let bootstrap_rlibs = build_bootstrap(temp_dir.as_path(), &plugin_target_dir.join("bootstrap"));
143159

144160
if bootstrap_rlibs.len() == BOOTSTRAP_DEPS.len() {
145161
let extern_args = bootstrap_rlibs
@@ -195,20 +211,50 @@ fn build_bootstrap(
195211
let mut cmd = Command::new("cargo")
196212
.current_dir(temp_dir)
197213
.stdout(Stdio::piped())
214+
.stderr(Stdio::piped())
198215
.args(["build", "--message-format=json"])
199216
.spawn()
200217
.unwrap();
201218

219+
info!(
220+
"cd {} && cargo build --message-format=json",
221+
temp_dir.display()
222+
);
223+
202224
let reader = std::io::BufReader::new(cmd.stdout.take().unwrap());
225+
let err_reader = std::io::BufReader::new(cmd.stderr.take().unwrap());
203226

204227
std::fs::create_dir_all(cache_dir).unwrap();
205228

206229
let mut bootstrap_rlibs = HashMap::with_capacity(BOOTSTRAP_DEPS.len());
207230
for msg in cargo_metadata::Message::parse_stream(reader) {
208-
if let cargo_metadata::Message::CompilerArtifact(artifact) = msg.unwrap() {
231+
let msg = msg.unwrap();
232+
if let cargo_metadata::Message::CompilerArtifact(artifact) = msg {
209233
for artifact in artifact.filenames.into_iter() {
210234
process_artifact(artifact, &mut bootstrap_rlibs);
211235
}
236+
} else {
237+
match msg {
238+
cargo_metadata::Message::BuildFinished(finished) => {
239+
if !finished.success {
240+
error!("Bootstrapping crate failed to build artifact");
241+
}
242+
}
243+
cargo_metadata::Message::TextLine(t) => {
244+
info!("{t}");
245+
}
246+
cargo_metadata::Message::CompilerMessage(msg) => {
247+
info!("{msg}");
248+
}
249+
_ => {}
250+
}
251+
}
252+
}
253+
for msg in err_reader.lines() {
254+
if let Ok(line) = msg {
255+
info!("{line}");
256+
} else {
257+
panic!("Failed to read cargo stderr");
212258
}
213259
}
214260

@@ -224,10 +270,17 @@ fn build_bootstrap(
224270
std::fs::copy(path, dest).unwrap();
225271
}
226272
}
273+
match cmd.wait() {
274+
Ok(status) => {
275+
if !status.success() {
276+
panic!("Building bootstrap crate returned a failure status code");
277+
}
278+
}
279+
Err(e) => {
280+
panic!("Failed to wait on cargo build process: {}", e);
281+
}
282+
}
227283

228-
if !cmd.wait().unwrap().success() {
229-
panic!("Building bootstrap crate returned a failure status code");
230-
};
231284
bootstrap_rlibs
232285
}
233286

@@ -248,6 +301,26 @@ fn process_artifact(
248301
}
249302
}
250303

304+
/// finds best location for bootstrapping crate
305+
/// this will be the nearest target/bevy_api_gen_bootstrap directory
306+
fn find_bootstrap_dir() -> PathBuf {
307+
let mut path = env::current_dir().unwrap();
308+
loop {
309+
if path.join("target").exists() {
310+
break;
311+
} else {
312+
path = path.parent().unwrap().to_owned();
313+
}
314+
}
315+
316+
path.push("target");
317+
path.push("bevy_api_gen_bootstrap");
318+
319+
// create all the directories
320+
create_dir_all(&path).unwrap();
321+
path
322+
}
323+
251324
/// Generate bootstrapping crate files
252325
fn write_bootstrap_files(path: &Path) {
253326
// write manifest file 'Cargo.toml'

crates/bevy_api_gen/src/feature_graph.rs

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use std::collections::{HashMap, HashSet};
22

3-
use cargo_metadata::{Metadata, Package};
3+
use cargo_metadata::{DependencyKind, Metadata, Package};
44
use itertools::{Either, Itertools};
5+
use log::{debug, info};
56

67
#[derive(Clone, Debug)]
78
pub enum FeatureEffect {
89
/// A feature which enables another feature
910
/// in a dependency
11+
/// if enable_optional is true, the dependency may not itself be enabled in which case the feature is not enabled
1012
EnableDepFeature {
1113
feature: String,
1214
dependency: String,
@@ -53,8 +55,8 @@ pub enum Depdenency {
5355

5456
#[derive(Debug)]
5557
pub struct FeatureGraph {
56-
workspace_root: String,
57-
crates: Vec<Crate>,
58+
pub workspace_root: String,
59+
pub crates: Vec<Crate>,
5860
}
5961

6062
impl FeatureGraph {
@@ -72,7 +74,7 @@ impl FeatureGraph {
7274
.find(|c| c.name == self.workspace_root)
7375
.unwrap();
7476
let mut buffer = Default::default();
75-
self.dependencies_for_features_on_crate(root, features, include_default, &mut buffer);
77+
self.dependencies_for_features_on_crate(root, features, include_default, &mut buffer, 0);
7678

7779
buffer.iter().map(|c| c.name.as_str()).collect()
7880
}
@@ -83,7 +85,18 @@ impl FeatureGraph {
8385
features: &[String],
8486
include_default: bool,
8587
buffer: &mut HashSet<&'a Crate>,
88+
depth: usize,
8689
) {
90+
let log_prefix = "|".to_owned() + &"-".repeat(depth);
91+
debug!(
92+
"{log_prefix}Processing dependencies for crate: `{}` with features: {}",
93+
crate_.name,
94+
features.join(", ")
95+
);
96+
if depth > 30 {
97+
panic!("Recursion depth exceeded");
98+
}
99+
87100
let active_features = features
88101
.iter()
89102
.map(|f| {
@@ -106,6 +119,7 @@ impl FeatureGraph {
106119
dependency,
107120
enable_optional,
108121
} => {
122+
// we ignore optional dependencies's features, is this what we want to do?
109123
if *enable_optional {
110124
deps.entry(self.crates.iter().find(|c| c.name == *dependency).unwrap())
111125
.or_default()
@@ -129,10 +143,26 @@ impl FeatureGraph {
129143
}));
130144

131145
// repeat for all dependencies recursively
146+
// we also ignore optional dependencies here, again is this what we want to do? I can't remember
132147
for (dep, features) in deps.iter() {
148+
debug!(
149+
"{log_prefix}Adding dependency: {} with features {:?}",
150+
dep.name, features
151+
);
133152
buffer.insert(dep);
134-
self.dependencies_for_features_on_crate(dep, features, include_default, buffer);
153+
self.dependencies_for_features_on_crate(
154+
dep,
155+
features,
156+
include_default,
157+
buffer,
158+
depth + 1,
159+
);
135160
}
161+
162+
debug!(
163+
"{log_prefix}Finished processing dependencies for crate: `{}`",
164+
crate_.name
165+
);
136166
}
137167

138168
/// "flattens" feature effects to a list of effects based on the selected active features, features which enable other features are expanded until
@@ -182,6 +212,7 @@ impl FeatureGraph {
182212
let (optional_dependencies, other_dependencies) = meta
183213
.dependencies
184214
.iter()
215+
.filter(|d| d.kind == DependencyKind::Normal) // dev dependencies can introduce weird cycles, and we don't care about them anyway
185216
.map(|f| (f.name.clone(), f.optional))
186217
.partition_map(|(name, opt)| {
187218
if opt {

makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ clean_bevy:
6060
cd ${BEVY_PATH} && cargo clean
6161

6262
generate_bevy:
63-
cd ${BEVY_PATH} && cargo +${NIGHTLY_VERSION} bevy-api-gen generate --output ${OUTPUT_PATH} --template-args '{ "self_is_bevy_script_api": true}' --features ${GEN_BEVY_FEATURES}
63+
cd ${BEVY_PATH} && cargo +${NIGHTLY_VERSION} bevy-api-gen generate --output ${OUTPUT_PATH} --template-args '{ "self_is_bevy_script_api": true}' --features ${GEN_BEVY_FEATURES} -vv
6464

6565
collect_bevy:
6666
cd ${BEVY_PATH} && cargo +${NIGHTLY_VERSION} bevy-api-gen collect --output ${OUTPUT_PATH} --template-args '{ "self_is_bevy_script_api": true}'

0 commit comments

Comments
 (0)