[lipo] add -remove flag (#188275)
Add the -remove flag to llvm-lipo. This matches the existing Darwin lipo
tool:
```
% xcrun lipo 2>&1 | grep remove
error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/lipo: one of -create, -thin <arch_type>, -extract <arch_type>, -remove <arch_type>, -replace <arch_type> <file_name>, -verify_arch <arch_type> ... , -archs, -info, or -detailed_info must be specified
-remove <arch_type> [-remove <arch_type> ...]
```
Assisted-by: claude-code
This commit is contained in:
39
llvm/test/tools/llvm-lipo/remove.test
Normal file
39
llvm/test/tools/llvm-lipo/remove.test
Normal file
@@ -0,0 +1,39 @@
|
||||
# RUN: yaml2obj %p/Inputs/i386-x86_64-universal.yaml -o %t-universal.o
|
||||
|
||||
# RUN: not llvm-lipo %t-universal.o -remove arm64_32 -output /dev/null 2>&1 | FileCheck --check-prefix=ARCH_NOT_IN_FILE %s
|
||||
# ARCH_NOT_IN_FILE: does not contain the specified architecture arm64_32 to remove
|
||||
|
||||
# RUN: yaml2obj %p/Inputs/i386-slice.yaml -o %t-i386.o
|
||||
# RUN: not llvm-lipo %t-i386.o -remove i386 -output /dev/null 2>&1 | FileCheck --check-prefix=INPUT_NOT_A_FAT_FILE %s
|
||||
# INPUT_NOT_A_FAT_FILE: must be a fat file when the -remove option is specified
|
||||
|
||||
# RUN: not llvm-lipo -remove FakeArch %t-universal.o -output /dev/null 2>&1 | FileCheck --check-prefix=INVALID_ARCH %s
|
||||
# INVALID_ARCH: Invalid architecture: FakeArch
|
||||
|
||||
# RUN: yaml2obj %p/Inputs/i386-x86_64-armv7-arm64-universal.yaml -o %t-4arch.o
|
||||
# RUN: llvm-lipo -remove i386 %t-4arch.o -output %t-removed.o
|
||||
# RUN: llvm-lipo -info %t-removed.o | FileCheck --check-prefix=REMOVE_ONE %s
|
||||
# REMOVE_ONE: x86_64 armv7 arm64
|
||||
|
||||
# RUN: llvm-lipo -remove i386 %t-universal.o -output %t-only-x86_64.o
|
||||
# RUN: llvm-lipo -thin x86_64 %t-universal.o -output %t-thinned-x86_64.o
|
||||
# RUN: yaml2obj %p/Inputs/x86_64-slice.yaml -o %t-x86_64.o
|
||||
# RUN: cmp %t-thinned-x86_64.o %t-x86_64.o
|
||||
|
||||
# Multiple -remove flags
|
||||
# RUN: llvm-lipo -remove i386 -remove armv7 %t-4arch.o -output %t-removed-multi.o
|
||||
# RUN: llvm-lipo -info %t-removed-multi.o | FileCheck --check-prefix=REMOVE_MULTI %s
|
||||
# REMOVE_MULTI: x86_64 arm64
|
||||
|
||||
# Multiple -remove flags removing all but one arch
|
||||
# RUN: llvm-lipo -remove i386 -remove x86_64 -remove armv7 %t-4arch.o -output %t-removed-three.o
|
||||
# RUN: llvm-lipo -info %t-removed-three.o | FileCheck --check-prefix=REMOVE_THREE %s
|
||||
# REMOVE_THREE: arm64
|
||||
|
||||
# Error: one of multiple -remove arches not in file
|
||||
# RUN: not llvm-lipo -remove i386 -remove arm64_32 %t-4arch.o -output /dev/null 2>&1 | FileCheck --check-prefix=MULTI_ARCH_NOT_IN_FILE %s
|
||||
# MULTI_ARCH_NOT_IN_FILE: does not contain the specified architecture arm64_32 to remove
|
||||
|
||||
# Error: removing all arches
|
||||
# RUN: not llvm-lipo -remove i386 -remove x86_64 %t-universal.o -output /dev/null 2>&1 | FileCheck --check-prefix=REMOVE_ALL %s
|
||||
# REMOVE_ALL: removing all architectures would result in an empty universal binary
|
||||
@@ -44,6 +44,11 @@ def extract : Option<["-", "--"], "extract", KIND_SEPARATE>,
|
||||
HelpText<"Create a universal output file containing only the specified "
|
||||
"arch_type from the fat input file. Requires -output option">;
|
||||
|
||||
def remove : Option<["-", "--"], "remove", KIND_SEPARATE>,
|
||||
Group<action_group>,
|
||||
HelpText<"Remove the specified arch_type from the universal input "
|
||||
"file. Requires -output option">;
|
||||
|
||||
def create : Option<["-", "--"], "create", KIND_FLAG>,
|
||||
Group<action_group>,
|
||||
HelpText<"Create a universal binary output file from the input "
|
||||
|
||||
@@ -99,6 +99,7 @@ enum class LipoAction {
|
||||
VerifyArch,
|
||||
ThinArch,
|
||||
ExtractArch,
|
||||
RemoveArch,
|
||||
CreateUniversal,
|
||||
ReplaceArch,
|
||||
};
|
||||
@@ -112,6 +113,7 @@ struct Config {
|
||||
SmallVector<InputFile, 1> InputFiles;
|
||||
SmallVector<std::string, 1> VerifyArchList;
|
||||
SmallVector<InputFile, 1> ReplacementFiles;
|
||||
SmallVector<std::string, 1> RemoveArchList;
|
||||
StringMap<const uint32_t> SegmentAlignments;
|
||||
std::string ArchType;
|
||||
std::string OutputFile;
|
||||
@@ -229,14 +231,18 @@ static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {
|
||||
SmallVector<opt::Arg *, 1> ActionArgs(InputArgs.filtered(LIPO_action_group));
|
||||
if (ActionArgs.empty())
|
||||
reportError("at least one action should be specified");
|
||||
// errors if multiple actions specified other than replace
|
||||
// multiple replace flags may be specified, as long as they are not mixed with
|
||||
// other action flags
|
||||
// errors if multiple actions specified other than replace or remove
|
||||
// multiple replace/remove flags may be specified, as long as they are not
|
||||
// mixed with other action flags
|
||||
auto ReplacementArgsRange = InputArgs.filtered(LIPO_replace);
|
||||
auto RemoveArgsRange = InputArgs.filtered(LIPO_remove);
|
||||
if (ActionArgs.size() > 1 &&
|
||||
ActionArgs.size() !=
|
||||
static_cast<size_t>(std::distance(ReplacementArgsRange.begin(),
|
||||
ReplacementArgsRange.end()))) {
|
||||
ReplacementArgsRange.end())) &&
|
||||
ActionArgs.size() !=
|
||||
static_cast<size_t>(
|
||||
std::distance(RemoveArgsRange.begin(), RemoveArgsRange.end()))) {
|
||||
std::string Buf;
|
||||
raw_string_ostream OS(Buf);
|
||||
OS << "only one of the following actions can be specified:";
|
||||
@@ -287,6 +293,19 @@ static Config parseLipoOptions(ArrayRef<const char *> ArgsArr) {
|
||||
C.ActionToPerform = LipoAction::ExtractArch;
|
||||
return C;
|
||||
|
||||
case LIPO_remove:
|
||||
for (auto *Action : ActionArgs) {
|
||||
std::string ArchType = Action->getValue();
|
||||
validateArchitectureName(ArchType);
|
||||
C.RemoveArchList.push_back(ArchType);
|
||||
}
|
||||
if (C.InputFiles.size() > 1)
|
||||
reportError("remove expects a single input file");
|
||||
if (C.OutputFile.empty())
|
||||
reportError("remove expects a single output file");
|
||||
C.ActionToPerform = LipoAction::RemoveArch;
|
||||
return C;
|
||||
|
||||
case LIPO_create:
|
||||
if (C.OutputFile.empty())
|
||||
reportError("create expects a single output file to be specified");
|
||||
@@ -672,6 +691,52 @@ extractSlice(LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries,
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
[[noreturn]] static void
|
||||
removeSlice(LLVMContext &LLVMCtx, ArrayRef<OwningBinary<Binary>> InputBinaries,
|
||||
const StringMap<const uint32_t> &Alignments,
|
||||
ArrayRef<std::string> ArchTypes, StringRef OutputFileName) {
|
||||
assert(!ArchTypes.empty() &&
|
||||
"The architecture type list should be non-empty");
|
||||
assert(InputBinaries.size() == 1 && "Incorrect number of input binaries");
|
||||
assert(!OutputFileName.empty() && "Remove expects a single output file");
|
||||
|
||||
if (InputBinaries.front().getBinary()->isMachO()) {
|
||||
reportError("input file " +
|
||||
InputBinaries.front().getBinary()->getFileName() +
|
||||
" must be a fat file when the -remove option is specified");
|
||||
}
|
||||
|
||||
SmallVector<std::unique_ptr<SymbolicFile>, 2> ExtractedObjects;
|
||||
SmallVector<std::unique_ptr<Archive>, 2> ExtractedArchives;
|
||||
SmallVector<Slice, 2> Slices = buildSlices(
|
||||
LLVMCtx, InputBinaries, Alignments, ExtractedObjects, ExtractedArchives);
|
||||
|
||||
SmallVector<StringRef, 1> NotFound;
|
||||
for (StringRef ArchType : ArchTypes) {
|
||||
size_t SizeBefore = Slices.size();
|
||||
erase_if(Slices, [ArchType](const Slice &S) {
|
||||
return ArchType == S.getArchString();
|
||||
});
|
||||
if (Slices.size() == SizeBefore)
|
||||
NotFound.push_back(ArchType);
|
||||
}
|
||||
|
||||
if (!NotFound.empty())
|
||||
reportError("fat input file " +
|
||||
InputBinaries.front().getBinary()->getFileName() +
|
||||
" does not contain the specified architecture " + NotFound[0] +
|
||||
" to remove");
|
||||
|
||||
if (Slices.empty())
|
||||
reportError(
|
||||
"removing all architectures would result in an empty universal binary");
|
||||
|
||||
llvm::stable_sort(Slices);
|
||||
if (Error E = writeUniversalBinary(Slices, OutputFileName))
|
||||
reportError(std::move(E));
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static StringMap<Slice>
|
||||
buildReplacementSlices(ArrayRef<OwningBinary<Binary>> ReplacementBinaries,
|
||||
const StringMap<const uint32_t> &Alignments) {
|
||||
@@ -770,6 +835,10 @@ int llvm_lipo_main(int argc, char **argv, const llvm::ToolContext &) {
|
||||
extractSlice(LLVMCtx, InputBinaries, C.SegmentAlignments, C.ArchType,
|
||||
C.OutputFile);
|
||||
break;
|
||||
case LipoAction::RemoveArch:
|
||||
removeSlice(LLVMCtx, InputBinaries, C.SegmentAlignments, C.RemoveArchList,
|
||||
C.OutputFile);
|
||||
break;
|
||||
case LipoAction::CreateUniversal:
|
||||
createUniversalBinary(
|
||||
LLVMCtx, InputBinaries, C.SegmentAlignments, C.OutputFile,
|
||||
|
||||
Reference in New Issue
Block a user