Index: test/tools/llvm-rc/Inputs/cpp-output.rc =================================================================== --- /dev/null +++ test/tools/llvm-rc/Inputs/cpp-output.rc @@ -0,0 +1,28 @@ +// Test that the input originally is included. +STRINGTABLE { + 1 "a" +} +#line 2 "cpp-source.rc" +// Content from a rc file (potentially the source file itself) is included. +STRINGTABLE { + 2 "b" +} +// Test a preprocessing directive that starts with leading whitespace. + #line 1 "\\some\\path\\header.h" +// Content from .h files is ignored. +typedef int Foo; +#line 123 "\\some\\path\\header.h" +void someFunc(void); +// Check GNU style line markers. +# 4 "cpp-source.rc" 1 +STRINGTABLE { + 3 "c" +} + # 1 "other/header.h" 1 +typedef int Bar; +# 10 "cpp-source.rc" 2 +// Test that other preprocessor directives are ignored. +#pragma foo +STRINGTABLE { + 4 "d" +} Index: test/tools/llvm-rc/cpp-output.test =================================================================== --- /dev/null +++ test/tools/llvm-rc/cpp-output.test @@ -0,0 +1,17 @@ +; RUN: llvm-rc /FO %t %p/Inputs/cpp-output.rc +; RUN: llvm-readobj %t | FileCheck %s + +; CHECK: Resource type (int): 6 +; CHECK-NEXT: Resource name (int): 1 +; CHECK-NEXT: Data version: 0 +; CHECK-NEXT: Memory flags: 0x1030 +; CHECK-NEXT: Language ID: 1033 +; CHECK-NEXT: Version (major): 0 +; CHECK-NEXT: Version (minor): 0 +; CHECK-NEXT: Characteristics: 0 +; CHECK-NEXT: Data size: 40 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 00000100 61000100 62000100 63000100 |....a...b...c...| +; CHECK-NEXT: 0010: 64000000 00000000 00000000 00000000 |d...............| +; CHECK-NEXT: 0020: 00000000 00000000 |........| +; CHECK-NEXT: ) Index: tools/llvm-rc/CMakeLists.txt =================================================================== --- tools/llvm-rc/CMakeLists.txt +++ tools/llvm-rc/CMakeLists.txt @@ -11,6 +11,7 @@ add_llvm_tool(llvm-rc llvm-rc.cpp ResourceFileWriter.cpp + ResourceScriptCppFilter.cpp ResourceScriptParser.cpp ResourceScriptStmt.cpp ResourceScriptToken.cpp Index: tools/llvm-rc/ResourceScriptCppFilter.h =================================================================== --- /dev/null +++ tools/llvm-rc/ResourceScriptCppFilter.h @@ -0,0 +1,33 @@ +//===-- ResourceScriptCppFilter.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// +// +// This filters the input to llvm-rc for preprocessor markers, removing +// preprocessing directives that a preprocessor can output or leave behind. +// +// It also filters out any contribution from files named *.h, based on +// preprocessor line markers. When preprocessing RC files, the included +// headers can leave behind C declarations, that RC doesn't understand. +// Rc.exe simply discards anything that comes from files named *.h. +// +//===---------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTCPPFILTER_H +#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTCPPFILTER_H + +#include "llvm/ADT/StringRef.h" + +#include + +namespace llvm { + +std::string filterCppOutput(StringRef Input); + +} // namespace llvm + +#endif Index: tools/llvm-rc/ResourceScriptCppFilter.cpp =================================================================== --- /dev/null +++ tools/llvm-rc/ResourceScriptCppFilter.cpp @@ -0,0 +1,125 @@ +//===-- ResourceScriptCppFilter.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// +// +// This file implements an interface defined in ResourceScriptCppFilter.h. +// +//===---------------------------------------------------------------------===// + +#include "ResourceScriptCppFilter.h" + +using namespace llvm; + +namespace { + +class Filter { +public: + Filter(StringRef Input) : Data(Input), DataLength(Input.size()) {} + + std::string run(); + +private: + // Parse the line, returning whether the line should be included in + // the output. + bool parseLine(StringRef Line); + + bool streamEof() const; + + StringRef Data; + size_t DataLength, Pos; + + bool Outputting; +}; + +std::string Filter::run() { + Pos = 0; + std::string Result; + + Outputting = true; + while (!streamEof() && Pos != StringRef::npos) { + size_t LineStart = Pos; + Pos = Data.find_first_of("\r\n", Pos); + // Line content without trailing newlines. + StringRef Line = Data.take_front(Pos).drop_front(LineStart); + Pos = Data.find_first_not_of("\r\n", Pos); + // Line content with all trailing newlines. + StringRef FullLine = Data.take_front(Pos).drop_front(LineStart); + + if (parseLine(Line)) + Result += FullLine; + } + + return Result; +} + +bool Filter::parseLine(StringRef Line) { + Line = Line.ltrim(); + + if (Line.size() == 0 || Line[0] != '#') { + // A normal content line, filtered according to the current mode. + return Outputting; + } + + // Found a preprocessing directive line. From here on, we always return + // false since the preprocessing directives should be filtered out. + + if (!Line.startswith("#line ") && !Line.startswith("# ")) + return false; // Not a line directive (pragma etc). + + // #line 123 "path/file.h" + // # 123 "path/file.h" 1 + + size_t Pos = 0; + // Skip past the # or #line. + Pos = Line.find_first_of(' ', Pos); + Pos = Line.find_first_not_of(' ', Pos); + + // Expecting a line number. + Pos = Line.find_first_not_of("1234567890", Pos); + // Assuming the above skipped past numbers and we're back at whitespace. + if (Pos >= Line.size() || Line[Pos] != ' ') + return false; // Malformed line, skip it. + + Pos = Line.find_first_not_of(' ', Pos); + // Assuming we've found the quoted pathname. + if (Pos >= Line.size() || Line[Pos] != '"') + return false; // Malformed line, just skip it. + + size_t RPos = Line.rfind('"'); + if (RPos <= Pos || RPos >= Line.size()) + return false; // No filename found. + + StringRef Path = Line.substr(Pos + 1, RPos - Pos - 1); + size_t ExtPos = Path.rfind('.'); + if (ExtPos == StringRef::npos) { + // No extension found + Outputting = true; + return false; + } + + StringRef Ext = Path.substr(ExtPos + 1); + if (Ext.equals_lower("h")) { + Outputting = false; + } else { + Outputting = true; + } + + return false; +} + +bool Filter::streamEof() const { return Pos == DataLength; } + +} // anonymous namespace + +namespace llvm { + +std::string filterCppOutput(StringRef Input) { + return Filter(Input).run(); +} + +} // namespace llvm Index: tools/llvm-rc/llvm-rc.cpp =================================================================== --- tools/llvm-rc/llvm-rc.cpp +++ tools/llvm-rc/llvm-rc.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "ResourceFileWriter.h" +#include "ResourceScriptCppFilter.h" #include "ResourceScriptParser.h" #include "ResourceScriptStmt.h" #include "ResourceScriptToken.h" @@ -111,7 +112,8 @@ std::unique_ptr FileContents = std::move(*File); StringRef Contents = FileContents->getBuffer(); - std::vector Tokens = ExitOnErr(tokenizeRC(Contents)); + std::string FilteredContents = filterCppOutput(Contents); + std::vector Tokens = ExitOnErr(tokenizeRC(FilteredContents)); if (BeVerbose) { const Twine TokenNames[] = {