Index: llvm/trunk/test/tools/llvm-ar/Inputs/a-plus-b.a =================================================================== --- llvm/trunk/test/tools/llvm-ar/Inputs/a-plus-b.a +++ llvm/trunk/test/tools/llvm-ar/Inputs/a-plus-b.a @@ -0,0 +1,6 @@ +! +// 14 ` +a.txt/ +b.txt/ +/0 0 0 0 644 11 ` +/7 0 0 0 644 11 ` Index: llvm/trunk/test/tools/llvm-ar/Inputs/a.txt =================================================================== --- llvm/trunk/test/tools/llvm-ar/Inputs/a.txt +++ llvm/trunk/test/tools/llvm-ar/Inputs/a.txt @@ -0,0 +1 @@ +a-contents Index: llvm/trunk/test/tools/llvm-ar/Inputs/b.txt =================================================================== --- llvm/trunk/test/tools/llvm-ar/Inputs/b.txt +++ llvm/trunk/test/tools/llvm-ar/Inputs/b.txt @@ -0,0 +1 @@ +b-contents Index: llvm/trunk/test/tools/llvm-ar/Inputs/c.txt =================================================================== --- llvm/trunk/test/tools/llvm-ar/Inputs/c.txt +++ llvm/trunk/test/tools/llvm-ar/Inputs/c.txt @@ -0,0 +1 @@ +c-contents Index: llvm/trunk/test/tools/llvm-ar/Inputs/d.txt =================================================================== --- llvm/trunk/test/tools/llvm-ar/Inputs/d.txt +++ llvm/trunk/test/tools/llvm-ar/Inputs/d.txt @@ -0,0 +1 @@ +d-contents Index: llvm/trunk/test/tools/llvm-ar/Inputs/nested-thin-archive.a =================================================================== --- llvm/trunk/test/tools/llvm-ar/Inputs/nested-thin-archive.a +++ llvm/trunk/test/tools/llvm-ar/Inputs/nested-thin-archive.a @@ -0,0 +1,7 @@ +! +// 20 ` +a-plus-b.a/ +c.txt/ + +/0 0 0 0 644 202 ` +/12 0 0 0 644 11 ` Index: llvm/trunk/test/tools/llvm-ar/flatten-thin-archive-recursive.test =================================================================== --- llvm/trunk/test/tools/llvm-ar/flatten-thin-archive-recursive.test +++ llvm/trunk/test/tools/llvm-ar/flatten-thin-archive-recursive.test @@ -0,0 +1,13 @@ +# Since llvm-ar cannot create thin archives that contain any thin archives, +# nested-thin-archive.a is a manually constructed thin archive that contains +# another (unflattened) thin archive. +# This test ensures that flat archives are recursively flattened. + +RUN: rm -f %t.a +RUN: llvm-ar rcsT %t.a %S/Inputs/nested-thin-archive.a %S/Inputs/d.txt +RUN: llvm-ar t %t.a | FileCheck %s + +CHECK: a.txt +CHECK-NEXT: b.txt +CHECK-NEXT: c.txt +CHECK-NEXT: d.txt Index: llvm/trunk/test/tools/llvm-ar/flatten-thin-archive.test =================================================================== --- llvm/trunk/test/tools/llvm-ar/flatten-thin-archive.test +++ llvm/trunk/test/tools/llvm-ar/flatten-thin-archive.test @@ -0,0 +1,18 @@ +# This test creates a thin archive that contains a thin archive, a regular +# archive, and a file. +# +# The inner thin archive should be flattened, but the regular archive should +# not. The order of members in the archive should match the input order, with +# flattened members appearing together. + +RUN: touch %t-a.txt %t-b.txt %t-c.txt %t-d.txt %t-e.txt +RUN: rm -f %t-a-plus-b.a %t.a +RUN: llvm-ar rcsT %t-a-plus-b.a %t-a.txt %t-b.txt +RUN: llvm-ar rcs %t-d-plus-e.a %t-d.txt %t-e.txt +RUN: llvm-ar rcsT %t.a %t-a-plus-b.a %t-c.txt %t-d-plus-e.a +RUN: llvm-ar t %t.a | FileCheck %s + +CHECK: a.txt +CHECK-NEXT: b.txt +CHECK-NEXT: c.txt +CHECK-NEXT: -d-plus-e.a Index: llvm/trunk/tools/llvm-ar/llvm-ar.cpp =================================================================== --- llvm/trunk/tools/llvm-ar/llvm-ar.cpp +++ llvm/trunk/tools/llvm-ar/llvm-ar.cpp @@ -536,52 +536,53 @@ exit(1); } -static void addMember(std::vector &Members, - StringRef FileName, int Pos = -1) { - Expected NMOrErr = - NewArchiveMember::getFile(FileName, Deterministic); - failIfError(NMOrErr.takeError(), FileName); - - // Use the basename of the object path for the member name. - NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName); - - if (Pos == -1) - Members.push_back(std::move(*NMOrErr)); - else - Members[Pos] = std::move(*NMOrErr); -} - -static void addMember(std::vector &Members, - const object::Archive::Child &M, int Pos = -1) { +static void addChildMember(std::vector &Members, + const object::Archive::Child &M, + bool FlattenArchive = false) { if (Thin && !M.getParent()->isThin()) fail("Cannot convert a regular archive to a thin one"); Expected NMOrErr = NewArchiveMember::getOldMember(M, Deterministic); failIfError(NMOrErr.takeError()); - if (Pos == -1) - Members.push_back(std::move(*NMOrErr)); - else - Members[Pos] = std::move(*NMOrErr); + if (FlattenArchive && + identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { + Expected FileNameOrErr = M.getName(); + failIfError(FileNameOrErr.takeError()); + object::Archive &Lib = readLibrary(*FileNameOrErr); + // When creating thin archives, only flatten if the member is also thin. + if (!Thin || Lib.isThin()) { + Error Err = Error::success(); + // Only Thin archives are recursively flattened. + for (auto &Child : Lib.children(Err)) + addChildMember(Members, Child, /*FlattenArchive=*/Thin); + failIfError(std::move(Err)); + return; + } + } + Members.push_back(std::move(*NMOrErr)); } -static void addLibMember(std::vector &Members, - StringRef FileName) { +static void addMember(std::vector &Members, + StringRef FileName, bool FlattenArchive = false) { Expected NMOrErr = NewArchiveMember::getFile(FileName, Deterministic); failIfError(NMOrErr.takeError(), FileName); - if (identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { + if (FlattenArchive && + identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { object::Archive &Lib = readLibrary(FileName); - Error Err = Error::success(); - - for (auto &Child : Lib.children(Err)) - addMember(Members, Child); - - failIfError(std::move(Err)); - } else { - // Use the basename of the object path for the member name. - NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName); - Members.push_back(std::move(*NMOrErr)); + // When creating thin archives, only flatten if the member is also thin. + if (!Thin || Lib.isThin()) { + Error Err = Error::success(); + // Only Thin archives are recursively flattened. + for (auto &Child : Lib.children(Err)) + addChildMember(Members, Child, /*FlattenArchive=*/Thin); + failIfError(std::move(Err)); + return; + } } + // Use the basename of the object path for the member name. + NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName); + Members.push_back(std::move(*NMOrErr)); } enum InsertAction { @@ -670,7 +671,7 @@ computeInsertAction(Operation, Child, Name, MemberI); switch (Action) { case IA_AddOldMember: - addMember(Ret, Child); + addChildMember(Ret, Child); break; case IA_AddNewMember: addMember(Ret, *MemberI); @@ -678,7 +679,7 @@ case IA_Delete: break; case IA_MoveOldMember: - addMember(Moved, Child); + addChildMember(Moved, Child); break; case IA_MoveNewMember: addMember(Moved, *MemberI); @@ -709,17 +710,16 @@ if (AddLibrary) { assert(Operation == QuickAppend); for (auto &Member : Members) - addLibMember(Ret, Member); + addMember(Ret, Member, /*FlattenArchive=*/true); return Ret; } - for (unsigned I = 0; I != Members.size(); ++I) - Ret.insert(Ret.begin() + InsertPos, NewArchiveMember()); - Pos = InsertPos; - for (auto &Member : Members) { - addMember(Ret, Member, Pos); - ++Pos; - } + std::vector NewMembers; + for (auto &Member : Members) + addMember(NewMembers, Member, /*FlattenArchive=*/Thin); + Ret.reserve(Ret.size() + NewMembers.size()); + std::move(NewMembers.begin(), NewMembers.end(), + std::inserter(Ret, std::next(Ret.begin(), InsertPos))); return Ret; } @@ -897,7 +897,7 @@ { Error Err = Error::success(); for (auto &Member : Lib.children(Err)) - addMember(NewMembers, Member); + addChildMember(NewMembers, Member); failIfError(std::move(Err)); } break;