Skip to content

Conversation

@osiewicz
Copy link
Contributor

Co-authored-by: dino [email protected]

What does this PR try to resolve?

This commit optimizes implementation of cargo clean -p by reducing the amount of directory walks that take place.
We now batch calls to rm_rf_prefix_list, thus potentially avoiding multiple walks over a single subdirectory. In practice this helps us significantly reduce the runtime for clearing large workspaces (as implemented in #16263); for Zed, cargo clean --workspace went down from 73 seconds to 3 seconds.

We have 216 workspace members.

How to test and review this PR?

We've tested it by hand, running it against regex, ruff and zed codebases.

This PR is still marked as draft, as I don't love the code. I would also understand if y'all were against merging this, given that new build directory layout is in flight.

@rustbot rustbot added A-cfg-expr Area: Platform cfg expressions Command-clean labels Nov 14, 2025
@epage
Copy link
Contributor

epage commented Nov 14, 2025

Have you tried out the performance of cargo clean --workspace with -Zbuild-dir-new-layout? Unsure what the timeline will be for stabilizing that so I can understand if you want to optimize this in the mean time.

@epage
Copy link
Contributor

epage commented Nov 14, 2025

Are we really benefiting from all of this filesystem globbing?

Should we just walk all of the content upfront, get it into a Vec, and then do a retain call on that, pattern matching against the files for the different packages, and then delete what is left?

@osiewicz
Copy link
Contributor Author

osiewicz commented Nov 14, 2025

Have you tried out the performance of cargo clean --workspace with -Zbuild-dir-new-layout?

No, I did not try -Zbuild-dir-new-layout. I'll do that and report back.

Are we really benefiting from all of this filesystem globbing?

I don't think we benefit much from globbing, particularly because most of it's use cases are about a simple prefix/suffix matching. I'm in the process of coalescing file walking further. I'll push that up to this branch before marking this PR as ready to review.

Should we just walk all of the content upfront, get it into a Vec, and then do a retain call on that, pattern matching against the files for the different packages, and then delete what is left?

If we could make the pattern matching part straightforward (to read/maintain) then sure, that'd be best. I'm clinging onto the existing setup for no good reason; the end goal for me is to have a faster cargo clean -p --workspace.

@osiewicz
Copy link
Contributor Author

I am very happy to report that -Zbuild-dir-new-layout would make cargo clean --workspace take about a second on Zed codebase. :)

This PR is currently at 2.6s. I think we could reach a similar ballpark with the current build dir layout.. But obviously, this code will be removed at some point either way.

@rustbot

This comment has been minimized.

@rustbot rustbot added the S-waiting-on-author Status: The marked PR is awaiting some action (such as code changes) from the PR author. label Nov 25, 2025
github-merge-queue bot pushed a commit that referenced this pull request Nov 26, 2025
### What does this PR try to resolve?

When we have limited content do remove, we were cleaning in an inner
loop and left off the host layout.

### How to test and review this PR?

This is a follow up to #15947 that I noticed when looking at #16264.
@osiewicz osiewicz force-pushed the clean-perf branch 3 times, most recently from d4a8b8d to 0afdf97 Compare November 28, 2025 14:57
github-merge-queue bot pushed a commit that referenced this pull request Nov 28, 2025
### What does this PR try to resolve?

A follow up to #16300.

This doesn't split out any functions
- The stuff before hand is quite significant
- For #16264, we made add more before and some after
- We could pull out only the branches of the `if` into functions but
then we'd just move them back when this is done and seems like extra
churn

### How to test and review this PR?
@rustbot

This comment has been minimized.

@osiewicz osiewicz marked this pull request as ready for review December 2, 2025 23:40
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Dec 2, 2025
@rustbot
Copy link
Collaborator

rustbot commented Dec 2, 2025

r? @ehuss

rustbot has assigned @ehuss.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@osiewicz
Copy link
Contributor Author

osiewicz commented Dec 2, 2025

I've rebased the existing implementation on top of #16304 - while we could push the perf further, I think this is good enough (after all, it gets us from 70s down to 3s) for our use case.

@osiewicz osiewicz changed the title clean: Optimize clean with multiple -p specifiers clean: Optimize (legacy) clean with multiple -p specifiers Dec 3, 2025
@osiewicz
Copy link
Contributor Author

osiewicz commented Dec 3, 2025

r? epage

@rustbot rustbot assigned epage and unassigned ehuss Dec 3, 2025
@epage
Copy link
Contributor

epage commented Dec 3, 2025

I don't think we benefit much from globbing, particularly because most of it's use cases are about a simple prefix/suffix matching. I'm in the process of coalescing file walking further. I'll push that up to this branch before marking this PR as ready to review.

In my mind, we would do file walking by

let mut cache: HashMap<PathBuf, Vec<OsString>> = Default::default();
let mut to_remove: Vec<PathBuf> = Default::default();
for ... {

    //...
    let file_names = cache.entry(dir).or_insert_with(|| std::fs::read_dir(dir));
    file_names.retain(|name| {
         let remove name.starts_with(prefix) && name.ends_with(suffix);
         if remove {
             to_remove.push(dir.join(name));
         }
         !remove
    });
}

for path in to_remove {
    self.rm_rf(path)?;
}

(psuedo code, no preference on the exact details including whether cache tracks file names or paths, likely it should be wrapped in a struct for easier use)

Benefits

  • Could then apply to the new layout as well. While the new layout benefits is already fast, we've been fixing bugs where we've been doing more globbing (including one still pending, fix(layout): Put examples in their unit dir in new layout #16335).
  • More flexibility for deciding what gets removed rather than having to fit within any specific rule system of prefix+suffix, globs, etc.
  • Guaranteed that each directory is only read once

{
let paths = [
dirs_to_clean.mark_utf(dir, |path| {
if path.starts_with(&path_dash) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we do the rsplit_once check earlier but not here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Earlier call sites were using rm_rf_package_glob_containing_dash, whereas this one used to call rm_rf_prefix_list. We could probably use strip_prefix(crate_name) here followed by a dispatch on the following character.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eh, lets leave it for now. In a follow up, we should probably extract a starts_with_pkg_name(name, sep) so we consistently rsplit_once.

@osiewicz osiewicz force-pushed the clean-perf branch 3 times, most recently from f6cf214 to 43b04a2 Compare December 4, 2025 21:54
@epage epage changed the title clean: Optimize (legacy) clean with multiple -p specifiers perf(clean): Optimize (legacy) clean with multiple -p specifiers Dec 4, 2025
This commit optimizes implementation of `cargo clean -p` by reducing the amount of directory walks that take place.
We now walk each directory at most once and add to the list of files to be cleaned, step by step. In practice this helps us significantly reduce the runtime for clearing large workspaces (as implemented in rust-lang#16263); for Zed, `cargo clean --workspace` went down from 73 seconds to 3 seconds.
We have 216 workspace members.

Co-authored-by: dino <[email protected]>
Co-authored-by: Ed Page <[email protected]>
Copy link
Contributor

@epage epage left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@epage epage enabled auto-merge December 4, 2025 22:05
@epage epage added this pull request to the merge queue Dec 4, 2025
Merged via the queue into rust-lang:master with commit 90db5c5 Dec 4, 2025
28 checks passed
@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Dec 4, 2025
bors added a commit to rust-lang/rust that referenced this pull request Dec 13, 2025
Update cargo submodule

27 commits in 2c283a9a5c5968eeb9a8f12313f04feb1ff8dfac..c46423de7351e3c4c734b2faa86088a9f5d1302b
2025-12-04 16:47:28 +0000 to 2025-12-12 23:16:12 +0000
- feat(report): cargo report timings HTML replay (rust-lang/cargo#16377)
- feat: stabilize `-Zconfig-include` (rust-lang/cargo#16284)
- fix(package): Don't verify registry for --list  (rust-lang/cargo#16341)
- Fixed incorrect locking logic when artifact-dir == build-dir (rust-lang/cargo#16385)
- feat(log): make timing messages ready for HTML replay (rust-lang/cargo#16382)
- chore(deps): update msrv (1 version) to v1.92 (rust-lang/cargo#16381)
- Downgrade curl-sys to 0.4.83 (rust-lang/cargo#16379)
- fix(timing): more self-contained timing/log data (rust-lang/cargo#16378)
- test: update to `proc_macro::tracked::path` (rust-lang/cargo#16380)
- refactor(lint): move lints to separate modules (rust-lang/cargo#16364)
- fix(index): Apply feedback from Cargo team (rust-lang/cargo#16369)
- fix(lints): handle lints separately at ws pkg level  (rust-lang/cargo#16367)
- feat(lint): new `implicit_minimum_version_req` lint (rust-lang/cargo#16321)
- fix(info): default to local without explicit reg (rust-lang/cargo#16358)
- Remove `[no-mentions]` handler in our triagebot config (rust-lang/cargo#16361)
- Don't read the config file twice when $CARGO_HOME is a symlink (rust-lang/cargo#16325)
- fix(timings): forgot to negate filter (rust-lang/cargo#16352)
- fix(doctest): Include all search paths with new build layout  (rust-lang/cargo#16348)
- fix(layout): Remove hashes from bins in new layout  (rust-lang/cargo#16351)
- docs(faq): Include an entry on disk space (rust-lang/cargo#16349)
- feat(timings): derive concurrency data from unit data (rust-lang/cargo#16350)
- perf(layout): Use unit_id, not pkg hash, for bin/lib pkg_dirs for new layout (rust-lang/cargo#16345)
- Validate target source paths before compilation with clearer errors (rust-lang/cargo#16338)
- test(doc): Remove unused build script (rust-lang/cargo#16344)
- refactor(timings): store UnitData in RenderContext instead  (rust-lang/cargo#16346)
- perf(clean): Optimize (legacy) clean with multiple -p specifiers (rust-lang/cargo#16264)
- test: Adjust output for out-of-tree build-dir (rust-lang/cargo#16343)
bors added a commit to rust-lang/rust that referenced this pull request Dec 13, 2025
Update cargo submodule

29 commits in 2c283a9a5c5968eeb9a8f12313f04feb1ff8dfac..e91b2baa632c0c7e84216c91ecfe107c37d887c1
2025-12-04 16:47:28 +0000 to 2025-12-13 16:29:21 +0000
- refactor(lints): move from cargo::util::lints to cargo::lints (rust-lang/cargo#16392)
- test(lint): redact more due to line got omitted (rust-lang/cargo#16391)
- feat(report): cargo report timings HTML replay (rust-lang/cargo#16377)
- feat: stabilize `-Zconfig-include` (rust-lang/cargo#16284)
- fix(package): Don't verify registry for --list  (rust-lang/cargo#16341)
- Fixed incorrect locking logic when artifact-dir == build-dir (rust-lang/cargo#16385)
- feat(log): make timing messages ready for HTML replay (rust-lang/cargo#16382)
- chore(deps): update msrv (1 version) to v1.92 (rust-lang/cargo#16381)
- Downgrade curl-sys to 0.4.83 (rust-lang/cargo#16379)
- fix(timing): more self-contained timing/log data (rust-lang/cargo#16378)
- test: update to `proc_macro::tracked::path` (rust-lang/cargo#16380)
- refactor(lint): move lints to separate modules (rust-lang/cargo#16364)
- fix(index): Apply feedback from Cargo team (rust-lang/cargo#16369)
- fix(lints): handle lints separately at ws pkg level  (rust-lang/cargo#16367)
- feat(lint): new `implicit_minimum_version_req` lint (rust-lang/cargo#16321)
- fix(info): default to local without explicit reg (rust-lang/cargo#16358)
- Remove `[no-mentions]` handler in our triagebot config (rust-lang/cargo#16361)
- Don't read the config file twice when $CARGO_HOME is a symlink (rust-lang/cargo#16325)
- fix(timings): forgot to negate filter (rust-lang/cargo#16352)
- fix(doctest): Include all search paths with new build layout  (rust-lang/cargo#16348)
- fix(layout): Remove hashes from bins in new layout  (rust-lang/cargo#16351)
- docs(faq): Include an entry on disk space (rust-lang/cargo#16349)
- feat(timings): derive concurrency data from unit data (rust-lang/cargo#16350)
- perf(layout): Use unit_id, not pkg hash, for bin/lib pkg_dirs for new layout (rust-lang/cargo#16345)
- Validate target source paths before compilation with clearer errors (rust-lang/cargo#16338)
- test(doc): Remove unused build script (rust-lang/cargo#16344)
- refactor(timings): store UnitData in RenderContext instead  (rust-lang/cargo#16346)
- perf(clean): Optimize (legacy) clean with multiple -p specifiers (rust-lang/cargo#16264)
- test: Adjust output for out-of-tree build-dir (rust-lang/cargo#16343)
@rustbot rustbot added this to the 1.94.0 milestone Dec 13, 2025
github-actions bot pushed a commit to rust-lang/miri that referenced this pull request Dec 14, 2025
Update cargo submodule

29 commits in 2c283a9a5c5968eeb9a8f12313f04feb1ff8dfac..e91b2baa632c0c7e84216c91ecfe107c37d887c1
2025-12-04 16:47:28 +0000 to 2025-12-13 16:29:21 +0000
- refactor(lints): move from cargo::util::lints to cargo::lints (rust-lang/cargo#16392)
- test(lint): redact more due to line got omitted (rust-lang/cargo#16391)
- feat(report): cargo report timings HTML replay (rust-lang/cargo#16377)
- feat: stabilize `-Zconfig-include` (rust-lang/cargo#16284)
- fix(package): Don't verify registry for --list  (rust-lang/cargo#16341)
- Fixed incorrect locking logic when artifact-dir == build-dir (rust-lang/cargo#16385)
- feat(log): make timing messages ready for HTML replay (rust-lang/cargo#16382)
- chore(deps): update msrv (1 version) to v1.92 (rust-lang/cargo#16381)
- Downgrade curl-sys to 0.4.83 (rust-lang/cargo#16379)
- fix(timing): more self-contained timing/log data (rust-lang/cargo#16378)
- test: update to `proc_macro::tracked::path` (rust-lang/cargo#16380)
- refactor(lint): move lints to separate modules (rust-lang/cargo#16364)
- fix(index): Apply feedback from Cargo team (rust-lang/cargo#16369)
- fix(lints): handle lints separately at ws pkg level  (rust-lang/cargo#16367)
- feat(lint): new `implicit_minimum_version_req` lint (rust-lang/cargo#16321)
- fix(info): default to local without explicit reg (rust-lang/cargo#16358)
- Remove `[no-mentions]` handler in our triagebot config (rust-lang/cargo#16361)
- Don't read the config file twice when $CARGO_HOME is a symlink (rust-lang/cargo#16325)
- fix(timings): forgot to negate filter (rust-lang/cargo#16352)
- fix(doctest): Include all search paths with new build layout  (rust-lang/cargo#16348)
- fix(layout): Remove hashes from bins in new layout  (rust-lang/cargo#16351)
- docs(faq): Include an entry on disk space (rust-lang/cargo#16349)
- feat(timings): derive concurrency data from unit data (rust-lang/cargo#16350)
- perf(layout): Use unit_id, not pkg hash, for bin/lib pkg_dirs for new layout (rust-lang/cargo#16345)
- Validate target source paths before compilation with clearer errors (rust-lang/cargo#16338)
- test(doc): Remove unused build script (rust-lang/cargo#16344)
- refactor(timings): store UnitData in RenderContext instead  (rust-lang/cargo#16346)
- perf(clean): Optimize (legacy) clean with multiple -p specifiers (rust-lang/cargo#16264)
- test: Adjust output for out-of-tree build-dir (rust-lang/cargo#16343)
github-actions bot pushed a commit to rust-lang/rust-analyzer that referenced this pull request Dec 15, 2025
Update cargo submodule

29 commits in 2c283a9a5c5968eeb9a8f12313f04feb1ff8dfac..e91b2baa632c0c7e84216c91ecfe107c37d887c1
2025-12-04 16:47:28 +0000 to 2025-12-13 16:29:21 +0000
- refactor(lints): move from cargo::util::lints to cargo::lints (rust-lang/cargo#16392)
- test(lint): redact more due to line got omitted (rust-lang/cargo#16391)
- feat(report): cargo report timings HTML replay (rust-lang/cargo#16377)
- feat: stabilize `-Zconfig-include` (rust-lang/cargo#16284)
- fix(package): Don't verify registry for --list  (rust-lang/cargo#16341)
- Fixed incorrect locking logic when artifact-dir == build-dir (rust-lang/cargo#16385)
- feat(log): make timing messages ready for HTML replay (rust-lang/cargo#16382)
- chore(deps): update msrv (1 version) to v1.92 (rust-lang/cargo#16381)
- Downgrade curl-sys to 0.4.83 (rust-lang/cargo#16379)
- fix(timing): more self-contained timing/log data (rust-lang/cargo#16378)
- test: update to `proc_macro::tracked::path` (rust-lang/cargo#16380)
- refactor(lint): move lints to separate modules (rust-lang/cargo#16364)
- fix(index): Apply feedback from Cargo team (rust-lang/cargo#16369)
- fix(lints): handle lints separately at ws pkg level  (rust-lang/cargo#16367)
- feat(lint): new `implicit_minimum_version_req` lint (rust-lang/cargo#16321)
- fix(info): default to local without explicit reg (rust-lang/cargo#16358)
- Remove `[no-mentions]` handler in our triagebot config (rust-lang/cargo#16361)
- Don't read the config file twice when $CARGO_HOME is a symlink (rust-lang/cargo#16325)
- fix(timings): forgot to negate filter (rust-lang/cargo#16352)
- fix(doctest): Include all search paths with new build layout  (rust-lang/cargo#16348)
- fix(layout): Remove hashes from bins in new layout  (rust-lang/cargo#16351)
- docs(faq): Include an entry on disk space (rust-lang/cargo#16349)
- feat(timings): derive concurrency data from unit data (rust-lang/cargo#16350)
- perf(layout): Use unit_id, not pkg hash, for bin/lib pkg_dirs for new layout (rust-lang/cargo#16345)
- Validate target source paths before compilation with clearer errors (rust-lang/cargo#16338)
- test(doc): Remove unused build script (rust-lang/cargo#16344)
- refactor(timings): store UnitData in RenderContext instead  (rust-lang/cargo#16346)
- perf(clean): Optimize (legacy) clean with multiple -p specifiers (rust-lang/cargo#16264)
- test: Adjust output for out-of-tree build-dir (rust-lang/cargo#16343)
Kobzol pushed a commit to Kobzol/rustc_codegen_gcc that referenced this pull request Dec 21, 2025
Update cargo submodule

29 commits in 2c283a9a5c5968eeb9a8f12313f04feb1ff8dfac..e91b2baa632c0c7e84216c91ecfe107c37d887c1
2025-12-04 16:47:28 +0000 to 2025-12-13 16:29:21 +0000
- refactor(lints): move from cargo::util::lints to cargo::lints (rust-lang/cargo#16392)
- test(lint): redact more due to line got omitted (rust-lang/cargo#16391)
- feat(report): cargo report timings HTML replay (rust-lang/cargo#16377)
- feat: stabilize `-Zconfig-include` (rust-lang/cargo#16284)
- fix(package): Don't verify registry for --list  (rust-lang/cargo#16341)
- Fixed incorrect locking logic when artifact-dir == build-dir (rust-lang/cargo#16385)
- feat(log): make timing messages ready for HTML replay (rust-lang/cargo#16382)
- chore(deps): update msrv (1 version) to v1.92 (rust-lang/cargo#16381)
- Downgrade curl-sys to 0.4.83 (rust-lang/cargo#16379)
- fix(timing): more self-contained timing/log data (rust-lang/cargo#16378)
- test: update to `proc_macro::tracked::path` (rust-lang/cargo#16380)
- refactor(lint): move lints to separate modules (rust-lang/cargo#16364)
- fix(index): Apply feedback from Cargo team (rust-lang/cargo#16369)
- fix(lints): handle lints separately at ws pkg level  (rust-lang/cargo#16367)
- feat(lint): new `implicit_minimum_version_req` lint (rust-lang/cargo#16321)
- fix(info): default to local without explicit reg (rust-lang/cargo#16358)
- Remove `[no-mentions]` handler in our triagebot config (rust-lang/cargo#16361)
- Don't read the config file twice when $CARGO_HOME is a symlink (rust-lang/cargo#16325)
- fix(timings): forgot to negate filter (rust-lang/cargo#16352)
- fix(doctest): Include all search paths with new build layout  (rust-lang/cargo#16348)
- fix(layout): Remove hashes from bins in new layout  (rust-lang/cargo#16351)
- docs(faq): Include an entry on disk space (rust-lang/cargo#16349)
- feat(timings): derive concurrency data from unit data (rust-lang/cargo#16350)
- perf(layout): Use unit_id, not pkg hash, for bin/lib pkg_dirs for new layout (rust-lang/cargo#16345)
- Validate target source paths before compilation with clearer errors (rust-lang/cargo#16338)
- test(doc): Remove unused build script (rust-lang/cargo#16344)
- refactor(timings): store UnitData in RenderContext instead  (rust-lang/cargo#16346)
- perf(clean): Optimize (legacy) clean with multiple -p specifiers (rust-lang/cargo#16264)
- test: Adjust output for out-of-tree build-dir (rust-lang/cargo#16343)
Kobzol pushed a commit to Kobzol/rustc_codegen_cranelift that referenced this pull request Dec 29, 2025
Update cargo submodule

29 commits in 2c283a9a5c5968eeb9a8f12313f04feb1ff8dfac..e91b2baa632c0c7e84216c91ecfe107c37d887c1
2025-12-04 16:47:28 +0000 to 2025-12-13 16:29:21 +0000
- refactor(lints): move from cargo::util::lints to cargo::lints (rust-lang/cargo#16392)
- test(lint): redact more due to line got omitted (rust-lang/cargo#16391)
- feat(report): cargo report timings HTML replay (rust-lang/cargo#16377)
- feat: stabilize `-Zconfig-include` (rust-lang/cargo#16284)
- fix(package): Don't verify registry for --list  (rust-lang/cargo#16341)
- Fixed incorrect locking logic when artifact-dir == build-dir (rust-lang/cargo#16385)
- feat(log): make timing messages ready for HTML replay (rust-lang/cargo#16382)
- chore(deps): update msrv (1 version) to v1.92 (rust-lang/cargo#16381)
- Downgrade curl-sys to 0.4.83 (rust-lang/cargo#16379)
- fix(timing): more self-contained timing/log data (rust-lang/cargo#16378)
- test: update to `proc_macro::tracked::path` (rust-lang/cargo#16380)
- refactor(lint): move lints to separate modules (rust-lang/cargo#16364)
- fix(index): Apply feedback from Cargo team (rust-lang/cargo#16369)
- fix(lints): handle lints separately at ws pkg level  (rust-lang/cargo#16367)
- feat(lint): new `implicit_minimum_version_req` lint (rust-lang/cargo#16321)
- fix(info): default to local without explicit reg (rust-lang/cargo#16358)
- Remove `[no-mentions]` handler in our triagebot config (rust-lang/cargo#16361)
- Don't read the config file twice when $CARGO_HOME is a symlink (rust-lang/cargo#16325)
- fix(timings): forgot to negate filter (rust-lang/cargo#16352)
- fix(doctest): Include all search paths with new build layout  (rust-lang/cargo#16348)
- fix(layout): Remove hashes from bins in new layout  (rust-lang/cargo#16351)
- docs(faq): Include an entry on disk space (rust-lang/cargo#16349)
- feat(timings): derive concurrency data from unit data (rust-lang/cargo#16350)
- perf(layout): Use unit_id, not pkg hash, for bin/lib pkg_dirs for new layout (rust-lang/cargo#16345)
- Validate target source paths before compilation with clearer errors (rust-lang/cargo#16338)
- test(doc): Remove unused build script (rust-lang/cargo#16344)
- refactor(timings): store UnitData in RenderContext instead  (rust-lang/cargo#16346)
- perf(clean): Optimize (legacy) clean with multiple -p specifiers (rust-lang/cargo#16264)
- test: Adjust output for out-of-tree build-dir (rust-lang/cargo#16343)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-cfg-expr Area: Platform cfg expressions Command-clean

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants