Index: llvm/test/tools/llvm-rc/Inputs/DirTest/Dir1/Dir2/a.html =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/DirTest/Dir1/Dir2/a.html @@ -0,0 +1 @@ +d1/d2 Index: llvm/test/tools/llvm-rc/Inputs/DirTest/Dir1/a.html =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/DirTest/Dir1/a.html @@ -0,0 +1 @@ +d1 Index: llvm/test/tools/llvm-rc/Inputs/DirTest/Dir1/dir-test.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/DirTest/Dir1/dir-test.rc @@ -0,0 +1,2 @@ +1 HTML "a.html" +2 HTML "Dir2/a.html" Index: llvm/test/tools/llvm-rc/Inputs/DirTest/Dir2/a.html =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/DirTest/Dir2/a.html @@ -0,0 +1 @@ +d2 Index: llvm/test/tools/llvm-rc/Inputs/DirTest/Dir3/dir-test.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/DirTest/Dir3/dir-test.rc @@ -0,0 +1,2 @@ +1 HTML "a.html" +2 HTML "Dir2/a.html" Index: llvm/test/tools/llvm-rc/Inputs/DirTest/a.html =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/DirTest/a.html @@ -0,0 +1 @@ +root Index: llvm/test/tools/llvm-rc/Inputs/DirTest/dir-test.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/DirTest/dir-test.rc @@ -0,0 +1,2 @@ +1 HTML "a.html" +2 HTML "Dir2/a.html" Index: llvm/test/tools/llvm-rc/lookup.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/lookup.test @@ -0,0 +1,153 @@ +; REQUIRES: shell +; RUN: rm -rf %t && cp -r %p/Inputs/DirTest %t && cd %t + +; RUN: cd %t && llvm-rc /fo %t/out1.res dir-test.rc +; RUN: llvm-readobj %t/out1.res | FileCheck %s --check-prefix=DIR1 + +; DIR1: Resource type (int): 23 +; DIR1-NEXT: Resource name (int): 1 +; DIR1-NEXT: Data version: 0 +; DIR1-NEXT: Memory flags: 0x30 +; DIR1-NEXT: Language ID: 1033 +; DIR1-NEXT: Version (major): 0 +; DIR1-NEXT: Version (minor): 0 +; DIR1-NEXT: Characteristics: 0 +; DIR1-NEXT: Data size: 5 +; DIR1-NEXT: Data:: (72 6F 6F 74 0A) + +; DIR1-DAG: Resource type (int): 23 +; DIR1-NEXT: Resource name (int): 2 +; DIR1-NEXT: Data version: 0 +; DIR1-NEXT: Memory flags: 0x30 +; DIR1-NEXT: Language ID: 1033 +; DIR1-NEXT: Version (major): 0 +; DIR1-NEXT: Version (minor): 0 +; DIR1-NEXT: Characteristics: 0 +; DIR1-NEXT: Data size: 3 +; DIR1-NEXT: Data:: (64 32 0A) + +; RUN: cd %t && llvm-rc /fo %t/out2.res Dir1/dir-test.rc +; RUN: llvm-readobj %t/out2.res | FileCheck %s --check-prefix=DIR2 + +; DIR2: Resource type (int): 23 +; DIR2-NEXT: Resource name (int): 1 +; DIR2-NEXT: Data version: 0 +; DIR2-NEXT: Memory flags: 0x30 +; DIR2-NEXT: Language ID: 1033 +; DIR2-NEXT: Version (major): 0 +; DIR2-NEXT: Version (minor): 0 +; DIR2-NEXT: Characteristics: 0 +; DIR2-NEXT: Data size: 3 +; DIR2-NEXT: Data:: (64 31 0A) + +; DIR2-DAG: Resource type (int): 23 +; DIR2-NEXT: Resource name (int): 2 +; DIR2-NEXT: Data version: 0 +; DIR2-NEXT: Memory flags: 0x30 +; DIR2-NEXT: Language ID: 1033 +; DIR2-NEXT: Version (major): 0 +; DIR2-NEXT: Version (minor): 0 +; DIR2-NEXT: Characteristics: 0 +; DIR2-NEXT: Data size: 6 +; DIR2-NEXT: Data:: (64 31 2F 64 32 0A) + +; RUN: cd %t && llvm-rc /fo %t/out3.res Dir3/dir-test.rc +; RUN: llvm-readobj %t/out3.res | FileCheck %s --check-prefix=DIR3 + +; DIR3: Resource type (int): 23 +; DIR3-NEXT: Resource name (int): 1 +; DIR3-NEXT: Data version: 0 +; DIR3-NEXT: Memory flags: 0x30 +; DIR3-NEXT: Language ID: 1033 +; DIR3-NEXT: Version (major): 0 +; DIR3-NEXT: Version (minor): 0 +; DIR3-NEXT: Characteristics: 0 +; DIR3-NEXT: Data size: 5 +; DIR3-NEXT: Data:: (72 6F 6F 74 0A) + +; DIR3-DAG: Resource type (int): 23 +; DIR3-NEXT: Resource name (int): 2 +; DIR3-NEXT: Data version: 0 +; DIR3-NEXT: Memory flags: 0x30 +; DIR3-NEXT: Language ID: 1033 +; DIR3-NEXT: Version (major): 0 +; DIR3-NEXT: Version (minor): 0 +; DIR3-NEXT: Characteristics: 0 +; DIR3-NEXT: Data size: 3 +; DIR3-NEXT: Data:: (64 32 0A) + +; RUN: cd %t/Dir3 && llvm-rc /fo %t/out4.res -I .. dir-test.rc +; RUN: llvm-readobj %t/out4.res | FileCheck %s --check-prefix=DIR4 + +; DIR4: Resource type (int): 23 +; DIR4-NEXT: Resource name (int): 1 +; DIR4-NEXT: Data version: 0 +; DIR4-NEXT: Memory flags: 0x30 +; DIR4-NEXT: Language ID: 1033 +; DIR4-NEXT: Version (major): 0 +; DIR4-NEXT: Version (minor): 0 +; DIR4-NEXT: Characteristics: 0 +; DIR4-NEXT: Data size: 5 +; DIR4-NEXT: Data:: (72 6F 6F 74 0A) + +; DIR4-DAG: Resource type (int): 23 +; DIR4-NEXT: Resource name (int): 2 +; DIR4-NEXT: Data version: 0 +; DIR4-NEXT: Memory flags: 0x30 +; DIR4-NEXT: Language ID: 1033 +; DIR4-NEXT: Version (major): 0 +; DIR4-NEXT: Version (minor): 0 +; DIR4-NEXT: Characteristics: 0 +; DIR4-NEXT: Data size: 3 +; DIR4-NEXT: Data:: (64 32 0A) + +; RUN: cd %t/Dir3 && llvm-rc /fo %t/out5.res -I %t/Dir1 -I %t dir-test.rc +; RUN: llvm-readobj %t/out5.res | FileCheck %s --check-prefix=DIR5 + +; DIR5: Resource type (int): 23 +; DIR5-NEXT: Resource name (int): 1 +; DIR5-NEXT: Data version: 0 +; DIR5-NEXT: Memory flags: 0x30 +; DIR5-NEXT: Language ID: 1033 +; DIR5-NEXT: Version (major): 0 +; DIR5-NEXT: Version (minor): 0 +; DIR5-NEXT: Characteristics: 0 +; DIR5-NEXT: Data size: 3 +; DIR5-NEXT: Data:: (64 31 0A) + +; DIR5-DAG: Resource type (int): 23 +; DIR5-NEXT: Resource name (int): 2 +; DIR5-NEXT: Data version: 0 +; DIR5-NEXT: Memory flags: 0x30 +; DIR5-NEXT: Language ID: 1033 +; DIR5-NEXT: Version (major): 0 +; DIR5-NEXT: Version (minor): 0 +; DIR5-NEXT: Characteristics: 0 +; DIR5-NEXT: Data size: 6 +; DIR5-NEXT: Data:: (64 31 2F 64 32 0A) + + +; RUN: cd %t/Dir3 && llvm-rc /fo %t/out6.res ../Dir1/dir-test.rc +; RUN: llvm-readobj %t/out6.res | FileCheck %s --check-prefix=DIR6 + +; DIR6: Resource type (int): 23 +; DIR6-NEXT: Resource name (int): 1 +; DIR6-NEXT: Data version: 0 +; DIR6-NEXT: Memory flags: 0x30 +; DIR6-NEXT: Language ID: 1033 +; DIR6-NEXT: Version (major): 0 +; DIR6-NEXT: Version (minor): 0 +; DIR6-NEXT: Characteristics: 0 +; DIR6-NEXT: Data size: 3 +; DIR6-NEXT: Data:: (64 31 0A) + +; DIR6-DAG: Resource type (int): 23 +; DIR6-NEXT: Resource name (int): 2 +; DIR6-NEXT: Data version: 0 +; DIR6-NEXT: Memory flags: 0x30 +; DIR6-NEXT: Language ID: 1033 +; DIR6-NEXT: Version (major): 0 +; DIR6-NEXT: Version (minor): 0 +; DIR6-NEXT: Characteristics: 0 +; DIR6-NEXT: Data size: 6 +; DIR6-NEXT: Data:: (64 31 2F 64 32 0A) Index: llvm/test/tools/llvm-rc/tag-html.test =================================================================== --- llvm/test/tools/llvm-rc/tag-html.test +++ llvm/test/tools/llvm-rc/tag-html.test @@ -38,4 +38,4 @@ ; RUN: not llvm-rc /FO %t/tag-html-wrong.res %p/Inputs/tag-html-wrong.rc 2>&1 | FileCheck %s --check-prefix NOFILE ; NOFILE: llvm-rc: Error in HTML statement (ID 1): -; NOFILE-NEXT: Error opening file 'some-really-nonexistent-file.html': +; NOFILE-NEXT: Error opening file "some-really-nonexistent-file.html": Index: llvm/test/tools/llvm-rc/tag-icon-cursor.test =================================================================== --- llvm/test/tools/llvm-rc/tag-icon-cursor.test +++ llvm/test/tools/llvm-rc/tag-icon-cursor.test @@ -326,7 +326,7 @@ ; RUN: not llvm-rc /FO %t/1 %p/Inputs/tag-icon-cursor-nonexistent.rc 2>&1 | FileCheck %s --check-prefix NOFILE ; NOFILE: llvm-rc: Error in CURSOR statement (ID 500): -; NOFILE-NEXT: Error opening cursor 'this-file-does-not-exist.cur': +; NOFILE-NEXT: Error opening cursor "this-file-does-not-exist.cur": ; RUN: not llvm-rc /FO %t/1 %p/Inputs/tag-icon-cursor-nonsense.rc 2>&1 | FileCheck %s --check-prefix NONSENSE Index: llvm/tools/llvm-rc/ResourceFileWriter.h =================================================================== --- llvm/tools/llvm-rc/ResourceFileWriter.h +++ llvm/tools/llvm-rc/ResourceFileWriter.h @@ -22,6 +22,8 @@ namespace llvm { namespace rc { +using PathType = SmallVector; + class ResourceFileWriter : public Visitor { public: ResourceFileWriter(std::unique_ptr Stream) @@ -52,6 +54,7 @@ Error dumpAllStringTables(); bool AppendNull; // Append '\0' to each existing STRINGTABLE element? + std::vector IncludeList; // List of include paths. struct ObjectInfo { uint16_t LanguageInfo; @@ -166,6 +169,7 @@ void writeRCInt(RCInt); + ErrorOr> openFile(StringRef Filename); Error appendFile(StringRef Filename); void padStream(uint64_t Length); Index: llvm/tools/llvm-rc/ResourceFileWriter.cpp =================================================================== --- llvm/tools/llvm-rc/ResourceFileWriter.cpp +++ llvm/tools/llvm-rc/ResourceFileWriter.cpp @@ -17,6 +17,7 @@ #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Endian.h" #include "llvm/Support/EndianStream.h" +#include "llvm/Support/Path.h" using namespace llvm::support; @@ -352,18 +353,29 @@ writeObject((uint16_t)Value); } -Error ResourceFileWriter::appendFile(StringRef Filename) { +// Open requested file, taking all include paths into account. +ErrorOr> +ResourceFileWriter::openFile(StringRef Filename) { bool IsLong; stripQuotes(Filename, IsLong); - // Filename path should be relative to the current working directory. - // FIXME: docs say so, but reality is more complicated, script - // location and include paths must be taken into account. - ErrorOr> File = - MemoryBuffer::getFile(Filename, -1, false); + for (auto Path : IncludeList) { + PathType FullPath = Path; + sys::path::append(FullPath, Filename); + if (!sys::fs::exists(FullPath)) + continue; + + return MemoryBuffer::getFile(FullPath, -1, false); + } + + return std::make_error_code(std::errc::no_such_file_or_directory); +} + +Error ResourceFileWriter::appendFile(StringRef Filename) { + auto File = openFile(Filename); if (!File) - return make_error("Error opening file '" + Filename + - "': " + File.getError().message(), + return make_error("Error opening file " + Filename + + ": " + File.getError().message(), File.getError()); *FS << (*File)->getBuffer(); return Error::success(); @@ -804,16 +816,12 @@ Type = IconCursorGroupType::Cursor; } - bool IsLong; - stripQuotes(FileStr, IsLong); - ErrorOr> File = - MemoryBuffer::getFile(FileStr, -1, false); - + auto File = openFile(FileStr); if (!File) return make_error( "Error opening " + Twine(Type == IconCursorGroupType::Icon ? "icon" : "cursor") + - " '" + FileStr + "': " + File.getError().message(), + " " + FileStr + ": " + File.getError().message(), File.getError()); BinaryStreamReader Reader((*File)->getBuffer(), support::little); Index: llvm/tools/llvm-rc/llvm-rc.cpp =================================================================== --- llvm/tools/llvm-rc/llvm-rc.cpp +++ llvm/tools/llvm-rc/llvm-rc.cpp @@ -22,6 +22,7 @@ #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Process.h" #include "llvm/Support/Signals.h" @@ -73,6 +74,30 @@ exit(1); } +PathType pathToNative(StringRef Location) { + PathType LocationStr; + sys::path::native(Location, LocationStr); + if (auto EC = sys::fs::make_absolute(LocationStr)) + fatalError("Could not process get absolute input file name."); + return LocationStr; +} + +std::vector +fetchIncludes(StringRef InputName, const opt::InputArgList &InputArgs) { + // Include search order: + // 1. next to the input .rc + // 2. cwd + // 3. in all /I args (relative to cwd), in order + // 4. in directories in %INCLUDE% (FIXME: not implemented; also a bit silly), + // if /x isn't passed (/x makes MS rc not look in %INCLUDE%) + + std::vector Dirs{pathToNative(sys::path::parent_path(InputName)), + pathToNative(StringRef())}; + for (auto Include : InputArgs.getAllArgValues(OPT_INCLUDE)) + Dirs.push_back(pathToNative(Include)); + return Dirs; +} + } // anonymous namespace int main(int argc_, const char *argv_[]) { @@ -107,7 +132,7 @@ } // Read and tokenize the input file. - const Twine &Filename = InArgsInfo[0]; + StringRef Filename = InArgsInfo[0]; ErrorOr> File = MemoryBuffer::getFile(Filename); if (!File) { fatalError("Error opening file '" + Filename + @@ -155,6 +180,7 @@ "': " + EC.message()); Visitor = make_unique(std::move(FOut)); Visitor->AppendNull = InputArgs.hasArg(OPT_ADD_NULL); + Visitor->IncludeList = fetchIncludes(Filename, InputArgs); ExitOnErr(NullResource().visit(Visitor.get()));