Skip to content

[llvm-rc] Accept filenames provided as multiple string literals #68881

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 16, 2023

Conversation

mstorsjo
Copy link
Member

GNU windres supports this, while MS rc.exe doesn't.

MS rc.exe only supports treating consecutive string literals as if they were fused into one in a few fixed locations (most of which are already supported), while GNU windres supports this essentially anywhere in any string. See
b989fcb for one recent change that extended support for this in one specific resource.

A reasonable use case for multiple concatenated string literals that GNU windres accepts is 1 ICON DIR "/name.ico", where the directory is provided via the preprocessor, expanding to another string literal; this is #51286.

Extend the parser to try to consume all consecutive string tokens, whenever reading a filename. Adjust the handling of user data resources read from a file to use the readFilename() helper.

While this probably doesn't cover every single case where GNU windres might accept concatenated string literals, this is the primary missing case that has been reported so far.

@llvmbot
Copy link
Member

llvmbot commented Oct 12, 2023

@llvm/pr-subscribers-platform-windows

Author: Martin Storsjö (mstorsjo)

Changes

GNU windres supports this, while MS rc.exe doesn't.

MS rc.exe only supports treating consecutive string literals as if they were fused into one in a few fixed locations (most of which are already supported), while GNU windres supports this essentially anywhere in any string. See
b989fcb for one recent change that extended support for this in one specific resource.

A reasonable use case for multiple concatenated string literals that GNU windres accepts is 1 ICON DIR "/name.ico", where the directory is provided via the preprocessor, expanding to another string literal; this is #51286.

Extend the parser to try to consume all consecutive string tokens, whenever reading a filename. Adjust the handling of user data resources read from a file to use the readFilename() helper.

While this probably doesn't cover every single case where GNU windres might accept concatenated string literals, this is the primary missing case that has been reported so far.


Full diff: https://github.com/llvm/llvm-project/pull/68881.diff

4 Files Affected:

  • (added) llvm/test/tools/llvm-rc/Inputs/split-path.rc (+2)
  • (added) llvm/test/tools/llvm-rc/split-path.test (+7)
  • (modified) llvm/tools/llvm-rc/ResourceScriptParser.cpp (+25-4)
  • (modified) llvm/tools/llvm-rc/ResourceScriptParser.h (+4)
diff --git a/llvm/test/tools/llvm-rc/Inputs/split-path.rc b/llvm/test/tools/llvm-rc/Inputs/split-path.rc
new file mode 100644
index 000000000000000..fb510e89698f747
--- /dev/null
+++ b/llvm/test/tools/llvm-rc/Inputs/split-path.rc
@@ -0,0 +1,2 @@
+100 ICON "subdir" "/icon-new.ico"
+101 24 "subdir" "/empty.manifest"
diff --git a/llvm/test/tools/llvm-rc/split-path.test b/llvm/test/tools/llvm-rc/split-path.test
new file mode 100644
index 000000000000000..a12fd2bc32c1161
--- /dev/null
+++ b/llvm/test/tools/llvm-rc/split-path.test
@@ -0,0 +1,7 @@
+; RUN: rm -rf %t
+; RUN: mkdir %t
+; RUN: cd %t
+; RUN: mkdir subdir
+; RUN: cp %p/Inputs/icon-new.ico subdir
+; RUN: touch subdir/empty.manifest
+; RUN: llvm-windres --no-preprocess %p/Inputs/split-path.rc %t/split-path.res
diff --git a/llvm/tools/llvm-rc/ResourceScriptParser.cpp b/llvm/tools/llvm-rc/ResourceScriptParser.cpp
index 9e1047448831b37..784bc1973d9062d 100644
--- a/llvm/tools/llvm-rc/ResourceScriptParser.cpp
+++ b/llvm/tools/llvm-rc/ResourceScriptParser.cpp
@@ -238,7 +238,27 @@ Expected<StringRef> RCParser::readString() {
 Expected<StringRef> RCParser::readFilename() {
   if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier))
     return getExpectedError("string");
-  return read().value();
+  const RCToken &Token = read();
+  StringRef Str = Token.value();
+  if (Token.kind() != Kind::String)
+    return Str;
+  while (isNextTokenKind(Kind::String)) {
+    const RCToken &NextToken = read();
+    StringRef Next = NextToken.value();
+    bool IsWide = Str.starts_with_insensitive("L");
+    if (IsWide)
+      Str = Str.drop_front();
+    if (Next.starts_with_insensitive("L"))
+      Next = Next.drop_front();
+    bool StrUnquoted = Str.consume_front("\"") && Str.consume_back("\"");
+    bool NextUnquoted = Next.consume_front("\"") && Next.consume_back("\"");
+    assert(StrUnquoted && NextUnquoted);
+    (void)StrUnquoted;
+    (void)NextUnquoted;
+
+    Str = Saver.save(Twine(IsWide ? "L" : "") + "\"" + Str + Next + "\"");
+  }
+  return Str;
 }
 
 Expected<StringRef> RCParser::readIdentifier() {
@@ -499,9 +519,10 @@ RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
   // Check if this is a file resource.
   switch (look().kind()) {
   case Kind::String:
-  case Kind::Identifier:
-    return std::make_unique<UserDefinedResource>(Type, read().value(),
-                                                 MemoryFlags);
+  case Kind::Identifier: {
+    ASSIGN_OR_RETURN(Filename, readFilename());
+    return std::make_unique<UserDefinedResource>(Type, *Filename, MemoryFlags);
+  }
   default:
     break;
   }
diff --git a/llvm/tools/llvm-rc/ResourceScriptParser.h b/llvm/tools/llvm-rc/ResourceScriptParser.h
index 5c01cec0f151e8b..603afd8d73fb1aa 100644
--- a/llvm/tools/llvm-rc/ResourceScriptParser.h
+++ b/llvm/tools/llvm-rc/ResourceScriptParser.h
@@ -18,6 +18,7 @@
 #include "ResourceScriptToken.h"
 
 #include "llvm/Support/Compiler.h"
+#include "llvm/Support/StringSaver.h"
 #include "llvm/Support/raw_ostream.h"
 
 #include <system_error>
@@ -185,6 +186,9 @@ class RCParser {
   std::vector<RCToken> Tokens;
   LocIter CurLoc;
   const LocIter End;
+
+  BumpPtrAllocator Alloc;
+  StringSaver Saver{Alloc};
 };
 
 } // namespace rc

Comment on lines 248 to 252
bool IsWide = Str.starts_with_insensitive("L");
if (IsWide)
Str = Str.drop_front();
if (Next.starts_with_insensitive("L"))
Next = Next.drop_front();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could use consume_front_insensitive.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use consume_front_insensitive, that does indeed make it yet a little bit nicer. Thanks!

@cjacek
Copy link
Contributor

cjacek commented Oct 13, 2023

Considering your findings from #68685, this approach looks reasonable to me.

GNU windres supports this, while MS rc.exe doesn't.

MS rc.exe only supports treating consecutive string literals as
if they were fused into one in a few fixed locations (most of
which are already supported), while GNU windres supports this
essentially anywhere in any string. See
b989fcb for one recent change
that extended support for this in one specific resource.

A reasonable use case for multiple concatenated string literals
that GNU windres accepts is `1 ICON DIR "/name.ico"`, where the
directory is provided via the preprocessor, expanding to another
string literal; this is llvm#51286.

Extend the parser to try to consume all consecutive string
tokens, whenever reading a filename. Adjust the handling of user
data resources read from a file to use the readFilename() helper.

While this probably doesn't cover every single case where GNU
windres might accept concatenated string literals, this is
the primary missing case that has been reported so far.
@mstorsjo mstorsjo force-pushed the llvm-rc-concat-filenames branch from 103180b to ca020c7 Compare October 16, 2023 09:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
llvm-tools All llvm tools that do not have corresponding tag platform:windows
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants