123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- use clap::Parser;
- use std::path::PathBuf;
- use wasm_split_cli::SplitModule;
- fn main() {
- tracing_subscriber::fmt()
- .without_time()
- .compact()
- .with_env_filter("debug,walrus=info")
- .init();
- match Commands::parse() {
- Commands::Split(split_args) => split(split_args),
- Commands::Validate(validate_args) => validate(validate_args),
- }
- }
- #[derive(Parser)]
- enum Commands {
- /// Split a wasm module into multiple chunks
- #[clap(name = "split")]
- Split(SplitArgs),
- /// Validate the main module of a wasm module
- #[clap(name = "validate")]
- Validate(ValidateArgs),
- }
- #[derive(Parser)]
- struct SplitArgs {
- /// The wasm module emitted by rustc
- original: PathBuf,
- /// The wasm module emitted by wasm-bindgen
- bindgened: PathBuf,
- /// The output *directory* to write the split wasm files to
- out_dir: PathBuf,
- }
- fn split(args: SplitArgs) {
- let original = std::fs::read(&args.original).expect("failed to read input file");
- let bindgened = std::fs::read(&args.bindgened).expect("failed to read input file");
- _ = std::fs::remove_dir_all(&args.out_dir);
- std::fs::create_dir_all(&args.out_dir).expect("failed to create output dir");
- tracing::info!("Building split module");
- let module = wasm_split_cli::Splitter::new(&original, &bindgened).unwrap();
- let mut chunks = module.emit().unwrap();
- // Write out the main module
- tracing::info!(
- "Writing main module to {}",
- args.out_dir.join("main.wasm").display()
- );
- std::fs::write(args.out_dir.join("main.wasm"), &chunks.main.bytes).unwrap();
- // Write the js module
- std::fs::write(
- args.out_dir.join("__wasm_split.js"),
- emit_js(&chunks.chunks, &chunks.modules),
- )
- .expect("failed to write js module");
- for (idx, chunk) in chunks.chunks.iter().enumerate() {
- tracing::info!(
- "Writing chunk {} to {}",
- idx,
- args.out_dir
- .join(format!("chunk_{}_{}.wasm", idx, chunk.module_name))
- .display()
- );
- std::fs::write(
- args.out_dir
- .join(format!("chunk_{}_{}.wasm", idx, chunk.module_name)),
- &chunk.bytes,
- )
- .expect("failed to write chunk");
- }
- for (idx, module) in chunks.modules.iter_mut().enumerate() {
- tracing::info!(
- "Writing module {} to {}",
- idx,
- args.out_dir
- .join(format!(
- "module_{}_{}.wasm",
- idx,
- module.component_name.as_ref().unwrap()
- ))
- .display()
- );
- std::fs::write(
- args.out_dir.join(format!(
- "module_{}_{}.wasm",
- idx,
- module.component_name.as_ref().unwrap()
- )),
- &module.bytes,
- )
- .expect("failed to write chunk");
- }
- }
- fn emit_js(chunks: &[SplitModule], modules: &[SplitModule]) -> String {
- use std::fmt::Write;
- let mut glue = format!(
- r#"import {{ initSync }} from "./main.js";
- {}"#,
- include_str!("./__wasm_split.js")
- );
- for (idx, chunk) in chunks.iter().enumerate() {
- tracing::debug!("emitting chunk: {:?}", chunk.module_name);
- writeln!(
- glue,
- "export const __wasm_split_load_chunk_{idx} = makeLoad(\"/harness/split/chunk_{idx}_{module}.wasm\", [], fusedImports, initSync);",
- module = chunk.module_name
- ).expect("failed to write to string");
- }
- // Now write the modules
- for (idx, module) in modules.iter().enumerate() {
- let deps = module
- .relies_on_chunks
- .iter()
- .map(|idx| format!("__wasm_split_load_chunk_{idx}"))
- .collect::<Vec<_>>()
- .join(", ");
- let hash_id = module.hash_id.as_ref().unwrap();
- writeln!(
- glue,
- "export const __wasm_split_load_{module}_{hash_id}_{cname} = makeLoad(\"/harness/split/module_{idx}_{cname}.wasm\", [{deps}], fusedImports, initSync);",
- module = module.module_name,
- idx = idx,
- cname = module.component_name.as_ref().unwrap(),
- deps = deps
- )
- .expect("failed to write to string");
- }
- glue
- }
- #[derive(Parser)]
- struct ValidateArgs {
- /// The input wasm file to validate
- main: PathBuf,
- chunks: Vec<PathBuf>,
- }
- fn validate(args: ValidateArgs) {
- let bytes = std::fs::read(&args.main).expect("failed to read input file");
- let main_module = walrus::Module::from_buffer(&bytes).unwrap();
- for chunk in args.chunks {
- let bytes = std::fs::read(chunk).expect("failed to read input file");
- let chunk_module = walrus::Module::from_buffer(&bytes).unwrap();
- assert!(chunk_module.tables.iter().count() == 1);
- for import in chunk_module.imports.iter() {
- let matching = main_module.exports.iter().find(|e| e.name == import.name);
- let Some(matching) = matching else {
- tracing::error!("Could not find matching export for import {import:#?}");
- continue;
- };
- tracing::debug!("import: {:?}", matching.name);
- }
- }
- }
|