Index: include/llvm/Object/ArchiveWriter.h =================================================================== --- include/llvm/Object/ArchiveWriter.h +++ include/llvm/Object/ArchiveWriter.h @@ -36,7 +36,8 @@ bool Deterministic); }; -std::string computeArchiveRelativePath(StringRef From, StringRef To); +std::string computeArchiveRelativePath(StringRef From, StringRef To, + bool ForceRelative = false); Error writeArchive(StringRef ArcName, ArrayRef NewMembers, bool WriteSymtab, object::Archive::Kind Kind, Index: lib/Object/ArchiveWriter.cpp =================================================================== --- lib/Object/ArchiveWriter.cpp +++ lib/Object/ArchiveWriter.cpp @@ -495,28 +495,51 @@ 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; - - StringRef DirFrom = sys::path::parent_path(From); - auto FromI = sys::path::begin(DirFrom); - auto ToI = sys::path::begin(To); +std::string computeArchiveRelativePath(StringRef From, StringRef To, + bool ForceRelative) { + if (!ForceRelative && sys::path::is_absolute(To)) + return sys::path::convert_to_slash(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; + }; + + 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 +RUN: cd %t/foo && llvm-ar dT %t/relative-2.ar delete.o + +RUN: FileCheck -input-file=%t/relative-2.ar --check-prefixes=THIN,CHECK %s -DPATH= +RUN: 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 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,12 @@ // 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)); + NMOrErr->MemberName = Saver.save(computeArchiveRelativePath( + ArchiveName, *FileNameOrErr, sys::path::is_relative(*NameOrErr))); } if (FlattenArchive && identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) {