diff --git a/llvm/docs/CommandGuide/llvm-libtool-darwin.rst b/llvm/docs/CommandGuide/llvm-libtool-darwin.rst --- a/llvm/docs/CommandGuide/llvm-libtool-darwin.rst +++ b/llvm/docs/CommandGuide/llvm-libtool-darwin.rst @@ -26,10 +26,6 @@ Build a static library only for the specified `` and ignore all other architectures in the files. -.. option:: -color - - Use colors in output. - .. option:: -D Use zero for timestamps and UIDs/GIDs. This is set by default. @@ -45,19 +41,14 @@ Show help and usage for this command. -.. option:: -help-list - - Show help and usage for this command without grouping the options - into categories. - -.. option:: -l +.. option:: -l Searches for the library libx.a in the library search path. If the string `` ends with '.o', then the library 'x' is searched for without prepending 'lib' or appending '.a'. If the library is found, it is added to the list of input files. Otherwise, an error is raised. -.. option:: -L +.. option:: -L Adds `` to the list of directories in which to search for libraries. The directories are searched in the order in which they are specified with diff --git a/llvm/test/tools/llvm-libtool-darwin/help-message.test b/llvm/test/tools/llvm-libtool-darwin/help-message.test --- a/llvm/test/tools/llvm-libtool-darwin/help-message.test +++ b/llvm/test/tools/llvm-libtool-darwin/help-message.test @@ -1,13 +1,11 @@ ## This test checks that the help message is displayed correctly. # RUN: llvm-libtool-darwin -h | \ -# RUN: FileCheck --check-prefixes=LIBTOOL-USAGE,CATEG %s --match-full-lines --implicit-check-not="General options:" +# RUN: FileCheck --check-prefixes=LIBTOOL-USAGE %s --match-full-lines --implicit-check-not="General options:" # RUN: llvm-libtool-darwin -help | \ -# RUN: FileCheck --check-prefixes=LIBTOOL-USAGE,CATEG %s --match-full-lines --implicit-check-not="General options:" +# RUN: FileCheck --check-prefixes=LIBTOOL-USAGE %s --match-full-lines --implicit-check-not="General options:" # RUN: llvm-libtool-darwin --help | \ -# RUN: FileCheck --check-prefixes=LIBTOOL-USAGE,CATEG %s --match-full-lines --implicit-check-not="General options:" -# RUN: llvm-libtool-darwin --help-list | \ -# RUN: FileCheck -check-prefixes=LIBTOOL-USAGE,LIST %s --match-full-lines --implicit-check-not="safepoint-ir-verifier-print-only" +# RUN: FileCheck --check-prefixes=LIBTOOL-USAGE %s --match-full-lines --implicit-check-not="General options:" # RUN: not llvm-libtool-darwin -abcabc 2>&1 | FileCheck --check-prefix=UNKNOWN-ARG %s # RUN: not llvm-libtool-darwin --abcabc 2>&1 | FileCheck --check-prefix=UNKNOWN-ARG %s @@ -16,12 +14,4 @@ # LIBTOOL-USAGE: USAGE: llvm-libtool-darwin{{(\.exe)?}} [options] # LIBTOOL-USAGE: OPTIONS: -# CATEG: Color Options: -# LIST-NOT: Color Options: -# CATEG: Generic Options: -# LIST-NOT: Generic Options: -# CATEG: llvm-libtool-darwin Options: -# LIST-NOT: llvm-libtool-darwin Options: -# LIST-NOT: General options: - -# UNKNOWN-ARG: Unknown command line argument '{{-+}}abcabc' +# UNKNOWN-ARG: unknown argument '{{-+}}abcabc' diff --git a/llvm/test/tools/llvm-libtool-darwin/invalid-input-output-args.test b/llvm/test/tools/llvm-libtool-darwin/invalid-input-output-args.test --- a/llvm/test/tools/llvm-libtool-darwin/invalid-input-output-args.test +++ b/llvm/test/tools/llvm-libtool-darwin/invalid-input-output-args.test @@ -10,13 +10,13 @@ # RUN: not llvm-libtool-darwin -static %t.input 2>&1 | \ # RUN: FileCheck %s --check-prefix=NO-OUTPUT -# NO-OUTPUT: for the -o option: must be specified +# NO-OUTPUT: -o option: must be specified ## Missing argument to -o: # RUN: not llvm-libtool-darwin -static %t.input -o 2>&1 | \ # RUN: FileCheck %s --check-prefix=MISSING -# MISSING: for the -o option: requires a value! +# MISSING: -o: missing argument ## Input file not found: # RUN: not llvm-libtool-darwin -static -o %t.lib %t.missing 2>&1 | \ diff --git a/llvm/test/tools/llvm-libtool-darwin/missing-library-type.test b/llvm/test/tools/llvm-libtool-darwin/missing-library-type.test --- a/llvm/test/tools/llvm-libtool-darwin/missing-library-type.test +++ b/llvm/test/tools/llvm-libtool-darwin/missing-library-type.test @@ -2,4 +2,4 @@ # RUN: not llvm-libtool-darwin -o %t.lib %t.input 2>&1 | \ # RUN: FileCheck %s --check-prefix=MISSING-OPERATION -# MISSING-OPERATION: Library Type: option: must be specified +# MISSING-OPERATION: -static option: must be specified diff --git a/llvm/test/tools/llvm-libtool-darwin/universal-object-flattening.test b/llvm/test/tools/llvm-libtool-darwin/universal-object-flattening.test --- a/llvm/test/tools/llvm-libtool-darwin/universal-object-flattening.test +++ b/llvm/test/tools/llvm-libtool-darwin/universal-object-flattening.test @@ -104,7 +104,7 @@ # RUN: not llvm-libtool-darwin -static -o %t.lib %t-universal.o -arch_only 2>&1 | \ # RUN: FileCheck %s --check-prefix=REQUIRE-ARCH -# REQUIRE-ARCH: for the --arch_only option: requires a value! +# REQUIRE-ARCH: -arch_only: missing argument ## x86_64-arm64-universal.yaml --- !fat-mach-o diff --git a/llvm/test/tools/llvm-libtool-darwin/version.test b/llvm/test/tools/llvm-libtool-darwin/version.test --- a/llvm/test/tools/llvm-libtool-darwin/version.test +++ b/llvm/test/tools/llvm-libtool-darwin/version.test @@ -15,7 +15,7 @@ ## Regular errors should occur when an operation is specified. # RUN: not llvm-libtool-darwin -V -static 2>&1 | FileCheck --check-prefix=ERROR %s -# ERROR: for the -o option: must be specified +# ERROR: -o option: must be specified ## A valid command line should print the version and perform the operation. # RUN: yaml2obj %S/Inputs/input1.yaml -o %t-input1.o diff --git a/llvm/tools/llvm-libtool-darwin/CMakeLists.txt b/llvm/tools/llvm-libtool-darwin/CMakeLists.txt --- a/llvm/tools/llvm-libtool-darwin/CMakeLists.txt +++ b/llvm/tools/llvm-libtool-darwin/CMakeLists.txt @@ -2,14 +2,22 @@ BinaryFormat Core Object + Option Support TargetParser TextAPI ${LLVM_TARGETS_TO_BUILD} ) +set(LLVM_TARGET_DEFINITIONS Opts.td) +tablegen(LLVM Opts.inc -gen-opt-parser-defs) +add_public_tablegen_target(LibtoolDarwinOptsTableGen) + add_llvm_tool(llvm-libtool-darwin llvm-libtool-darwin.cpp + + DEPENDS + LibtoolDarwinOptsTableGen ) if(LLVM_INSTALL_CCTOOLS_SYMLINKS) diff --git a/llvm/tools/llvm-libtool-darwin/Opts.td b/llvm/tools/llvm-libtool-darwin/Opts.td new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-libtool-darwin/Opts.td @@ -0,0 +1,25 @@ +include "llvm/Option/OptParser.td" + +class F : Flag<["-", "--"], letter>, HelpText; +class FF : Flag<["-", "--"], name>, HelpText; +class S : Separate<["-", "--"], letter>, HelpText; +class SS : Separate<["-", "--"], name>, HelpText; +class JS : JoinedOrSeparate<["-", "--"], letter>, HelpText; + +def help : FF<"help", "Display this help">; +def : F<"h", "">, Alias; +def deterministicOption : F<"D","Use zero for timestamps and UIDs/GIDs (Default)">; +def nonDeterministicOption : F<"U", "Use actual timestamps and UIDs/GIDs">; +def version : FF<"version", "Display the version of this program">; +def : F<"V", "Print the version number and exit">, Alias; +def noWarningForNoSymbols : FF<"no_warning_for_no_symbols", "Do not warn about files that have no symbols">; +def warningsAsErrors : FF<"warnings_as_errors", "Treat warnings as errors">; +def static : Flag<["-", "--"], "static">, HelpText<"Produce a statically linked library from the input files">; +def outputFile : S<"o", "Specify output filename">, MetaVarName<"filename">; +def fileList : SS<"filelist", "Pass in file containing a list of filenames">, MetaVarName<"listfile[,dirname]">; +def archType : SS<"arch_only", "Specify architecture type for output library">, MetaVarName<"arch_type">; +def libraries : JS<"l", "l searches for the library libx.a in the library search path. If the string 'x'"# + " ends with '.o', then the library 'x' is searched for without prepending 'lib' or appending '.a'">, MetaVarName<"x">; +def librarySearchDirs : JS<"L", "L adds to the list of directories in which to search for libraries">, MetaVarName<"dir">; +def ignoredSyslibRoot : SS<"syslibroot", "">, Flags<[HelpHidden]>; +def dependencyInfoPath : SS<"dependency_info", "Write an Xcode dependency info file describing the dependencies of the created library">, MetaVarName<"string">; diff --git a/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp b/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp --- a/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp +++ b/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp @@ -19,12 +19,15 @@ #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/MachOUniversalWriter.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/LineIterator.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/WithColor.h" +#include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TextAPI/Architecture.h" #include @@ -32,83 +35,64 @@ using namespace llvm; using namespace llvm::object; +using namespace llvm::opt; + +// Command-line option boilerplate. +namespace { +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Opts.inc" +#undef OPTION +}; -class NewArchiveMemberList; -typedef std::map MembersPerArchitectureMap; - -cl::OptionCategory LibtoolCategory("llvm-libtool-darwin Options"); +#define PREFIX(NAME, VALUE) \ + static constexpr StringLiteral NAME##_init[] = VALUE; \ + static constexpr ArrayRef NAME(NAME##_init, \ + std::size(NAME##_init) - 1); +#include "Opts.inc" +#undef PREFIX + +static constexpr opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Opts.inc" +#undef OPTION +}; -static cl::opt OutputFile("o", cl::desc("Specify output filename"), - cl::value_desc("filename"), - cl::cat(LibtoolCategory)); +class LibtoolDarwinOptTable : public opt::GenericOptTable { +public: + LibtoolDarwinOptTable() : GenericOptTable(InfoTable) {} +}; +} // end anonymous namespace -static cl::list InputFiles(cl::Positional, - cl::desc(""), - cl::cat(LibtoolCategory)); +class NewArchiveMemberList; +typedef std::map MembersPerArchitectureMap; -static cl::opt - ArchType("arch_only", - cl::desc("Specify architecture type for output library"), - cl::value_desc("arch_type"), cl::cat(LibtoolCategory)); +static std::string OutputFile; +static std::vector InputFiles; +static std::optional ArchType; enum class Operation { None, Static }; - -static cl::opt LibraryOperation( - cl::desc("Library Type: "), - cl::values( - clEnumValN(Operation::Static, "static", - "Produce a statically linked library from the input files")), - cl::init(Operation::None), cl::cat(LibtoolCategory)); - -static cl::opt DeterministicOption( - "D", cl::desc("Use zero for timestamps and UIDs/GIDs (Default)"), - cl::init(false), cl::cat(LibtoolCategory)); - -static cl::opt - NonDeterministicOption("U", cl::desc("Use actual timestamps and UIDs/GIDs"), - cl::init(false), cl::cat(LibtoolCategory)); - -static cl::opt - FileList("filelist", - cl::desc("Pass in file containing a list of filenames"), - cl::value_desc("listfile[,dirname]"), cl::cat(LibtoolCategory)); - -static cl::list Libraries( - "l", - cl::desc( - "l searches for the library libx.a in the library search path. If" - " the string 'x' ends with '.o', then the library 'x' is searched for" - " without prepending 'lib' or appending '.a'"), - cl::Prefix, cl::cat(LibtoolCategory)); - -static cl::list LibrarySearchDirs( - "L", - cl::desc( - "L adds to the list of directories in which to search for" - " libraries"), - cl::Prefix, cl::cat(LibtoolCategory)); - -static cl::opt DependencyInfoPath( - "dependency_info", - cl::desc("Write an Xcode dependency info file describing the dependencies " - "of the created library"), - cl::cat(LibtoolCategory)); - -static cl::opt - VersionOption("V", cl::desc("Print the version number and exit"), - cl::cat(LibtoolCategory)); - -static cl::opt NoWarningForNoSymbols( - "no_warning_for_no_symbols", - cl::desc("Do not warn about files that have no symbols"), - cl::cat(LibtoolCategory), cl::init(false)); - -static cl::opt WarningsAsErrors("warnings_as_errors", - cl::desc("Treat warnings as errors"), - cl::cat(LibtoolCategory), - cl::init(false)); - -static cl::opt IgnoredSyslibRoot("syslibroot", cl::Hidden); +static Operation LibraryOperation = Operation::None; + +static bool DeterministicOption; +static bool NonDeterministicOption; +static std::string FileList; +static std::vector Libraries; +static std::vector LibrarySearchDirs; +static std::string DependencyInfoPath; +static bool VersionOption; +static bool NoWarningForNoSymbols; +static bool WarningsAsErrors; +static std::string IgnoredSyslibRoot; static const std::array StandardSearchDirs{ "/lib", @@ -288,14 +272,15 @@ if (Error E = AddMember(*this, FileName)()) return std::move(E); - if (!ArchType.empty()) { + std::string Arch = ArchType.value_or(""); + if (!Arch.empty()) { uint64_t ArchCPUID = getCPUID(C.ArchCPUType, C.ArchCPUSubtype); if (Data.MembersPerArchitecture.find(ArchCPUID) == Data.MembersPerArchitecture.end()) return createStringError(std::errc::invalid_argument, "no library created (no object files in input " "files matching -arch_only %s)", - ArchType.c_str()); + Arch.c_str()); } return std::move(Data); } @@ -383,7 +368,7 @@ // If -arch_only is specified then skip this file if it doesn't match // the architecture specified. - if (!ArchType.empty() && !acceptFileArch(FileCPUType, FileCPUSubtype)) { + if (ArchType && !acceptFileArch(FileCPUType, FileCPUSubtype)) { return Error::success(); } @@ -426,7 +411,7 @@ // If -arch_only is specified then skip this file if it doesn't match // the architecture specified. - if (!ArchType.empty() && + if (ArchType && !acceptFileArch(*FileCPUTypeOrErr, *FileCPUSubTypeOrErr)) { return Error::success(); } @@ -649,16 +634,58 @@ return writeUniversalBinary(*Slices, OutputFile); } +static void parseRawArgs(int Argc, char **Argv) { + LibtoolDarwinOptTable Tbl; + llvm::BumpPtrAllocator A; + llvm::StringSaver Saver{A}; + opt::InputArgList Args = + Tbl.parseArgs(Argc, Argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { + llvm::errs() << Msg << '\n'; + std::exit(1); + }); + + if (Args.hasArg(OPT_help)) { + Tbl.printHelp(llvm::outs(), "llvm-libtool-darwin [options] ", + "llvm-libtool-darwin"); + std::exit(0); + } + + InputFiles = Args.getAllArgValues(OPT_INPUT); + Libraries = Args.getAllArgValues(OPT_libraries); + LibrarySearchDirs = Args.getAllArgValues(OPT_librarySearchDirs); + + if (const opt::Arg *A = Args.getLastArg(OPT_outputFile)) + OutputFile = A->getValue(); + + if (const opt::Arg *A = Args.getLastArg(OPT_archType)) + ArchType = std::make_optional(A->getValue()); + + if (const opt::Arg *A = Args.getLastArg(OPT_fileList)) + FileList = A->getValue(); + + if (const opt::Arg *A = Args.getLastArg(OPT_dependencyInfoPath)) + DependencyInfoPath = A->getValue(); + + if (const opt::Arg *A = Args.getLastArg(OPT_ignoredSyslibRoot)) + IgnoredSyslibRoot = A->getValue(); + + LibraryOperation = + Args.hasArg(OPT_static) ? Operation::Static : Operation::None; + DeterministicOption = Args.hasArg(OPT_deterministicOption); + NonDeterministicOption = Args.hasArg(OPT_nonDeterministicOption); + VersionOption = Args.hasArg(OPT_version); + NoWarningForNoSymbols = Args.hasArg(OPT_noWarningForNoSymbols); + WarningsAsErrors = Args.hasArg(OPT_warningsAsErrors); +} + static Expected parseCommandLine(int Argc, char **Argv) { Config C; - cl::ParseCommandLineOptions(Argc, Argv, "llvm-libtool-darwin\n"); + parseRawArgs(Argc, Argv); if (LibraryOperation == Operation::None) { if (!VersionOption) { - std::string Error; - raw_string_ostream Stream(Error); - LibraryOperation.error("must be specified", "", Stream); - return createStringError(std::errc::invalid_argument, Error.c_str()); + return createStringError(std::errc::invalid_argument, + "-static option: must be specified"); } return C; } @@ -669,10 +696,8 @@ : std::make_unique(DependencyInfoPath); if (OutputFile.empty()) { - std::string Error; - raw_string_ostream Stream(Error); - OutputFile.error("must be specified", "o", Stream); - return createStringError(std::errc::invalid_argument, Error.c_str()); + return createStringError(std::errc::invalid_argument, + "-o option: must be specified"); } if (DeterministicOption && NonDeterministicOption) @@ -693,13 +718,13 @@ return createStringError(std::errc::invalid_argument, "no input files specified"); - if (ArchType.getNumOccurrences()) { - if (Error E = validateArchitectureName(ArchType)) + if (ArchType) { + if (Error E = validateArchitectureName(ArchType.value())) return std::move(E); std::tie(C.ArchCPUType, C.ArchCPUSubtype) = MachO::getCPUTypeFromArchitecture( - MachO::getArchitectureFromName(ArchType)); + MachO::getArchitectureFromName(ArchType.value())); } GlobalDependencyInfo->write("llvm-libtool-darwin " LLVM_VERSION_STRING, @@ -710,7 +735,6 @@ int main(int Argc, char **Argv) { InitLLVM X(Argc, Argv); - cl::HideUnrelatedOptions({&LibtoolCategory, &getColorCategory()}); Expected ConfigOrErr = parseCommandLine(Argc, Argv); if (!ConfigOrErr) { WithColor::defaultErrorHandler(ConfigOrErr.takeError()); diff --git a/llvm/utils/gn/secondary/llvm/tools/llvm-libtool-darwin/BUILD.gn b/llvm/utils/gn/secondary/llvm/tools/llvm-libtool-darwin/BUILD.gn --- a/llvm/utils/gn/secondary/llvm/tools/llvm-libtool-darwin/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/tools/llvm-libtool-darwin/BUILD.gn @@ -1,5 +1,11 @@ import("//llvm/tools/cctools_symlinks.gni") import("//llvm/utils/gn/build/symlink_or_copy.gni") +import("//llvm/utils/TableGen/tablegen.gni") + +tablegen("Opts") { + visibility = [ ":llvm-libtool-darwin" ] + args = [ "-gen-opt-parser-defs" ] +} if (llvm_install_cctools_symlinks) { symlink_or_copy("libtool") { @@ -20,7 +26,9 @@ executable("llvm-libtool-darwin") { deps = [ + ":Opts", "//llvm/lib/Object", + "//llvm/lib/Option", "//llvm/lib/Support", "//llvm/lib/Target:TargetsToBuild", "//llvm/lib/TargetParser", diff --git a/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel b/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel --- a/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel @@ -3677,6 +3677,18 @@ ], ) +gentbl( + name = "LibtoolDarwinOptionsTableGen", + strip_include_prefix = "tools/llvm-libtool-darwin", + tbl_outs = [( + "-gen-opt-parser-defs", + "tools/llvm-libtool-darwin/Opts.inc", + )], + tblgen = ":llvm-tblgen", + td_file = "tools/llvm-libtool-darwin/Opts.td", + td_srcs = ["include/llvm/Option/OptParser.td"], +) + cc_binary( name = "llvm-libtool-darwin", srcs = glob([ @@ -3689,8 +3701,10 @@ ":AllTargetsAsmParsers", ":AllTargetsCodeGens", ":BinaryFormat", + ":LibtoolDarwinOptionsTableGen", ":Core", ":Object", + ":Option", ":Support", ":TextAPI", ],