1use std::{
2 env::{current_dir, var_os},
3 path::PathBuf,
4 process,
5};
6
7use anyhow::Result;
8use clap::{Command, arg};
9
10mod nft_bench;
11mod summarize_bench;
12mod visualize_bundler_bench;
13
14use nft_bench::show_result;
15use rustc_hash::{FxHashMap, FxHashSet};
16
17fn cli() -> Command {
18 Command::new("xtask")
19 .about("turbo-tooling cargo tasks")
20 .subcommand_required(true)
21 .arg_required_else_help(true)
22 .allow_external_subcommands(true)
23 .subcommand(
24 Command::new("nft-bench-result")
25 .about("Print node-file-trace benchmark result against @vercel/nft"),
26 )
27 .subcommand(
28 Command::new("upgrade-swc").about("Upgrade all SWC dependencies to the latest version"),
29 )
30 .subcommand(
31 Command::new("summarize-benchmarks")
32 .about(
33 "Normalize all raw data based on similar benchmarks, average data by \
34 system+sha and compute latest by system",
35 )
36 .arg(arg!(<PATH> "the path to the benchmark data directory")),
37 )
38 .subcommand(
39 Command::new("visualize-bundler-benchmarks")
40 .about("Generate visualizations of bundler benchmarks")
41 .long_about(
42 "Generates visualizations of bundler benchmarks. Currently supports:
43 * Scaling: shows how each bundler scales with varying module counts
44
45To generate the summary json file:
46 * Check out this repository at the `benchmark-data` branch. An additional shallow clone or git \
47 worktree is recommended.
48 * Run `cargo xtask summarize-benchmarks path/to/repo/data`
49 * A summary file is generated within the data dir, e.g. \
50 path/to/repo/data/ubuntu-latest-16-core.json
51
52Visualizations generated by this command will appear in a sibling directory to the summary data \
53 file.",
54 )
55 .arg(arg!(<PATH_TO_SUMMARY_JSON> "the path to the benchmark summary json file"))
56 .arg(arg!(--bundlers <BUNDLERS> "comma separated list of bundlers to include in the visualization")),
57 )
58}
59
60fn main() -> Result<()> {
61 let matches = cli().get_matches();
62 match matches.subcommand() {
63 Some(("nft-bench-result", _)) => {
64 show_result();
65 Ok(())
66 }
67 Some(("upgrade-swc", _)) => {
68 let workspace_dir = var_os("CARGO_WORKSPACE_DIR")
69 .map(PathBuf::from)
70 .unwrap_or_else(|| current_dir().unwrap());
71 let cargo_lock_path = workspace_dir.join("../../Cargo.lock");
72 let lock = cargo_lock::Lockfile::load(cargo_lock_path).unwrap();
73 let swc_packages = lock
74 .packages
75 .iter()
76 .filter(|p| {
77 p.name.as_str().starts_with("swc_")
78 || p.name.as_str() == "swc"
79 || p.name.as_str() == "testing"
80 })
81 .collect::<Vec<_>>();
82 let only_swc_set = swc_packages
83 .iter()
84 .map(|p| p.name.as_str())
85 .collect::<FxHashSet<_>>();
86 let packages = lock
87 .packages
88 .iter()
89 .map(|p| (format!("{}@{}", p.name, p.version), p))
90 .collect::<FxHashMap<_, _>>();
91 let mut queue = swc_packages.clone();
92 let mut set = FxHashSet::default();
93 while let Some(package) = queue.pop() {
94 for dep in package.dependencies.iter() {
95 let ident = format!("{}@{}", dep.name, dep.version);
96 let package = *packages.get(&ident).unwrap();
97 if set.insert(ident) {
98 queue.push(package);
99 }
100 }
101 }
102 let status = process::Command::new("cargo")
103 .arg("upgrade")
104 .arg("--workspace")
105 .args(only_swc_set)
106 .current_dir(&workspace_dir)
107 .stdout(process::Stdio::inherit())
108 .stderr(process::Stdio::inherit())
109 .status()
110 .expect("Running cargo upgrade failed");
111 assert!(status.success());
112 let status = process::Command::new("cargo")
113 .arg("update")
114 .args(set.iter().flat_map(|p| ["-p", p]))
115 .current_dir(&workspace_dir)
116 .stdout(process::Stdio::inherit())
117 .stderr(process::Stdio::inherit())
118 .status()
119 .expect("Running cargo update failed");
120 assert!(status.success());
121 Ok(())
122 }
123 Some(("summarize-benchmarks", sub_matches)) => {
124 let path = sub_matches
125 .get_one::<String>("PATH")
126 .expect("PATH is required");
127 let path = PathBuf::from(path);
128 let path = path.canonicalize().unwrap();
129 summarize_bench::process_all(path);
130 Ok(())
131 }
132 Some(("visualize-bundler-benchmarks", sub_matches)) => {
133 let path = sub_matches
134 .get_one::<String>("PATH_TO_SUMMARY_JSON")
135 .expect("PATH_TO_SUMMARY_JSON is required");
136 let bundlers: Option<FxHashSet<&str>> = sub_matches
137 .get_one::<String>("bundlers")
138 .map(|s| s.split(',').collect());
139
140 let path = PathBuf::from(path);
141 let path = path.canonicalize().unwrap();
142 visualize_bundler_bench::generate(path, bundlers)
143 }
144 _ => {
145 anyhow::bail!("Unknown command {:?}", matches.subcommand().map(|c| c.0));
146 }
147 }
148}