Index: test/tools/llvm-lipo/create-arch.test =================================================================== --- test/tools/llvm-lipo/create-arch.test +++ test/tools/llvm-lipo/create-arch.test @@ -0,0 +1,17 @@ +# RUN: yaml2obj %p/Inputs/i386-slice.yaml > %t-i386.o +# RUN: yaml2obj %p/Inputs/x86_64-slice.yaml > %t-x86_64.o + +# RUN: llvm-lipo %t-i386.o %t-x86_64.o -create -output %t-universal.o +# RUN: llvm-lipo %t-i386.o -arch x86_64 %t-x86_64.o -create -output %t-universal-1.o +# RUN: cmp %t-universal.o %t-universal-1.o +# RUN: llvm-lipo -arch i386 %t-i386.o -arch x86_64 %t-x86_64.o -create -output %t-universal-2.o +# RUN: cmp %t-universal.o %t-universal-2.o +# +# RUN: not llvm-lipo -arch armv7 %t-i386.o -arch x86_64 %t-x86_64.o -create -output /dev/null 2>&1 | FileCheck --check-prefix=ARCH_NOT_MATCHED %s +# ARCH_NOT_MATCHED: error: specified architecture: armv7 for file: {{.*}} does not match the file's architecture (i386) +# +# # RUN: not llvm-lipo -arch i3866 %t-32.o -create -o /dev/null 2>&1 | FileCheck --check-prefix=INVALID_ARCH %s +# INVALID_ARCH: error: Invalid architecture: i3866 +# +# RUN: not llvm-lipo -arch i386 %t-33.o -create -o /dev/null 2>&1 | FileCheck --check-prefix=INVALID_FILE %s +# INVALID_FILE: {{[nN]}}o such file or directory Index: test/tools/llvm-lipo/replace-invalid-input.test =================================================================== --- test/tools/llvm-lipo/replace-invalid-input.test +++ test/tools/llvm-lipo/replace-invalid-input.test @@ -5,20 +5,20 @@ # RUN: not llvm-lipo %t-universal.o -replace %t-32.o 2>&1 | FileCheck --check-prefix=MISSING_ARG %s # MISSING_ARG: error: replace is missing an argument: expects -replace arch_type file_name -# RUN: not llvm-lipo %t-universal.o -replace %t-32.o i386 2>&1 | FileCheck --check-prefix=OUTPUT_FILE %s +# RUN: not llvm-lipo %t-universal.o -replace i386 %t-32.o 2>&1 | FileCheck --check-prefix=OUTPUT_FILE %s # OUTPUT_FILE: error: replace expects a single output file to be specified -# RUN: not llvm-lipo %t-universal.o %t-universal.o -replace %t-32.o i386 -o %t.o 2>&1 | FileCheck --check-prefix=INPUT_ARGS %s +# RUN: not llvm-lipo %t-universal.o %t-universal.o -replace i386 %t-32.o -o %t.o 2>&1 | FileCheck --check-prefix=INPUT_ARGS %s # INPUT_ARGS: error: replace expects a single input file -# RUN: not llvm-lipo %t-universal.o -replace %t-32.o i386 -o %t.o 2>&1 | FileCheck --check-prefix=INVALID_FILE %s -# INVALID_FILE: error: 'i386': {{[nN]}}o such file or directory +# RUN: not llvm-lipo %t-universal.o -replace i386 %t-33.o -o %t.o 2>&1 | FileCheck --check-prefix=INVALID_FILE %s +# INVALID_FILE: {{[nN]}}o such file or directory # RUN: not llvm-lipo %t-universal.o -replace i3866 %t-32.o -o %t.o 2>&1 | FileCheck --check-prefix=INVALID_ARCH %s # INVALID_ARCH: error: Invalid architecture: i3866 # RUN: not llvm-lipo %t-universal.o -replace arm64 %t-32.o -o %t.o 2>&1 | FileCheck --check-prefix=ARCH_NOT_MATCHED %s -# ARCH_NOT_MATCHED: error: specified architecture: arm64 for replacement file: {{.*}} does not match the file's architecture +# ARCH_NOT_MATCHED: error: specified architecture: arm64 for file: {{.*}} does not match the file's architecture (i386) # RUN: not llvm-lipo %t-universal.o -replace arm64 %t-arm64.o -o %t.o 2>&1 | FileCheck --check-prefix=ARCH_NOT_IN_INPUT %s # ARCH_NOT_IN_INPUT: error: -replace arm64 specified but fat file: {{.*}} does not contain that architecture Index: tools/llvm-lipo/LipoOpts.td =================================================================== --- tools/llvm-lipo/LipoOpts.td +++ tools/llvm-lipo/LipoOpts.td @@ -12,6 +12,10 @@ "architecture when creating a universal binary file. The " "alignment is a hexadecimal number that is a power of 2.">; +def arch + : MultiArg<["-", "--"], "arch", 2>, + HelpText<"Specifies the architecture and the corresponding input file">; + def action_group : OptionGroup<"action group">; def verify_arch Index: tools/llvm-lipo/llvm-lipo.cpp =================================================================== --- tools/llvm-lipo/llvm-lipo.cpp +++ tools/llvm-lipo/llvm-lipo.cpp @@ -86,15 +86,15 @@ ReplaceArch, }; -struct Replacement { - StringRef ArchType; +struct InputFile { + Optional ArchType; StringRef FileName; }; struct Config { - SmallVector InputFiles; + SmallVector InputFiles; SmallVector VerifyArchList; - SmallVector Replacements; + SmallVector ReplacementFiles; StringMap SegmentAlignments; std::string ThinArchType; std::string OutputFile; @@ -301,7 +301,15 @@ reportError("unknown argument '" + Arg->getAsString(InputArgs) + "'"); for (auto Arg : InputArgs.filtered(LIPO_INPUT)) - C.InputFiles.push_back(Arg->getValue()); + C.InputFiles.push_back({None, Arg->getValue()}); + for (auto Arg : InputArgs.filtered(LIPO_arch)) { + validateArchitectureName(Arg->getValue(0)); + if (!Arg->getValue(1)) + reportError( + "arch is missing an argument: expects -arch arch_type file_name"); + C.InputFiles.push_back({StringRef(Arg->getValue(0)), Arg->getValue(1)}); + } + if (C.InputFiles.empty()) reportError("at least one input file should be specified"); @@ -402,8 +410,9 @@ reportError( "replace is missing an argument: expects -replace arch_type " "file_name"); - C.Replacements.push_back( - Replacement{Action->getValue(0), Action->getValue(1)}); + validateArchitectureName(Action->getValue(0)); + C.ReplacementFiles.push_back( + {StringRef(Action->getValue(0)), Action->getValue(1)}); } if (C.OutputFile.empty()) @@ -419,16 +428,25 @@ } static SmallVector, 1> -readInputBinaries(ArrayRef InputFiles) { +readInputBinaries(ArrayRef InputFiles) { SmallVector, 1> InputBinaries; - for (StringRef InputFile : InputFiles) { - Expected> BinaryOrErr = createBinary(InputFile); + for (const InputFile &IF : InputFiles) { + Expected> BinaryOrErr = createBinary(IF.FileName); if (!BinaryOrErr) - reportError(InputFile, BinaryOrErr.takeError()); - if (!BinaryOrErr->getBinary()->isArchive() && - !BinaryOrErr->getBinary()->isMachO() && - !BinaryOrErr->getBinary()->isMachOUniversalBinary()) - reportError("File " + InputFile + " has unsupported binary format"); + reportError(IF.FileName, BinaryOrErr.takeError()); + const Binary *B = BinaryOrErr->getBinary(); + if (!B->isArchive() && !B->isMachO() && !B->isMachOUniversalBinary()) + reportError("File " + IF.FileName + " has unsupported binary format"); + if (IF.ArchType && (B->isMachO() || B->isArchive())) { + const auto ArchType = + B->isMachO() ? Slice(cast(B)).getArchString() + : Slice(cast(B)).getArchString(); + if (Triple(*IF.ArchType).getArch() != Triple(ArchType).getArch()) + reportError("specified architecture: " + *IF.ArchType + + " for file: " + B->getFileName() + + " does not match the file's architecture (" + ArchType + + ")"); + } InputBinaries.push_back(std::move(*BinaryOrErr)); } return InputBinaries; @@ -716,32 +734,20 @@ static StringMap buildReplacementSlices(ArrayRef> ReplacementBinaries, - const StringMap &Alignments, - ArrayRef Replacements) { - assert(ReplacementBinaries.size() == Replacements.size() && - "Number of replacment binaries does not match the number of " - "replacements"); + const StringMap &Alignments) { StringMap Slices; // populates StringMap of slices to replace with; error checks for mismatched // replace flag args, fat files, and duplicate arch_types - for (size_t Index = 0, Size = Replacements.size(); Index < Size; ++Index) { - StringRef ReplacementArch = Replacements[Index].ArchType; - const Binary *ReplacementBinary = ReplacementBinaries[Index].getBinary(); - validateArchitectureName(ReplacementArch); - + for (const auto &OB : ReplacementBinaries) { + const Binary *ReplacementBinary = OB.getBinary(); auto O = dyn_cast(ReplacementBinary); if (!O) reportError("replacement file: " + ReplacementBinary->getFileName() + " is a fat file (must be a thin file)"); - - if (O->getArch() != Triple(ReplacementArch).getArch()) - reportError("specified architecture: " + ReplacementArch + - " for replacement file: " + ReplacementBinary->getFileName() + - " does not match the file's architecture"); - - auto Entry = Slices.try_emplace(ReplacementArch, Slice(O)); + Slice S(O); + auto Entry = Slices.try_emplace(S.getArchString(), S); if (!Entry.second) - reportError("-replace " + ReplacementArch + + reportError("-replace " + S.getArchString() + " specified multiple times: " + Entry.first->second.getBinary()->getFileName() + ", " + O->getFileName()); @@ -756,7 +762,7 @@ static void replaceSlices(ArrayRef> InputBinaries, const StringMap &Alignments, StringRef OutputFileName, - ArrayRef Replacements) { + ArrayRef ReplacementFiles) { assert(InputBinaries.size() == 1 && "Incorrect number of input binaries"); assert(!OutputFileName.empty() && "Replace expects a single output file"); @@ -765,14 +771,11 @@ InputBinaries.front().getBinary()->getFileName() + " must be a fat file when the -replace option is specified"); - SmallVector ReplacementFiles; - for (const auto &R : Replacements) - ReplacementFiles.push_back(R.FileName); SmallVector, 1> ReplacementBinaries = readInputBinaries(ReplacementFiles); StringMap ReplacementSlices = - buildReplacementSlices(ReplacementBinaries, Alignments, Replacements); + buildReplacementSlices(ReplacementBinaries, Alignments); SmallVector, 2> ExtractedObjects; SmallVector Slices = buildSlices(InputBinaries, Alignments, ExtractedObjects); @@ -820,7 +823,7 @@ break; case LipoAction::ReplaceArch: replaceSlices(InputBinaries, C.SegmentAlignments, C.OutputFile, - C.Replacements); + C.ReplacementFiles); break; } return EXIT_SUCCESS;