Index: lib/Object/ArchiveWriter.cpp =================================================================== --- lib/Object/ArchiveWriter.cpp +++ lib/Object/ArchiveWriter.cpp @@ -496,27 +496,46 @@ namespace llvm { // Compute the relative path from From to To. std::string computeArchiveRelativePath(StringRef From, StringRef To) { - if (sys::path::is_absolute(From) || sys::path::is_absolute(To)) - return To; + auto getDotlessAbsolutePath = + [](SmallVectorImpl &P) -> std::error_code { + std::error_code Ret = sys::fs::make_absolute(P); + if (!Ret) { + sys::path::remove_dots(P, /*removedotdot*/ true); +#ifdef _WIN32 + // Canonicalise '\\' to '/' as sys::path::const_iterator may return a UNC + // specifier. It also returns the first directory separator after a drive + // letter or UNC specifier as a component of the path. + std::replace(P.begin(), P.end(), '\\', '/'); +#endif + } + return Ret; + }; - StringRef DirFrom = sys::path::parent_path(From); - auto FromI = sys::path::begin(DirFrom); - auto ToI = sys::path::begin(To); + SmallString<128> PathTo = To; + SmallString<128> DirFrom = sys::path::parent_path(From); + if (getDotlessAbsolutePath(PathTo) || getDotlessAbsolutePath(DirFrom)) + return sys::path::convert_to_slash(To); + + auto FromS = sys::path::begin(DirFrom); + auto FromI = FromS; + auto ToI = sys::path::begin(PathTo); while (*FromI == *ToI) { ++FromI; ++ToI; } + // Can't construct a relative path between different roots + if ((FromI == FromS) && !sys::path::root_name(PathTo).empty()) + return PathTo.str(); + SmallString<128> Relative; for (auto FromE = sys::path::end(DirFrom); FromI != FromE; ++FromI) - sys::path::append(Relative, ".."); + sys::path::append(Relative, sys::path::Style::posix, ".."); - for (auto ToE = sys::path::end(To); ToI != ToE; ++ToI) - sys::path::append(Relative, *ToI); + for (auto ToE = sys::path::end(PathTo); ToI != ToE; ++ToI) + sys::path::append(Relative, sys::path::Style::posix, *ToI); - // Replace backslashes with slashes so that the path is portable between *nix - // and Windows. - return sys::path::convert_to_slash(Relative); + return Relative.str(); } Error writeArchive(StringRef ArcName, ArrayRef NewMembers, Index: test/tools/llvm-ar/reduce-thin-path.test =================================================================== --- test/tools/llvm-ar/reduce-thin-path.test +++ test/tools/llvm-ar/reduce-thin-path.test @@ -0,0 +1,10 @@ +RUN: rm -rf %t && mkdir -p %t/foo/bar/ +RUN: mkdir -p %t/baz/ +RUN: yaml2obj %S/Inputs/elf.yaml -o %t/elf.o + +RUN: cd %t && llvm-ar rTc %t/baz/internal.ar elf.o +RUN: cd %t/foo && llvm-ar rTc %t/foo/bar/external.ar ../baz/internal.ar + +RUN: FileCheck -input-file=%t/foo/bar/external.ar %s + +CHECK: {{^}}../../elf.o/ Index: test/tools/llvm-ar/thin-archive.test =================================================================== --- test/tools/llvm-ar/thin-archive.test +++ test/tools/llvm-ar/thin-archive.test @@ -0,0 +1,45 @@ +RUN: rm -rf %t && mkdir -p %t/foo/bar/ + +RUN: yaml2obj %S/Inputs/elf.yaml -o %t/foo/elf.o +RUN: cp %t/foo/elf.o %t/foo/bar/elf.o +RUN: cp %t/foo/bar/elf.o %t/delete.o + +Test that modules can be added with absolute paths when the archive is created using an absolute path + +RUN: llvm-ar rTc %t/absolute-1.ar %t/foo/elf.o %t/delete.o %t/foo/bar/elf.o +RUN: llvm-ar dT %t/absolute-1.ar delete.o + +RUN: FileCheck -input-file=%t/absolute-1.ar --check-prefixes=THIN,CHECK %s -DPATH=%/t/ +RUN: llvm-ar t %t/absolute-1.ar | FileCheck %s -DPATH=%/t/ + +Test that modules can be added with absolute paths when the archive is created using a relative path + +RUN: llvm-ar rTc Output/%basename_t.tmp/absolute-2.ar %t/foo/elf.o %t/delete.o %t/foo/bar/elf.o +RUN: llvm-ar dT Output/%basename_t.tmp/absolute-2.ar %t/delete.o + +RUN: FileCheck -input-file=%t/absolute-2.ar --check-prefixes=THIN,CHECK %s -DPATH=%/t/ +RUN: llvm-ar t %t/absolute-2.ar | FileCheck %s -DPATH=%/t/ + +These tests must be run in %t/foo. cd %t is included on each line to make debugging this test case easier. + +Test that modules can be added with relative paths when the archive is created using a relative path + +RUN: cd %t/foo && llvm-ar rTc ../relative-1.ar elf.o ../delete.o bar/elf.o +RUN: cd %t/foo && llvm-ar dT ../relative-1.ar delete.o + +RUN: FileCheck -input-file=%t/relative-1.ar --check-prefixes=THIN,CHECK %s -DPATH= +RUN: llvm-ar t %t/relative-1.ar | FileCheck %s -DPATH=%/t/ + +Test that modules can be added with relative paths when the archive is created using a absolute path + +RUN: cd %t/foo && llvm-ar rTc %t/relative-2.ar elf.o ../delete.o bar/elf.o +cd %t/foo && llvm-ar dT %t/relative-2.ar delete.o + +FileCheck -input-file=%t/relative-2.ar --check-prefixes=THIN,CHECK %s -DPATH= +llvm-ar t %t/relative-2.ar | FileCheck %s -DPATH=%/t/ + +THIN: ! + +CHECK-NOT: delete.o +CHECK: {{^}}[[PATH]]foo/elf.o +CHECK: {{^}}[[PATH]]foo/bar/elf.o \ No newline at end of file Index: tools/llvm-ar/llvm-ar.cpp =================================================================== --- tools/llvm-ar/llvm-ar.cpp +++ tools/llvm-ar/llvm-ar.cpp @@ -439,9 +439,11 @@ } if (C.getParent()->isThin()) { - StringRef ParentDir = sys::path::parent_path(ArchiveName); - if (!ParentDir.empty()) - outs() << ParentDir << '/'; + if (!sys::path::is_absolute(Name)) { + StringRef ParentDir = sys::path::parent_path(ArchiveName); + if (!ParentDir.empty()) + outs() << sys::path::convert_to_slash(ParentDir) << '/'; + } } outs() << Name << "\n"; } @@ -565,10 +567,20 @@ // the archive it's in, so the file resolves correctly. if (Thin && FlattenArchive) { StringSaver Saver(Alloc); + Expected NameOrErr = M.getName(); + failIfError(NameOrErr.takeError()); Expected FileNameOrErr = M.getFullName(); failIfError(FileNameOrErr.takeError()); - NMOrErr->MemberName = - Saver.save(computeArchiveRelativePath(ArchiveName, *FileNameOrErr)); + + std::string MemberPath; + if (sys::path::is_absolute(*NameOrErr) && + sys::path::is_absolute(*FileNameOrErr)) { + MemberPath = sys::path::convert_to_slash(*FileNameOrErr); + } else { + MemberPath = computeArchiveRelativePath(ArchiveName, *FileNameOrErr); + } + + NMOrErr->MemberName = Saver.save(MemberPath); } if (FlattenArchive && identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { @@ -597,9 +609,15 @@ // For regular archives, use the basename of the object path for the member // name. For thin archives, use the full relative paths so the file resolves // correctly. - NMOrErr->MemberName = - Thin ? Saver.save(computeArchiveRelativePath(ArchiveName, FileName)) - : sys::path::filename(NMOrErr->MemberName); + auto getThinMemberPath = [](StringRef FileName) -> std::string { + if (sys::path::is_absolute(FileName)) { + return sys::path::convert_to_slash(FileName); + } + return computeArchiveRelativePath(ArchiveName, FileName); + }; + + NMOrErr->MemberName = Thin ? Saver.save(getThinMemberPath(FileName)) + : sys::path::filename(NMOrErr->MemberName); if (FlattenArchive && identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { object::Archive &Lib = readLibrary(FileName);