1
0

main.rs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. use clap::Parser;
  2. use std::path::PathBuf;
  3. use wasm_split_cli::SplitModule;
  4. fn main() {
  5. tracing_subscriber::fmt()
  6. .without_time()
  7. .compact()
  8. .with_env_filter("debug,walrus=info")
  9. .init();
  10. match Commands::parse() {
  11. Commands::Split(split_args) => split(split_args),
  12. Commands::Validate(validate_args) => validate(validate_args),
  13. }
  14. }
  15. #[derive(Parser)]
  16. enum Commands {
  17. /// Split a wasm module into multiple chunks
  18. #[clap(name = "split")]
  19. Split(SplitArgs),
  20. /// Validate the main module of a wasm module
  21. #[clap(name = "validate")]
  22. Validate(ValidateArgs),
  23. }
  24. #[derive(Parser)]
  25. struct SplitArgs {
  26. /// The wasm module emitted by rustc
  27. original: PathBuf,
  28. /// The wasm module emitted by wasm-bindgen
  29. bindgened: PathBuf,
  30. /// The output *directory* to write the split wasm files to
  31. out_dir: PathBuf,
  32. }
  33. fn split(args: SplitArgs) {
  34. let original = std::fs::read(&args.original).expect("failed to read input file");
  35. let bindgened = std::fs::read(&args.bindgened).expect("failed to read input file");
  36. _ = std::fs::remove_dir_all(&args.out_dir);
  37. std::fs::create_dir_all(&args.out_dir).expect("failed to create output dir");
  38. tracing::info!("Building split module");
  39. let module = wasm_split_cli::Splitter::new(&original, &bindgened).unwrap();
  40. let mut chunks = module.emit().unwrap();
  41. // Write out the main module
  42. tracing::info!(
  43. "Writing main module to {}",
  44. args.out_dir.join("main.wasm").display()
  45. );
  46. std::fs::write(args.out_dir.join("main.wasm"), &chunks.main.bytes).unwrap();
  47. // Write the js module
  48. std::fs::write(
  49. args.out_dir.join("__wasm_split.js"),
  50. emit_js(&chunks.chunks, &chunks.modules),
  51. )
  52. .expect("failed to write js module");
  53. for (idx, chunk) in chunks.chunks.iter().enumerate() {
  54. tracing::info!(
  55. "Writing chunk {} to {}",
  56. idx,
  57. args.out_dir
  58. .join(format!("chunk_{}_{}.wasm", idx, chunk.module_name))
  59. .display()
  60. );
  61. std::fs::write(
  62. args.out_dir
  63. .join(format!("chunk_{}_{}.wasm", idx, chunk.module_name)),
  64. &chunk.bytes,
  65. )
  66. .expect("failed to write chunk");
  67. }
  68. for (idx, module) in chunks.modules.iter_mut().enumerate() {
  69. tracing::info!(
  70. "Writing module {} to {}",
  71. idx,
  72. args.out_dir
  73. .join(format!(
  74. "module_{}_{}.wasm",
  75. idx,
  76. module.component_name.as_ref().unwrap()
  77. ))
  78. .display()
  79. );
  80. std::fs::write(
  81. args.out_dir.join(format!(
  82. "module_{}_{}.wasm",
  83. idx,
  84. module.component_name.as_ref().unwrap()
  85. )),
  86. &module.bytes,
  87. )
  88. .expect("failed to write chunk");
  89. }
  90. }
  91. fn emit_js(chunks: &[SplitModule], modules: &[SplitModule]) -> String {
  92. use std::fmt::Write;
  93. let mut glue = format!(
  94. r#"import {{ initSync }} from "./main.js";
  95. {}"#,
  96. include_str!("./__wasm_split.js")
  97. );
  98. for (idx, chunk) in chunks.iter().enumerate() {
  99. tracing::debug!("emitting chunk: {:?}", chunk.module_name);
  100. writeln!(
  101. glue,
  102. "export const __wasm_split_load_chunk_{idx} = makeLoad(\"/harness/split/chunk_{idx}_{module}.wasm\", [], fusedImports, initSync);",
  103. module = chunk.module_name
  104. ).expect("failed to write to string");
  105. }
  106. // Now write the modules
  107. for (idx, module) in modules.iter().enumerate() {
  108. let deps = module
  109. .relies_on_chunks
  110. .iter()
  111. .map(|idx| format!("__wasm_split_load_chunk_{idx}"))
  112. .collect::<Vec<_>>()
  113. .join(", ");
  114. let hash_id = module.hash_id.as_ref().unwrap();
  115. writeln!(
  116. glue,
  117. "export const __wasm_split_load_{module}_{hash_id}_{cname} = makeLoad(\"/harness/split/module_{idx}_{cname}.wasm\", [{deps}], fusedImports, initSync);",
  118. module = module.module_name,
  119. idx = idx,
  120. cname = module.component_name.as_ref().unwrap(),
  121. deps = deps
  122. )
  123. .expect("failed to write to string");
  124. }
  125. glue
  126. }
  127. #[derive(Parser)]
  128. struct ValidateArgs {
  129. /// The input wasm file to validate
  130. main: PathBuf,
  131. chunks: Vec<PathBuf>,
  132. }
  133. fn validate(args: ValidateArgs) {
  134. let bytes = std::fs::read(&args.main).expect("failed to read input file");
  135. let main_module = walrus::Module::from_buffer(&bytes).unwrap();
  136. for chunk in args.chunks {
  137. let bytes = std::fs::read(chunk).expect("failed to read input file");
  138. let chunk_module = walrus::Module::from_buffer(&bytes).unwrap();
  139. assert!(chunk_module.tables.iter().count() == 1);
  140. for import in chunk_module.imports.iter() {
  141. let matching = main_module.exports.iter().find(|e| e.name == import.name);
  142. let Some(matching) = matching else {
  143. tracing::error!("Could not find matching export for import {import:#?}");
  144. continue;
  145. };
  146. tracing::debug!("import: {:?}", matching.name);
  147. }
  148. }
  149. }