Index: llvm/test/tools/llvm-ar/windows-path.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-ar/windows-path.test @@ -0,0 +1,44 @@ +# Test that windows path seperators are handled correctly +REQUIRES: system-windows + +# Note: many of these tests depend on relative paths, so we have to cd to a +# test directory first. +RUN: mkdir -p %t && cd %t +RUN: rm -rf a b && mkdir a b +RUN: echo hello-a > a/foo.txt +RUN: echo hello-b > b/foo.txt +RUN: echo hello-parent > foo.txt + +# P is implied when using thin archives. +# Create an archive. +RUN: rm -f archive.a +RUN: llvm-ar rcST archive.a a\foo.txt +RUN: llvm-ar rcST archive.a foo.txt +RUN: llvm-ar rcST archive.a b\foo.txt b/foo.txt +RUN: llvm-ar dT archive.a foo.txt +RUN: llvm-ar t archive.a | FileCheck %s --check-prefix=ARCHIVE --implicit-check-not {{.}} + +ARCHIVE: a/foo.txt +ARCHIVE-NEXT: b/foo.txt +ARCHIVE-NEXT: b/foo.txt + +RUN: llvm-ar t archive.a | FileCheck %s --check-prefix=LIST-BOTH --implicit-check-not {{.}} +RUN: llvm-ar t archive.a a\foo.txt | FileCheck %s --check-prefix=LIST-A --implicit-check-not {{.}} +RUN: llvm-ar t archive.a b\foo.txt | FileCheck %s --check-prefix=LIST-B --implicit-check-not {{.}} + +LIST-BOTH: a/foo.txt +LIST-BOTH-NEXT: b/foo.txt +LIST-BOTH-NEXT: b/foo.txt +LIST-A: a/foo.txt +LIST-B: b/foo.txt + +# Nesting a thin archive with a name conflict. +RUN: rm -f a\nested.a b\nested.a nested.a +RUN: llvm-ar rcST a\nested.a a\foo.txt +RUN: llvm-ar rcST b\nested.a b\foo.txt +RUN: llvm-ar rcST nested.a a\nested.a foo.txt b\nested.a +RUN: llvm-ar t nested.a | FileCheck %s --check-prefix=NESTED --implicit-check-not {{.}} + +NESTED: a/foo.txt +NESTED-NEXT: foo.txt +NESTED-NEXT: b/foo.txt Index: llvm/tools/llvm-ar/llvm-ar.cpp =================================================================== --- llvm/tools/llvm-ar/llvm-ar.cpp +++ llvm/tools/llvm-ar/llvm-ar.cpp @@ -474,8 +474,9 @@ outs() << Name << "\n"; } -static StringRef normalizePath(StringRef Path) { - return CompareFullPath ? Path : sys::path::filename(Path); +static std::string normalizePath(StringRef Path) { + return CompareFullPath ? sys::path::convert_to_slash(Path) + : sys::path::filename(Path); } // Implement the 'x' operation. This function extracts files back to the file @@ -698,9 +699,8 @@ return IA_MoveOldMember; if (Operation == ReplaceOrInsert) { - StringRef PosName = normalizePath(RelPos); if (!OnlyUpdate) { - if (PosName.empty()) + if (RelPos.empty()) return IA_AddNewMember; return IA_MoveNewMember; } @@ -712,12 +712,12 @@ auto ModTimeOrErr = Member.getLastModified(); failIfError(ModTimeOrErr.takeError()); if (Status.getLastModificationTime() < ModTimeOrErr.get()) { - if (PosName.empty()) + if (RelPos.empty()) return IA_AddOldMember; return IA_MoveOldMember; } - if (PosName.empty()) + if (RelPos.empty()) return IA_AddNewMember; return IA_MoveNewMember; } @@ -732,8 +732,8 @@ std::vector Ret; std::vector Moved; int InsertPos = -1; - StringRef PosName = normalizePath(RelPos); if (OldArchive) { + std::string PosName = normalizePath(RelPos); Error Err = Error::success(); StringMap MemberCount; for (auto &Child : OldArchive->children(Err)) { @@ -1009,7 +1009,7 @@ ArchiveName = Rest; break; case MRICommand::Delete: { - StringRef Name = normalizePath(Rest); + std::string Name = normalizePath(Rest); llvm::erase_if(NewMembers, [=](NewArchiveMember &M) { return M.MemberName == Name; }); break;