[llvm] 4e22a6e - [llvm-rc] Add /showIncludes dependency reporting (#217102)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 10 00:25:50 PDT 2026
Author: Oren Azad
Date: 2026-09-10T10:25:45+03:00
New Revision: 4e22a6ef80ac0ac234acca473706cc5cce17d6c4
URL: https://github.com/llvm/llvm-project/commit/4e22a6ef80ac0ac234acca473706cc5cce17d6c4
DIFF: https://github.com/llvm/llvm-project/commit/4e22a6ef80ac0ac234acca473706cc5cce17d6c4.diff
LOG: [llvm-rc] Add /showIncludes dependency reporting (#217102)
Add `/showIncludes` support to `llvm-rc` so build systems can discover
the files a resource compile depends on. This enables Ninja and other
build systems to track and correctly recompile the `.rc` file when
inputs change.
Two kinds of dependencies are reported:
- Preprocessor header dependencies, by forwarding `--show-includes` to
the clang preprocessor.
- Resource files, (bitmaps, icons, manifests, etc) that are used in the
resource file.
The output format matches the `/showIncludes` format that cl.exe and
clang-cl.exe use, and that Ninja can consume natively via `deps=msvc`.
Assisted-by: Anthropic Claude Code (Opus 4.8)
Assisted-by: OpenAI Codex (GPT 5.6 Sol)
Added:
llvm/test/tools/llvm-rc/show-includes.test
Modified:
llvm/docs/ReleaseNotes.md
llvm/test/tools/llvm-rc/helpmsg.test
llvm/tools/llvm-rc/Opts.td
llvm/tools/llvm-rc/ResourceFileWriter.cpp
llvm/tools/llvm-rc/ResourceFileWriter.h
llvm/tools/llvm-rc/llvm-rc.cpp
Removed:
################################################################################
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 4718d47bf8c10..739cf86ad54bb 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -278,6 +278,9 @@ Makes programs 10x faster by doing Special New Thing.
* llvm-mca no longer defaults -mcpu to "native"
+* llvm-rc now supports `/showIncludes` to report header and resource-file
+ dependencies in a format compatible with Ninja's `deps = msvc` mode.
+
### Changes to LLDB
* `platform.plugin.wasm.runtime-args` now precede the port argument on the Wasm
diff --git a/llvm/test/tools/llvm-rc/helpmsg.test b/llvm/test/tools/llvm-rc/helpmsg.test
index ecda426e3280a..6230ddb6e4667 100644
--- a/llvm/test/tools/llvm-rc/helpmsg.test
+++ b/llvm/test/tools/llvm-rc/helpmsg.test
@@ -17,6 +17,7 @@
; CHECK-NEXT: /L <value> Set the default language identifier.
; CHECK-NEXT: /no-preprocess Don't try to preprocess the input file.
; CHECK-NEXT: /N Null-terminate all strings in the string table.
+; CHECK-NEXT: /showIncludes Print cl.exe-style include information to stderr.
; CHECK-NEXT: /U <value> Undefine a symbol for the C preprocessor.
; CHECK-NEXT: /V Be verbose.
; CHECK-NEXT: /X Ignore 'include' variable.
diff --git a/llvm/test/tools/llvm-rc/show-includes.test b/llvm/test/tools/llvm-rc/show-includes.test
new file mode 100644
index 0000000000000..d32bf3c6dc820
--- /dev/null
+++ b/llvm/test/tools/llvm-rc/show-includes.test
@@ -0,0 +1,26 @@
+; Verify /showIncludes forwards --show-includes and -sys-header-deps through to
+; clang cc1 so that all preprocessor-phase includes, including system headers,
+; are reported in MSVC-compatible form.
+; RUN: llvm-rc -### /showIncludes -- %p/Inputs/empty.rc | FileCheck --check-prefix=CMD %s
+; RUN: llvm-rc -### -- %p/Inputs/empty.rc | FileCheck --check-prefix=NOCMD %s
+
+; CMD: "-Xclang" "--show-includes" "-Xclang" "-sys-header-deps"
+; NOCMD-NOT: --show-includes
+; NOCMD-NOT: -sys-header-deps
+
+; Verify /showIncludes emits a "Note: including file:" line for resource files
+; (icons, bitmaps, etc.) loaded after parsing.
+; RUN: rm -f %t.res
+; RUN: llvm-rc -no-preprocess /showIncludes /FO %t.res -- %p/Inputs/include.rc \
+; RUN: 2> %t.includes
+; RUN: FileCheck --check-prefix=RES --input-file=%t.includes %s
+
+; RES: Note: including file: {{.*}}bitmap.bmp
+
+; Without /showIncludes, the note must not be emitted.
+; RUN: rm -f %t.res
+; RUN: llvm-rc -no-preprocess /FO %t.res -- %p/Inputs/include.rc 2> %t.no-includes
+; RUN: FileCheck --check-prefix=NORES --allow-empty \
+; RUN: --input-file=%t.no-includes %s
+
+; NORES-NOT: Note: including file:
diff --git a/llvm/tools/llvm-rc/Opts.td b/llvm/tools/llvm-rc/Opts.td
index 6d9c0e2601a46..ee16a2d51c392 100644
--- a/llvm/tools/llvm-rc/Opts.td
+++ b/llvm/tools/llvm-rc/Opts.td
@@ -42,6 +42,9 @@ def dry_run : F<"dry-run", "Don't compile the input; only try to parse it.">;
def no_preprocess : F<"no-preprocess", "Don't try to preprocess the input file.">;
+def show_includes : F<"showIncludes", "Print cl.exe-style include information "
+ "to stderr.">;
+
// Print (but do not run) the commands to run for preprocessing
def _HASH_HASH_HASH : F_nodoc<"###">;
diff --git a/llvm/tools/llvm-rc/ResourceFileWriter.cpp b/llvm/tools/llvm-rc/ResourceFileWriter.cpp
index 0c3c4abc6606c..fe88ce45bc878 100644
--- a/llvm/tools/llvm-rc/ResourceFileWriter.cpp
+++ b/llvm/tools/llvm-rc/ResourceFileWriter.cpp
@@ -19,6 +19,7 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
+#include "llvm/Support/raw_ostream.h"
using namespace llvm::support;
@@ -1565,6 +1566,14 @@ ResourceFileWriter::loadFile(StringRef File) const {
SmallString<128> Path;
SmallString<128> Cwd;
+ auto Open = [&](StringRef Resolved) {
+ auto Buffer = MemoryBuffer::getFile(Resolved, /*IsText=*/false,
+ /*RequiresNullTerminator=*/false);
+ if (Buffer && Params.ShowIncludes)
+ errs() << "Note: including file: " << Resolved << "\n";
+ return errorOrToExpected(std::move(Buffer));
+ };
+
// 0. The file path is absolute or has a root directory, so we shouldn't
// try to append it on top of other base directories. (An absolute path
// must have a root directory, but e.g. the path "\dir\file" on windows
@@ -1575,16 +1584,14 @@ ResourceFileWriter::loadFile(StringRef File) const {
// properly though, so if using that to append paths below, this early
// exception case could be removed.)
if (sys::path::has_root_directory(File))
- return errorOrToExpected(MemoryBuffer::getFile(
- File, /*IsText=*/false, /*RequiresNullTerminator=*/false));
+ return Open(File);
// 1. The current working directory.
sys::fs::current_path(Cwd);
Path.assign(Cwd.begin(), Cwd.end());
sys::path::append(Path, File);
if (sys::fs::exists(Path))
- return errorOrToExpected(MemoryBuffer::getFile(
- Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
+ return Open(Path);
// 2. The directory of the input resource file, if it is
diff erent from the
// current working directory.
@@ -1592,22 +1599,19 @@ ResourceFileWriter::loadFile(StringRef File) const {
Path.assign(InputFileDir.begin(), InputFileDir.end());
sys::path::append(Path, File);
if (sys::fs::exists(Path))
- return errorOrToExpected(MemoryBuffer::getFile(
- Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
+ return Open(Path);
// 3. All of the include directories specified on the command line.
for (StringRef ForceInclude : Params.Include) {
Path.assign(ForceInclude.begin(), ForceInclude.end());
sys::path::append(Path, File);
if (sys::fs::exists(Path))
- return errorOrToExpected(MemoryBuffer::getFile(
- Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
+ return Open(Path);
}
if (!Params.NoInclude) {
if (auto Result = llvm::sys::Process::FindInEnvPath("INCLUDE", File))
- return errorOrToExpected(MemoryBuffer::getFile(
- *Result, /*IsText=*/false, /*RequiresNullTerminator=*/false));
+ return Open(*Result);
}
return make_error<StringError>("error : file not found : " + Twine(File),
diff --git a/llvm/tools/llvm-rc/ResourceFileWriter.h b/llvm/tools/llvm-rc/ResourceFileWriter.h
index a13af45512625..74dadfa13c9b6 100644
--- a/llvm/tools/llvm-rc/ResourceFileWriter.h
+++ b/llvm/tools/llvm-rc/ResourceFileWriter.h
@@ -41,6 +41,7 @@ struct WriterParams {
bool NoInclude; // Ignore the INCLUDE variable.
StringRef InputFilePath; // The full path of the input file.
int CodePage = CpAcp; // The codepage for interpreting characters.
+ bool ShowIncludes = false; // Report resource-file dependencies.
};
class ResourceFileWriter : public Visitor {
diff --git a/llvm/tools/llvm-rc/llvm-rc.cpp b/llvm/tools/llvm-rc/llvm-rc.cpp
index 471a4467e6303..7afecf667fed0 100644
--- a/llvm/tools/llvm-rc/llvm-rc.cpp
+++ b/llvm/tools/llvm-rc/llvm-rc.cpp
@@ -267,6 +267,12 @@ void preprocess(StringRef Src, StringRef Dst, const RcOptions &Opts,
}
}
llvm::append_range(Args, Opts.PreprocessArgs);
+ if (Opts.Params.ShowIncludes) {
+ Args.push_back("-Xclang");
+ Args.push_back("--show-includes");
+ Args.push_back("-Xclang");
+ Args.push_back("-sys-header-deps");
+ }
Args.push_back(Src);
Args.push_back("-o");
Args.push_back(Dst);
@@ -547,6 +553,7 @@ RcOptions parseRcOptions(ArrayRef<const char *> ArgsArr,
Opts.Preprocess = !InputArgs.hasArg(OPT_no_preprocess);
Opts.Params.Include = InputArgs.getAllArgValues(OPT_includepath);
Opts.Params.NoInclude = InputArgs.hasArg(OPT_noinclude);
+ Opts.Params.ShowIncludes = InputArgs.hasArg(OPT_show_includes);
if (Opts.Params.NoInclude) {
// Clear the INLCUDE variable for the external preprocessor
#ifdef _WIN32
More information about the llvm-commits
mailing list