Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
crates/bevy_mod_scripting_functions/src/bevy_bindings/*.rs linguist-generated
crates/bevy_mod_scripting_functions/src/bevy_gindings/*.rs -diff -merge
crates/bindings/**/* linguist-generated
crates/bindings/**/* -diff -merge
17 changes: 17 additions & 0 deletions .github/workflows/bevy_mod_scripting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ jobs:
base: main
filters: |
src:
- 'bindings/**'
- 'src/**'
- 'crates/**'
- 'examples/**'
Expand Down Expand Up @@ -132,6 +133,7 @@ jobs:
echo "Clippy version: $(cargo clippy --version)"
echo "Active toolchain: $(rustup show active-toolchain)"
continue-on-error: true

- name: Rust Cache
if: ${{ needs.check-needs-run.outputs.any-changes == 'true' && (matrix.run_args.run_on_forks || needs.check-is-fork.outputs.is_fork != 'true') }}
uses: Swatinem/rust-cache@v2.7.7
Expand Down Expand Up @@ -185,4 +187,19 @@ jobs:
sed -n 's/.*pull\/\([0-9]*\).*/\1/p' pr.txt > pr_number.txt
PRNUMBER=$(cat pr_number.txt)
gh pr merge $PRNUMBER --squash
fi

rollup-check:
name: Required Checks Successful
needs: [check]
runs-on: ubuntu-latest
if: ${{ always() }}
steps:
- name: Check if all jobs succeeded
run: |
if [[ "${{ needs.check.result }}" != "success" ]]; then
echo "One or more checks failed. See detailed job results above."
exit 1
else
echo "All checks passed successfully!"
fi
1 change: 1 addition & 0 deletions .github/workflows/synchronize_bindings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ jobs:
- name: Generate Bindings
run: |
cargo xtask codegen
cargo fmt
- name: Check for changes and create diff
id: check_changes
run: |
Expand Down
54 changes: 54 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ default = [
"bevy_reflect_bindings",
"bevy_time_bindings",
"bevy_transform_bindings",
"bevy_color_bindings",
"bevy_core_pipeline_bindings",
]

lua = [
Expand All @@ -46,10 +48,29 @@ luau = ["bevy_mod_scripting_lua/luau", "lua"]

# bindings
core_functions = ["bevy_mod_scripting_functions/core_functions"]

bevy_a11y_bindings = ["bevy_mod_scripting_functions/bevy_a11y"]
bevy_animation_bindings = ["bevy_mod_scripting_functions/bevy_animation"]
bevy_asset_bindings = ["bevy_mod_scripting_functions/bevy_asset"]
bevy_color_bindings = ["bevy_mod_scripting_functions/bevy_color"]
bevy_core_pipeline_bindings = [
"bevy_mod_scripting_functions/bevy_core_pipeline",
]
bevy_ecs_bindings = ["bevy_mod_scripting_functions/bevy_ecs"]
bevy_gizmos_bindings = ["bevy_mod_scripting_functions/bevy_gizmos"]
bevy_gltf_bindings = ["bevy_mod_scripting_functions/bevy_gltf"]
bevy_image_bindings = ["bevy_mod_scripting_functions/bevy_image"]
bevy_input_bindings = ["bevy_mod_scripting_functions/bevy_input"]
bevy_input_focus_bindings = ["bevy_mod_scripting_functions/bevy_input_focus"]
bevy_math_bindings = ["bevy_mod_scripting_functions/bevy_math"]
bevy_mesh_bindings = ["bevy_mod_scripting_functions/bevy_mesh"]
bevy_pbr_bindings = ["bevy_mod_scripting_functions/bevy_pbr"]
bevy_picking_bindings = ["bevy_mod_scripting_functions/bevy_picking"]
bevy_reflect_bindings = ["bevy_mod_scripting_functions/bevy_reflect"]
bevy_render_bindings = ["bevy_mod_scripting_functions/bevy_render"]
bevy_scene_bindings = ["bevy_mod_scripting_functions/bevy_scene"]
bevy_sprite_bindings = ["bevy_mod_scripting_functions/bevy_sprite"]
bevy_text_bindings = ["bevy_mod_scripting_functions/bevy_text"]
bevy_time_bindings = ["bevy_mod_scripting_functions/bevy_time"]
bevy_transform_bindings = ["bevy_mod_scripting_functions/bevy_transform"]

Expand Down Expand Up @@ -105,6 +126,38 @@ bevy_diagnostic = { version = "0.16.0", default-features = false }
bevy_platform = { version = "0.16.0", default-features = false }
bevy_time = { version = "0.16.0", default-features = false }
bevy_input = { version = "0.16.0", default-features = false }
bevy_a11y = { version = "0.16.0", default-features = false, features = [
"std",
"bevy_reflect",
] }
bevy_animation = { version = "0.16.0", default-features = false }
bevy_color = { version = "0.16.0", default-features = false, features = [
"std",
"bevy_reflect",
] }
bevy_core_pipeline = { version = "0.16.0", default-features = false }
bevy_gizmos = { version = "0.16.0", default-features = false }
bevy_gltf = { version = "0.16.0", default-features = false }
bevy_image = { version = "0.16.0", default-features = false, features = [
"bevy_reflect",
] }
bevy_input_focus = { version = "0.16.0", default-features = false, features = [
"std",
"bevy_reflect",
] }
bevy_mesh = { version = "0.16.0", default-features = false }
bevy_pbr = { version = "0.16.0", default-features = false }
bevy_picking = { version = "0.16.0", default-features = false }
bevy_render = { version = "0.16.0", default-features = false }
bevy_scene = { version = "0.16.0", default-features = false }
bevy_sprite = { version = "0.16.0", default-features = false }
bevy_text = { version = "0.16.0", default-features = false }
bevy_window = { version = "0.16.0", default-features = false, features = [
"bevy_reflect",
"std",
] }
bevy_winit = { version = "0.16.0", default-features = false }

glam = { version = "0.29.3", default-features = false }
uuid = { version = "1.11", default-features = false }
smol_str = { version = "0.2.0", default-features = false }
Expand Down Expand Up @@ -186,6 +239,7 @@ members = [
"crates/lad_backends/mdbook_lad_preprocessor",
"crates/ladfile_builder",
"crates/bevy_system_reflection",
"crates/bindings/*",
]
resolver = "2"
exclude = ["codegen", "crates/macro_tests", "xtask"]
Expand Down
2 changes: 1 addition & 1 deletion check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ WORKSPACE_DIR="$PWD"
cd "$(dirname "$0")"
# if the path is in /bevy_api_gen then we run the codegen check

if [[ "$WORKSPACE_DIR" == *"/bevy_api_gen"* ]]; then
if [[ "$WORKSPACE_DIR" == *"/codegen"* ]]; then
# save output to file as well as stdout and stderr
cargo xtask check --ide-mode --kind codegen
elif [[ "$WORKSPACE_DIR" == *"/xtask"* ]]; then
Expand Down
2 changes: 2 additions & 0 deletions codegen/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[alias]
xtask = "run --manifest-path ../xtask/Cargo.toml --package xtask --"
32 changes: 30 additions & 2 deletions codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ include = [
]

[workspace]
members = []
members = ["crates/crate_feature_graph"]
exclude = [
"crates/test_crates/workspace/root",
"crates/test_crates/workspace/crate_a",
"crates/test_crates/workspace/crate_b",
]

[[bin]]
name = "cargo-bms-codegen"
Expand All @@ -37,9 +42,12 @@ debug = false
[package.metadata.rust-analyzer]
rustc_private = true

[dependencies]
[workspace.dependencies]
crate_feature_graph = { path = "crates/crate_feature_graph", version = "0.1" }
petgraph = { version = "0.8" }
log = "0.4"
env_logger = "0.11"
pretty_env_logger = "0.4"
indexmap = "2"
cargo_metadata = "0.18"
serde_json = "1"
Expand All @@ -53,6 +61,26 @@ convert_case = "0.6"
syn = { version = "2", features = ["parsing"], default-features = false }
itertools = "0.12"
chrono = "0.4"
pretty_assertions = "1.3"

[dependencies]
crate_feature_graph = { workspace = true, features = ["dot_parser", "serde"] }
clap = { workspace = true }
tera = { workspace = true }
strum = { workspace = true }
include_dir = { workspace = true }
syn = { workspace = true }
itertools = { workspace = true }
log = { workspace = true }
# env_logger = { workspace = true }
pretty_env_logger = { workspace = true }
indexmap = { workspace = true }
cargo_metadata = { workspace = true }
serde_json = { workspace = true }
serde = { workspace = true }
prettyplease = { workspace = true }
convert_case = { workspace = true }
chrono = { workspace = true }

[build-dependencies]
toml = "0.8"
35 changes: 35 additions & 0 deletions codegen/crates/crate_feature_graph/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
name = "crate_feature_graph"
version = "0.1.0"
edition = "2024"
readme = "README.md"
license = "MIT OR Apache-2.0"
include = ["src/**", "bin/**", "Cargo.toml", "README.md", "example.png"]
description = "A tool to visualize the flow of features throughout your crate graph."

[lib]
path = "src/lib.rs"

[[bin]]
name = "crate_feature_graph"
path = "bin/main.rs"

[features]
dot_parser = ["petgraph/dot_parser"]
serde = ["dep:serde", "dep:serde_json"]

[dependencies]
petgraph = { workspace = true }
cargo_metadata = { workspace = true }
itertools = { workspace = true }
log = { workspace = true }
clap = { workspace = true, features = ["derive"] }
serde = { workspace = true, features = ["derive"], optional = true }
serde_json = { workspace = true, optional = true }
pretty_env_logger = { workspace = true }
indexmap = { workspace = true, features = ["serde"] }

[dev-dependencies]
petgraph = { workspace = true, features = ["dot_parser"] }
pretty_assertions = { workspace = true }
env_logger = { workspace = true }
28 changes: 28 additions & 0 deletions codegen/crates/crate_feature_graph/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Crate Feature Graph

![Example output graph](./example.png)

A tool for visualising feature flow in a rust workspace, from the perspective of build time features.

## Features
- Compute feature flow throughout your workspace
- Filter out crates you don't want to see from the graph, but retain the connections
- Output as a dot graph or if the `dot_parser` feature is disabled as a text representation

## Usage
You can either filter the output or generate the entire graph.
If you filter out any crates, the edges will be "collapsed" at the edges of the filtered crates, so you can still see how features propagate through the graph.

For larger workspaces it is recommended to generate an svg so you can properly view the entire graph.

features obey the following syntax:
- `feature1` - enable feature1 in the root crate
- `crate/feature2` - enable feature2 in the crate crate (and implicitly enable crate as a dependency)
- `crate/default` - enable the default feature for the crate
- `default` - enable the default feature for the root crate

```bash
cargo install crate_feature_graph --features dot_parser

RUST_LOG=info cargo_feature_graph --manifest-path path/to/Cargo.toml --features="feature1,feature2" --only-show-crates="maybe_crate1,maybe_crate2"
```
75 changes: 75 additions & 0 deletions codegen/crates/crate_feature_graph/bin/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use clap::Parser;
use crate_feature_graph::{CrateName, Workspace, WorkspaceGraph};

#[derive(Parser)]
struct Args {
/// Path to the Cargo.toml of the workspace to analyze
#[clap(long, short, default_value = "./Cargo.toml")]
manifest_path: String,

/// Comma-separated list of feature names to enable for the given manifest
#[clap(
short,
long,
default_value = "",
use_value_delimiter = true,
value_delimiter = ','
)]
features: Vec<String>,

/// Whether to disable the default features of the given manifest
/// Defaults to true
#[clap(long, default_value = "false")]
disable_default_features: bool,

/// Only show crates in the output graph that match one of these names
/// Only works for dot graphs
/// Defaults to empty, which shows all crates
#[clap(long, use_value_delimiter = true, value_delimiter = ',')]
only_show_crates: Vec<String>,

/// The package to consider as the root of the search
/// The default will use the root of the workspace
/// The features provided will be applied to this package, unless a crate prefix is provided
#[clap(long)]
root_package: Option<String>,
}

pub fn main() -> std::io::Result<()> {
pretty_env_logger::init();
let args = Args::parse();

let metadata = cargo_metadata::MetadataCommand::new()
.manifest_path(&args.manifest_path)
.exec()
.expect("Failed to get cargo metadata");

let workspace = Workspace::from(&metadata);
let mut graph = WorkspaceGraph::from(workspace);

let root = args.root_package;
log::info!(
"Calculating features for root: {:?} with features: {:?}, default features: {}",
root,
args.features,
!args.disable_default_features
);

// TODO: allow focusing on a non-workspace root, i.e. package
graph
.calculate_enabled_features_and_dependencies_parse(args.features, root.map(CrateName::new));

#[cfg(feature = "dot_parser")]
let dot_graph = graph.visualise_feature_flow(args.only_show_crates)?;

#[cfg(not(feature = "dot_parser"))]
let dot_graph = {
log::warn!("Feature `dot_parser` not enabled, dumping debug print instead");
format!("{:?}", graph.workspace)
};

// dump the graph
println!("{dot_graph}");

Ok(())
}
Binary file added codegen/crates/crate_feature_graph/example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading