From c8390cdbfaccc55aa374222e39cdf9092a1e5ff6 Mon Sep 17 00:00:00 2001
From: Michael Baikov <manpacket@gmail.com>
Date: Sat, 6 Apr 2024 11:22:21 -0400
Subject: [PATCH] Show files produced by --emit foo in json artifact
 notifications

---
 .../rustc_codegen_cranelift/src/driver/aot.rs | 23 ++++++++++
 compiler/rustc_codegen_ssa/src/back/write.rs  | 23 ++++++++++
 compiler/rustc_codegen_ssa/src/lib.rs         | 18 ++++++++
 compiler/rustc_mir_transform/src/dump_mir.rs  |  3 ++
 src/doc/rustc/src/json.md                     |  8 +++-
 .../run-make/notify-all-emit-artifacts/lib.rs | 21 +++++++++
 .../notify-all-emit-artifacts/rmake.rs        | 45 +++++++++++++++++++
 7 files changed, 140 insertions(+), 1 deletion(-)
 create mode 100644 tests/run-make/notify-all-emit-artifacts/lib.rs
 create mode 100644 tests/run-make/notify-all-emit-artifacts/rmake.rs

diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
index e8c96486041b1..aff9448a89c30 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
@@ -287,6 +287,29 @@ fn produce_final_output_artifacts(
         }
     }
 
+    if sess.opts.json_artifact_notifications {
+        if codegen_results.modules.len() == 1 {
+            codegen_results.modules[0].for_each_output(|_path, ty| {
+                if sess.opts.output_types.contains_key(&ty) {
+                    let descr = ty.shorthand();
+                    // for single cgu file is renamed to drop cgu specific suffix
+                    // so we regenerate it the same way
+                    let path = crate_output.path(ty);
+                    sess.dcx().emit_artifact_notification(path.as_path(), descr);
+                }
+            });
+        } else {
+            for module in &codegen_results.modules {
+                module.for_each_output(|path, ty| {
+                    if sess.opts.output_types.contains_key(&ty) {
+                        let descr = ty.shorthand();
+                        sess.dcx().emit_artifact_notification(&path, descr);
+                    }
+                });
+            }
+        }
+    }
+
     // We leave the following files around by default:
     //  - #crate#.o
     //  - #crate#.crate.metadata.o
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index c4f062405bb5c..72030dcbac54f 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -706,6 +706,29 @@ fn produce_final_output_artifacts(
         }
     }
 
+    if sess.opts.json_artifact_notifications {
+        if compiled_modules.modules.len() == 1 {
+            compiled_modules.modules[0].for_each_output(|_path, ty| {
+                if sess.opts.output_types.contains_key(&ty) {
+                    let descr = ty.shorthand();
+                    // for single cgu file is renamed to drop cgu specific suffix
+                    // so we regenerate it the same way
+                    let path = crate_output.path(ty);
+                    sess.dcx().emit_artifact_notification(path.as_path(), descr);
+                }
+            });
+        } else {
+            for module in &compiled_modules.modules {
+                module.for_each_output(|path, ty| {
+                    if sess.opts.output_types.contains_key(&ty) {
+                        let descr = ty.shorthand();
+                        sess.dcx().emit_artifact_notification(&path, descr);
+                    }
+                });
+            }
+        }
+    }
+
     // We leave the following files around by default:
     //  - #crate#.o
     //  - #crate#.crate.metadata.o
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index 80fe7e0bb7865..7e0e5a9e632e7 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -113,6 +113,24 @@ pub struct CompiledModule {
     pub llvm_ir: Option<PathBuf>,  // --emit=llvm-ir, llvm-bc is in bytecode
 }
 
+impl CompiledModule {
+    /// Call `emit` function with every artifact type currently compiled
+    pub fn for_each_output(&self, mut emit: impl FnMut(&Path, OutputType)) {
+        if let Some(path) = self.object.as_deref() {
+            emit(path, OutputType::Object);
+        }
+        if let Some(path) = self.bytecode.as_deref() {
+            emit(path, OutputType::Bitcode);
+        }
+        if let Some(path) = self.llvm_ir.as_deref() {
+            emit(path, OutputType::LlvmAssembly);
+        }
+        if let Some(path) = self.assembly.as_deref() {
+            emit(path, OutputType::Assembly);
+        }
+    }
+}
+
 pub struct CachedModuleCodegen {
     pub name: String,
     pub source: WorkProduct,
diff --git a/compiler/rustc_mir_transform/src/dump_mir.rs b/compiler/rustc_mir_transform/src/dump_mir.rs
index 13841be494cf0..3b71cf02c1a0a 100644
--- a/compiler/rustc_mir_transform/src/dump_mir.rs
+++ b/compiler/rustc_mir_transform/src/dump_mir.rs
@@ -28,6 +28,9 @@ pub fn emit_mir(tcx: TyCtxt<'_>) -> io::Result<()> {
         OutFileName::Real(path) => {
             let mut f = io::BufWriter::new(File::create(&path)?);
             write_mir_pretty(tcx, None, &mut f)?;
+            if tcx.sess.opts.json_artifact_notifications {
+                tcx.dcx().emit_artifact_notification(&path, "mir");
+            }
         }
     }
     Ok(())
diff --git a/src/doc/rustc/src/json.md b/src/doc/rustc/src/json.md
index 32083b2f731d8..c853f34ee036c 100644
--- a/src/doc/rustc/src/json.md
+++ b/src/doc/rustc/src/json.md
@@ -217,7 +217,8 @@ Diagnostics have the following format:
 Artifact notifications are emitted when the [`--json=artifacts`
 flag][option-json] is used. They indicate that a file artifact has been saved
 to disk. More information about emit kinds may be found in the [`--emit`
-flag][option-emit] documentation.
+flag][option-emit] documentation. Notifications can contain more than one file
+for each type, for example when using multiple codegen units.
 
 ```javascript
 {
@@ -229,6 +230,11 @@ flag][option-emit] documentation.
        - "link": The generated crate as specified by the crate-type.
        - "dep-info": The `.d` file with dependency information in a Makefile-like syntax.
        - "metadata": The Rust `.rmeta` file containing metadata about the crate.
+       - "asm": The `.s` file with generated assembly
+       - "llvm-ir": The `.ll` file with generated textual LLVM IR
+       - "llvm-bc": The `.bc` file with generated LLVM bitcode
+       - "mir": The `.mir` file with rustc's mid-level intermediate representation.
+       - "obj": The `.o` file with generated native object code
     */
     "emit": "link"
 }
diff --git a/tests/run-make/notify-all-emit-artifacts/lib.rs b/tests/run-make/notify-all-emit-artifacts/lib.rs
new file mode 100644
index 0000000000000..6ed194204b454
--- /dev/null
+++ b/tests/run-make/notify-all-emit-artifacts/lib.rs
@@ -0,0 +1,21 @@
+fn one() -> usize {
+    1
+}
+
+pub mod a {
+    pub fn two() -> usize {
+        ::one() + ::one()
+    }
+}
+
+pub mod b {
+    pub fn three() -> usize {
+        ::one() + ::a::two()
+    }
+}
+
+#[inline(never)]
+pub fn main() {
+    a::two();
+    b::three();
+}
diff --git a/tests/run-make/notify-all-emit-artifacts/rmake.rs b/tests/run-make/notify-all-emit-artifacts/rmake.rs
new file mode 100644
index 0000000000000..c866d9179f94b
--- /dev/null
+++ b/tests/run-make/notify-all-emit-artifacts/rmake.rs
@@ -0,0 +1,45 @@
+// rust should produce artifact notifications about files it was asked to --emit.
+//
+// It should work in incremental mode both on the first pass where files are generated as well
+// as on subsequent passes where they are taken from the incremental cache
+//
+// See <https://internals.rust-lang.org/t/easier-access-to-files-generated-by-emit-foo/20477>
+extern crate run_make_support;
+
+use run_make_support::{rustc, tmp_dir};
+
+fn main() {
+    let inc_dir = tmp_dir();
+
+    // With single codegen unit files are renamed to match the source file name
+    for _ in 0..=1 {
+        let output = rustc()
+            .input("lib.rs")
+            .emit("obj,asm,llvm-ir,llvm-bc,mir")
+            .codegen_units(1)
+            .json("artifacts")
+            .error_format("json")
+            .incremental(&inc_dir)
+            .run();
+        let stderr = String::from_utf8_lossy(&output.stderr);
+        for file in &["lib.o", "lib.ll", "lib.bc", "lib.s"] {
+            assert!(stderr.contains(file), "No {:?} in {:?}", file, stderr);
+        }
+    }
+
+    // with multiple codegen units files keep codegen unit id part.
+    for _ in 0..=1 {
+        let output = rustc()
+            .input("lib.rs")
+            .emit("obj,asm,llvm-ir,llvm-bc,mir")
+            .codegen_units(2)
+            .json("artifacts")
+            .error_format("json")
+            .incremental(&inc_dir)
+            .run();
+        let stderr = String::from_utf8_lossy(&output.stderr);
+        for file in &["rcgu.o", "rcgu.ll", "rcgu.bc", "rcgu.s"] {
+            assert!(stderr.contains(file), "No {:?} in {:?}", file, stderr);
+        }
+    }
+}