Index: llvm/trunk/include/llvm/Support/Process.h =================================================================== --- llvm/trunk/include/llvm/Support/Process.h +++ llvm/trunk/include/llvm/Support/Process.h @@ -80,9 +80,15 @@ /// This function searches for an existing file in the list of directories /// in a PATH like environment variable, and returns the first file found, /// according to the order of the entries in the PATH like environment - /// variable. - static Optional FindInEnvPath(const std::string& EnvName, - const std::string& FileName); + /// variable. If an ignore list is specified, then any folder which is in + /// the PATH like environment variable but is also in IgnoreList is not + /// considered. + static Optional FindInEnvPath(StringRef EnvName, + StringRef FileName, + ArrayRef IgnoreList); + + static Optional FindInEnvPath(StringRef EnvName, + StringRef FileName); /// This function returns a SmallVector containing the arguments passed from /// the operating system to the program. This function expects to be handed Index: llvm/trunk/lib/Support/Process.cpp =================================================================== --- llvm/trunk/lib/Support/Process.cpp +++ llvm/trunk/lib/Support/Process.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/Process.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Config/config.h" #include "llvm/Support/FileSystem.h" @@ -26,9 +27,14 @@ //=== independent code. //===----------------------------------------------------------------------===// -Optional Process::FindInEnvPath(const std::string& EnvName, - const std::string& FileName) -{ +Optional Process::FindInEnvPath(StringRef EnvName, + StringRef FileName) { + return FindInEnvPath(EnvName, FileName, {}); +} + +Optional Process::FindInEnvPath(StringRef EnvName, + StringRef FileName, + ArrayRef IgnoreList) { assert(!path::is_absolute(FileName)); Optional FoundPath; Optional OptPath = Process::GetEnv(EnvName); @@ -39,10 +45,13 @@ SmallVector Dirs; SplitString(OptPath.getValue(), Dirs, EnvPathSeparatorStr); - for (const auto &Dir : Dirs) { + for (StringRef Dir : Dirs) { if (Dir.empty()) continue; + if (any_of(IgnoreList, [&](StringRef S) { return fs::equivalent(S, Dir); })) + continue; + SmallString<128> FilePath(Dir); path::append(FilePath, FileName); if (fs::exists(Twine(FilePath))) { Index: llvm/trunk/test/tools/llvm-rc/Inputs/deep-include.rc =================================================================== --- llvm/trunk/test/tools/llvm-rc/Inputs/deep-include.rc +++ llvm/trunk/test/tools/llvm-rc/Inputs/deep-include.rc @@ -0,0 +1,3 @@ +// Whether this is found depends on whether the /I flag searches within the +// "nested" subdirectory +foo BITMAP "nested-bitmap.bmp" \ No newline at end of file Index: llvm/trunk/test/tools/llvm-rc/Inputs/include.rc =================================================================== --- llvm/trunk/test/tools/llvm-rc/Inputs/include.rc +++ llvm/trunk/test/tools/llvm-rc/Inputs/include.rc @@ -0,0 +1,2 @@ +// Found because bitmap.bmp is in same directory +foo BITMAP "bitmap.bmp" \ No newline at end of file Index: llvm/trunk/test/tools/llvm-rc/include-paths.test =================================================================== --- llvm/trunk/test/tools/llvm-rc/include-paths.test +++ llvm/trunk/test/tools/llvm-rc/include-paths.test @@ -0,0 +1,45 @@ +; Should find the bitmap if it is in the same folder as the rc file. +; RUN: rm %t.include.res +; RUN: llvm-rc /FO %t.include.res %p/Inputs/include.rc +; RUN: llvm-readobj %t.include.res | FileCheck --check-prefix=FOUND %s + +; Should find the bitmap if the folder is explicitly specified. +; RUN: rm %t.nested-include.res +; RUN: llvm-rc /FO %t.nested-include.res /I %p/Inputs/nested %p/Inputs/deep-include.rc +; RUN: llvm-readobj %t.nested-include.res | FileCheck --check-prefix=FOUND %s + +; Otherwise, it should not find the bitmap. +; RUN: rm %t.nested-include.res +; RUN: not llvm-rc /FO %t.nested-include.res %p/Inputs/deep-include.rc 2>&1 \ +; RUN: | FileCheck --check-prefix=MISSING %s + +; Should find the bitmap if the process's current working directory +; contains the resource being searched for. Do this test last since it +; changes the current working directory and could affect the success or +; failure of other tests if run first. +; RUN: rm %t.nested-include.res +; RUN: cd %p/Inputs/nested +; RUN: llvm-rc /FO %t.nested-include.res %p/Inputs/include.rc +; RUN: llvm-readobj %t.nested-include.res | FileCheck --check-prefix=FOUND %s + +FOUND: Resource type (string): BITMAP +FOUND-NEXT: Resource name (string): FOO +FOUND-NEXT: Data version: 0 +FOUND-NEXT: Memory flags: 0x30 +FOUND-NEXT: Language ID: 1033 +FOUND-NEXT: Version (major): 0 +FOUND-NEXT: Version (minor): 0 +FOUND-NEXT: Characteristics: 0 +FOUND-NEXT: Data size: 110 +FOUND-NEXT: Data: ( +FOUND-NEXT: 0000: 424D6E00 00000000 00003600 00002800 |BMn.......6...(.| +FOUND-NEXT: 0010: 00000200 00000700 00000100 18000000 |................| +FOUND-NEXT: 0020: 00003800 00000000 00000000 00000000 |..8.............| +FOUND-NEXT: 0030: 00000000 00005BB3 855BB385 0000FFFF |......[..[......| +FOUND-NEXT: 0040: FFFFFFFF 0000FFFF FFFFFFFF 0000FFFF |................| +FOUND-NEXT: 0050: FFFFFFFF 00005BB3 85FFFFFF 0000FFFF |......[.........| +FOUND-NEXT: 0060: FF0EC9FF 0000241C EDFFFFFF 0000 |......$.......| +FOUND-NEXT: ) + +MISSING: llvm-rc: Error in BITMAP statement (ID foo): +MISSING-NEXT: error : file not found : nested-bitmap.bmp Index: llvm/trunk/test/tools/llvm-rc/tag-html.test =================================================================== --- llvm/trunk/test/tools/llvm-rc/tag-html.test +++ llvm/trunk/test/tools/llvm-rc/tag-html.test @@ -33,9 +33,3 @@ ; HTML-NEXT: 0020: 202D2D3E 0A3C696D 67207372 633D226B | -->..| ; HTML-NEXT: ) - - -; 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': Index: llvm/trunk/tools/llvm-rc/ResourceFileWriter.h =================================================================== --- llvm/trunk/tools/llvm-rc/ResourceFileWriter.h +++ llvm/trunk/tools/llvm-rc/ResourceFileWriter.h @@ -22,10 +22,17 @@ namespace llvm { namespace rc { +struct SearchParams { + std::vector Include; // Additional folders to search for files. + std::vector NoInclude; // Folders to exclude from file search. + StringRef InputFilePath; // The full path of the input file. +}; + class ResourceFileWriter : public Visitor { public: - ResourceFileWriter(std::unique_ptr Stream) - : FS(std::move(Stream)), IconCursorID(1) { + ResourceFileWriter(const SearchParams &Params, + std::unique_ptr Stream) + : Params(Params), FS(std::move(Stream)), IconCursorID(1) { assert(FS && "Output stream needs to be provided to the serializator"); } @@ -136,6 +143,8 @@ Error writeVersionInfoBlock(const VersionInfoBlock &); Error writeVersionInfoValue(const VersionInfoValue &); + const SearchParams &Params; + // Output stream handling. std::unique_ptr FS; @@ -170,6 +179,8 @@ void padStream(uint64_t Length); + Expected> loadFile(StringRef File) const; + // Icon and cursor IDs are allocated starting from 1 and increasing for // each icon/cursor dumped. This maintains the current ID to be allocated. uint16_t IconCursorID; Index: llvm/trunk/tools/llvm-rc/ResourceFileWriter.cpp =================================================================== --- llvm/trunk/tools/llvm-rc/ResourceFileWriter.cpp +++ llvm/trunk/tools/llvm-rc/ResourceFileWriter.cpp @@ -17,6 +17,8 @@ #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Endian.h" #include "llvm/Support/EndianStream.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" using namespace llvm::support; @@ -41,12 +43,13 @@ ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; } }; -static Error createError(Twine Message, +static Error createError(const Twine &Message, std::errc Type = std::errc::invalid_argument) { return make_error(Message, std::make_error_code(Type)); } -static Error checkNumberFits(uint32_t Number, size_t MaxBits, Twine FieldName) { +static Error checkNumberFits(uint32_t Number, size_t MaxBits, + const Twine &FieldName) { assert(1 <= MaxBits && MaxBits <= 32); if (!(Number >> MaxBits)) return Error::success(); @@ -56,13 +59,13 @@ } template -static Error checkNumberFits(uint32_t Number, Twine FieldName) { +static Error checkNumberFits(uint32_t Number, const Twine &FieldName) { return checkNumberFits(Number, sizeof(FitType) * 8, FieldName); } // A similar function for signed integers. template -static Error checkSignedNumberFits(uint32_t Number, Twine FieldName, +static Error checkSignedNumberFits(uint32_t Number, const Twine &FieldName, bool CanBeNegative) { int32_t SignedNum = Number; if (SignedNum < std::numeric_limits::min() || @@ -79,13 +82,13 @@ return Error::success(); } -static Error checkRCInt(RCInt Number, Twine FieldName) { +static Error checkRCInt(RCInt Number, const Twine &FieldName) { if (Number.isLong()) return Error::success(); return checkNumberFits(Number, FieldName); } -static Error checkIntOrString(IntOrString Value, Twine FieldName) { +static Error checkIntOrString(IntOrString Value, const Twine &FieldName) { if (!Value.isInt()) return Error::success(); return checkNumberFits(Value.getInt(), FieldName); @@ -356,15 +359,10 @@ 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); + auto File = loadFile(Filename); if (!File) - return make_error("Error opening file '" + Filename + - "': " + File.getError().message(), - File.getError()); + return File.takeError(); + *FS << (*File)->getBuffer(); return Error::success(); } @@ -805,15 +803,10 @@ bool IsLong; stripQuotes(FileStr, IsLong); - ErrorOr> File = - MemoryBuffer::getFile(FileStr, -1, false); + auto File = loadFile(FileStr); if (!File) - return make_error( - "Error opening " + - Twine(Type == IconCursorGroupType::Icon ? "icon" : "cursor") + - " '" + FileStr + "': " + File.getError().message(), - File.getError()); + return File.takeError(); BinaryStreamReader Reader((*File)->getBuffer(), support::little); @@ -1413,5 +1406,43 @@ return Error::success(); } +Expected> +ResourceFileWriter::loadFile(StringRef File) const { + SmallString<128> Path; + SmallString<128> Cwd; + std::unique_ptr Result; + + // 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, -1i64, false)); + + // 2. The directory of the input resource file, if it is different from the + // current + // working directory. + StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath); + Path.assign(InputFileDir.begin(), InputFileDir.end()); + sys::path::append(Path, File); + if (sys::fs::exists(Path)) + return errorOrToExpected(MemoryBuffer::getFile(Path, -1i64, false)); + + // 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, -1i64, false)); + } + + if (auto Result = + llvm::sys::Process::FindInEnvPath("INCLUDE", File, Params.NoInclude)) + return errorOrToExpected(MemoryBuffer::getFile(*Result, -1i64, false)); + + return make_error("error : file not found : " + Twine(File), + inconvertibleErrorCode()); +} + } // namespace rc } // namespace llvm Index: llvm/trunk/tools/llvm-rc/ResourceScriptParser.h =================================================================== --- llvm/trunk/tools/llvm-rc/ResourceScriptParser.h +++ llvm/trunk/tools/llvm-rc/ResourceScriptParser.h @@ -25,6 +25,9 @@ #include namespace llvm { +namespace opt { +class InputArgList; +} namespace rc { class RCParser { @@ -51,7 +54,7 @@ LocIter ErrorLoc, FileEnd; }; - RCParser(std::vector TokenList); + explicit RCParser(std::vector TokenList); // Reads and returns a single resource definition, or error message if any // occurred. Index: llvm/trunk/tools/llvm-rc/ResourceScriptParser.cpp =================================================================== --- llvm/trunk/tools/llvm-rc/ResourceScriptParser.cpp +++ llvm/trunk/tools/llvm-rc/ResourceScriptParser.cpp @@ -12,6 +12,10 @@ //===---------------------------------------------------------------------===// #include "ResourceScriptParser.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" // Take an expression returning llvm::Error and forward the error if it exists. #define RETURN_IF_ERROR(Expr) \ Index: llvm/trunk/tools/llvm-rc/llvm-rc.cpp =================================================================== --- llvm/trunk/tools/llvm-rc/llvm-rc.cpp +++ llvm/trunk/tools/llvm-rc/llvm-rc.cpp @@ -68,7 +68,7 @@ static ExitOnError ExitOnErr; -LLVM_ATTRIBUTE_NORETURN static void fatalError(Twine Message) { +LLVM_ATTRIBUTE_NORETURN static void fatalError(const Twine &Message) { errs() << Message << "\n"; exit(1); } @@ -107,10 +107,10 @@ } // Read and tokenize the input file. - const Twine &Filename = InArgsInfo[0]; - ErrorOr> File = MemoryBuffer::getFile(Filename); + ErrorOr> File = + MemoryBuffer::getFile(InArgsInfo[0]); if (!File) { - fatalError("Error opening file '" + Filename + + fatalError("Error opening file '" + Twine(InArgsInfo[0]) + "': " + File.getError().message()); } @@ -138,6 +138,13 @@ } } + SearchParams Params; + SmallString<128> InputFile(InArgsInfo[0]); + llvm::sys::fs::make_absolute(InputFile); + Params.InputFilePath = InputFile; + Params.Include = InputArgs.getAllArgValues(OPT_INCLUDE); + Params.NoInclude = InputArgs.getAllArgValues(OPT_NOINCLUDE); + std::unique_ptr Visitor; bool IsDryRun = InputArgs.hasArg(OPT_DRY_RUN); @@ -153,7 +160,7 @@ if (EC) fatalError("Error opening output file '" + OutArgsInfo[0] + "': " + EC.message()); - Visitor = llvm::make_unique(std::move(FOut)); + Visitor = llvm::make_unique(Params, std::move(FOut)); Visitor->AppendNull = InputArgs.hasArg(OPT_ADD_NULL); ExitOnErr(NullResource().visit(Visitor.get()));