return Filename;
}
+static Optional<sys::fs::UniqueID> getUniqueID(StringRef Path) {
+ sys::fs::UniqueID Ret;
+ if (sys::fs::getUniqueID(Path, Ret))
+ return None;
+ return Ret;
+}
+
// Resolves a file path. This never returns the same path
// (in that case, it returns None).
Optional<StringRef> LinkerDriver::findFile(StringRef Filename) {
StringRef Path = doFindFile(Filename);
- bool Seen = !VisitedFiles.insert(Path.lower()).second;
- if (Seen)
- return None;
+
+ if (Optional<sys::fs::UniqueID> ID = getUniqueID(Path)) {
+ bool Seen = !VisitedFiles.insert(*ID).second;
+ if (Seen)
+ return None;
+ }
+
if (Path.endswith_lower(".lib"))
VisitedLibs.insert(sys::path::filename(Path));
return Path;
return None;
if (!VisitedLibs.insert(Filename.lower()).second)
return None;
+
StringRef Path = doFindLib(Filename);
if (Config->NoDefaultLibs.count(Path))
return None;
- if (!VisitedFiles.insert(Path.lower()).second)
- return None;
+
+ if (Optional<sys::fs::UniqueID> ID = getUniqueID(Path))
+ if (!VisitedFiles.insert(*ID).second)
+ return None;
return Path;
}
if (errorCount())
return;
- bool WholeArchiveFlag = Args.hasArg(OPT_wholearchive_flag);
+ std::set<sys::fs::UniqueID> WholeArchives;
+ for (auto *Arg : Args.filtered(OPT_wholearchive_file))
+ if (Optional<sys::fs::UniqueID> ID = getUniqueID(Arg->getValue()))
+ WholeArchives.insert(*ID);
+
+ // A predicate returning true if a given path is an argument for
+ // /wholearchive:, or /wholearchive is enabled globally.
+ // This function is a bit tricky because "foo.obj /wholearchive:././foo.obj"
+ // needs to be handled as "/wholearchive:foo.obj foo.obj".
+ auto IsWholeArchive = [&](StringRef Path) -> bool {
+ if (Args.hasArg(OPT_wholearchive_flag))
+ return true;
+ if (Optional<sys::fs::UniqueID> ID = getUniqueID(Path))
+ return WholeArchives.count(*ID);
+ return false;
+ };
+
// Create a list of input files. Files can be given as arguments
// for /defaultlib option.
- std::vector<MemoryBufferRef> MBs;
- for (auto *Arg : Args.filtered(OPT_INPUT, OPT_wholearchive_file)) {
- switch (Arg->getOption().getID()) {
- case OPT_INPUT:
- if (Optional<StringRef> Path = findFile(Arg->getValue()))
- enqueuePath(*Path, WholeArchiveFlag);
- break;
- case OPT_wholearchive_file:
- if (Optional<StringRef> Path = findFile(Arg->getValue()))
- enqueuePath(*Path, true);
- break;
- }
- }
+ for (auto *Arg : Args.filtered(OPT_INPUT, OPT_wholearchive_file))
+ if (Optional<StringRef> Path = findFile(Arg->getValue()))
+ enqueuePath(*Path, IsWholeArchive(Arg->getValue()));
+
for (auto *Arg : Args.filtered(OPT_defaultlib))
if (Optional<StringRef> Path = findLib(Arg->getValue()))
enqueuePath(*Path, false);
#include "llvm/Object/COFF.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/TarWriter.h"
#include <memory>
#include <set>
// Library search path. The first element is always "" (current directory).
std::vector<StringRef> SearchPaths;
- std::set<std::string> VisitedFiles;
+
+ // We don't want to add the same file more than once.
+ // Files are uniquified by their filesystem and file number.
+ std::set<llvm::sys::fs::UniqueID> VisitedFiles;
+
std::set<std::string> VisitedLibs;
Symbol *addUndefined(StringRef Sym);
# RUN: lld-link -dll -out:%t.dll -entry:main %t.main.obj -wholearchive %t.archive.lib -implib:%t.lib
# RUN: llvm-readobj %t.lib | FileCheck %s -check-prefix CHECK-IMPLIB
+# RUN: lld-link -dll -out:%t.dll -entry:main %t.main.obj %t.archive.lib -wholearchive:%t.archive.lib -implib:%t.lib
+# RUN: llvm-readobj %t.lib | FileCheck %s -check-prefix CHECK-IMPLIB
+
+# RUN: mkdir -p %t.dir
+# RUN: cp %t.archive.lib %t.dir/foo.lib
+# RUN: lld-link -dll -out:%t.dll -entry:main %t.main.obj %t.dir/./foo.lib -wholearchive:%t.dir/foo.lib -implib:%t.lib
+# RUN: llvm-readobj %t.lib | FileCheck %s -check-prefix CHECK-IMPLIB
+
# CHECK-IMPLIB: Symbol: __imp_exportfn3
# CHECK-IMPLIB: Symbol: exportfn3