@@ -49,10 +49,16 @@ An alternate invocation style would be:
abipkgdiff [options] --set1 <pkg1-v1> <pkg2-v1> <pkg3-v1> \
--set2 <pkg1-v2> <pkg2-v2> <pkg3-v2>
+or
+
+ abipkgdiff [options] --set1 <pkg1-v1>,<pkg2-v1>,<pkg3-v1> \
+ --set2 <pkg1-v2>,<pkg2-v2>,<pkg3-v2>
where the ABI of binaries contained in the second set of packages are
compared against binaries contained in the first set of packages.
+Packages in a set are delimited either by a white spaces or by a
+comma.
Environment
===========
@@ -1,2 +1,2 @@
-abipkgdiff: The input file nonexistent-0.rpm doesn't exist
+abipkgdiff: Input files nonexistent-0.rpm nonexistent-1.rpm don't exist
try the --help option for more information
@@ -26,6 +26,7 @@
/// library provides.
#include <unistd.h>
+#include <argp.h>
#include <cassert>
#include <cstdio>
#include <cstdlib>
@@ -93,7 +94,6 @@ class options
public:
string prog_name;
- string unknow_option;
string app_path;
string lib1_path;
string lib2_path;
@@ -101,8 +101,6 @@ public:
string lib1_di_root_path;
string lib2_di_root_path;
vector<string> suppression_paths;
- bool display_help;
- bool display_version;
bool weak_mode;
bool list_undefined_symbols_only;
bool show_base_names;
@@ -121,8 +119,6 @@ public:
options(const char* program_name)
:prog_name(program_name),
- display_help(),
- display_version(),
weak_mode(),
list_undefined_symbols_only(),
show_base_names(),
@@ -218,170 +214,6 @@ perform_compat_check_in_weak_mode(options& opts,
corpus_sptr app_corpus,
corpus_sptr lib_corpus);
-static void
-display_usage(const string& prog_name, ostream& out)
-{
- emit_prefix(prog_name, out)
- << "usage: " << prog_name
- << " [options] [application-path] [lib-v1-path] [lib-v2-path]"
- << "\n"
- << " where options can be: \n"
- << " --help|-h display this help message\n"
- << " --version|-v show program version information and exit\n"
- << " --list-undefined-symbols|-u display the list of "
- "undefined symbols of the application\n"
- << " --show-base-names|b in the report, only show the base names "
- " of the files; not the full paths\n"
- << " --app-debug-info-dir|--appd <path-to-app-debug-info> set the path "
- "to the debug information directory for the application\n"
- << " --lib-debug-info-dir1|--libd1 <path-to-lib-debug-info1> set the path "
- "to the debug information directory for the first library\n"
- << " --lib-debug-info-dir2|--libd2 <path-to-lib-debug-info2> set the path "
- "to the debug information directory for the second library\n"
- << " --suppressions|--suppr <path> specify a suppression file\n"
- << " --no-redundant do not display redundant changes\n"
- << " --no-show-locs do now show location information\n"
- << " --ignore-soname do not take the SONAMEs into account\n"
- << " --fail-no-debug-info bail out if no debug info was found\n"
- << " --redundant display redundant changes (this is the default)\n"
- << " --weak-mode check compatibility between the application and "
- "just one version of the library.\n"
-#ifdef WITH_CTF
- << " --ctf use CTF instead of DWARF in ELF files\n"
-#endif
-#ifdef WITH_BTF
- << " --btf use BTF instead of DWARF in ELF files\n"
-#endif
- ;
-}
-
-static bool
-parse_command_line(int argc, char* argv[], options& opts)
-{
- if (argc < 2)
- return false;
-
- for (int i = 1; i < argc; ++i)
- {
- if (argv[i][0] != '-')
- {
- if (opts.app_path.empty())
- opts.app_path = argv[i];
- else if (opts.lib1_path.empty())
- opts.lib1_path = argv[i];
- else if (opts.lib2_path.empty())
- opts.lib2_path = argv[i];
- else
- return false;
- }
- else if (!strcmp(argv[i], "--version")
- || !strcmp(argv[i], "-v"))
- {
- opts.display_version = true;
- return true;
- }
- else if (!strcmp(argv[i], "--list-undefined-symbols")
- || !strcmp(argv[i], "-u"))
- opts.list_undefined_symbols_only = true;
- else if (!strcmp(argv[i], "--show-base-names")
- || !strcmp(argv[i], "-b"))
- opts.show_base_names = true;
- else if (!strcmp(argv[i], "--app-debug-info-dir")
- || !strcmp(argv[i], "--appd"))
- {
- if (argc <= i + 1
- || argv[i + 1][0] == '-')
- return false;
- // elfutils wants the root path to the debug info to be
- // absolute.
- opts.app_di_root_path =
- abigail::tools_utils::make_path_absolute(string(argv[i + 1]));
- ++i;
- }
- else if (!strcmp(argv[i], "--lib-debug-info-dir1")
- || !strcmp(argv[i], "--libd1"))
- {
- if (argc <= i + 1
- || argv[i + 1][0] == '-')
- return false;
- // elfutils wants the root path to the debug info to be
- // absolute.
- opts.lib1_di_root_path =
- abigail::tools_utils::make_path_absolute(string(argv[i + 1]));
- ++i;
- }
- else if (!strcmp(argv[i], "--lib-debug-info-dir2")
- || !strcmp(argv[i], "--libd2"))
- {
- if (argc <= i + 1
- || argv[i + 1][0] == '-')
- return false;
- // elfutils wants the root path to the debug info to be
- // absolute.
- opts.lib2_di_root_path =
- abigail::tools_utils::make_path_absolute(string(argv[i + 1]));
- ++i;
- }
- else if (!strcmp(argv[i], "--suppressions")
- || !strcmp(argv[i], "--suppr"))
- {
- int j = i + 1;
- if (j >= argc)
- return false;
- opts.suppression_paths.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--redundant"))
- {
- opts.show_redundant = true;
- opts.redundant_opt_set = true;
- }
- else if (!strcmp(argv[i], "--no-redundant"))
- {
- opts.show_redundant = false;
- opts.no_redundant_opt_set = true;
- }
- else if (!strcmp(argv[i], "--no-show-locs"))
- opts.show_locs = false;
- else if (!strcmp(argv[i], "--ignore-soname"))
- opts.ignore_soname=true;
- else if (!strcmp(argv[i], "--fail-no-debug-info"))
- opts.fail_no_debug_info = true;
- else if (!strcmp(argv[i], "--help")
- || !strcmp(argv[i], "-h"))
- {
- opts.display_help = true;
- return true;
- }
- else if (!strcmp(argv[i], "--weak-mode"))
- opts.weak_mode = true;
-#ifdef WITH_CTF
- else if (!strcmp(argv[i], "--ctf"))
- opts.use_ctf = true;
-#endif
-#ifdef WITH_BTF
- else if (!strcmp(argv[i], "--btf"))
- opts.use_btf = true;
-#endif
- else
- {
- opts.unknow_option = argv[i];
- return false;
- }
- }
-
- if (!opts.list_undefined_symbols_only)
- {
- if (opts.app_path.empty()
- || opts.lib1_path.empty())
- return false;
- if (!opts.weak_mode && opts.lib2_path.empty())
- opts.weak_mode = true;
- }
-
- return true;
-}
-
/// Create the context of a diff.
///
/// Create the diff context, initialize it and return a smart pointer
@@ -904,45 +736,225 @@ read_corpus(options opts,
return retval;
}
-int
-main(int argc, char* argv[])
+enum option_key
{
- options opts(argv[0]);
-
- abigail::tools_utils::initialize();
+ OPT_APP_DEBUG_INFO_DIR = 256,
+ OPT_BTF,
+ OPT_CTF,
+ OPT_FAIL_NO_DEBUG_INFO,
+ OPT_IGNORE_SONAME,
+ OPT_LIB_DEBUG_INFO_DIR1,
+ OPT_LIB_DEBUG_INFO_DIR2,
+ OPT_LIST_UNDEFINED_SYMBOLS,
+ OPT_NO_REDUNDANT,
+ OPT_NO_SHOW_LOCS,
+ OPT_REDUNDANT,
+ OPT_SHOW_BASE_NAMES,
+ OPT_SUPPR,
+ OPT_WEAK_MODE,
+};
+
+static const struct argp_option argp_options[] =
+{
+ { "app-debug-info-dir", OPT_APP_DEBUG_INFO_DIR, "PATH", 0,
+ "set the path to the debug information directory for the application", 0 },
+ { "appd", OPT_APP_DEBUG_INFO_DIR, "PATH", OPTION_ALIAS, 0, 0 },
+#ifdef WITH_BTF
+ { "btf", OPT_BTF, 0, 0,
+ "use BTF instead of DWARF in ELF files", 0 },
+#endif
+#ifdef WITH_CTF
+ { "ctf", OPT_CTF, 0, 0,
+ "use CTF instead of DWARF in ELF files", 0 },
+#endif
+ { "fail-no-debug-info", OPT_FAIL_NO_DEBUG_INFO, 0, 0,
+ "bail out if no debug info was found", 0 },
+ { "ignore-soname", OPT_IGNORE_SONAME, 0, 0,
+ "do not take the SONAMEs into account", 0 },
+ { "lib-debug-info-dir1", OPT_LIB_DEBUG_INFO_DIR1, "PATH", 0,
+ "set the path to the debug information directory for the first library", 0 },
+ { "libd1", OPT_LIB_DEBUG_INFO_DIR1, "PATH", OPTION_ALIAS, 0, 0 },
+ { "lib-debug-info-dir2", OPT_LIB_DEBUG_INFO_DIR2, "PATH", 0,
+ "set the path to the debug information directory for the second library", 0 },
+ { "libd2", OPT_LIB_DEBUG_INFO_DIR2, "PATH", OPTION_ALIAS, 0, 0 },
+ { "list-undefined-symbols", OPT_LIST_UNDEFINED_SYMBOLS, 0, 0,
+ "display the list of undefined symbols of the application", 0 },
+ { "u", OPT_LIST_UNDEFINED_SYMBOLS, 0, OPTION_ALIAS, 0, 0 },
+ { "no-redundant", OPT_NO_REDUNDANT, 0, 0,
+ "do not display redundant changes", 0 },
+ { "no-show-locs", OPT_NO_SHOW_LOCS, 0, 0,
+ "do not show location information", 0 },
+ { "redundant", OPT_REDUNDANT, 0, 0,
+ "display redundant changes (this is the default)", 0 },
+ { "show-base-names", OPT_SHOW_BASE_NAMES, 0, 0,
+ "in the report, only show the base names of the files; not the full paths",
+ 0 },
+ { "b", OPT_SHOW_BASE_NAMES, 0, OPTION_ALIAS, 0, 0 },
+ { "suppressions", OPT_SUPPR, "PATH", 0,
+ "specify a suppression file", 0 },
+ { "suppr", OPT_SUPPR, "PATH", OPTION_ALIAS, 0, 0 },
+ { "weak-mode", OPT_WEAK_MODE, 0, 0,
+ "check compatibility between the application and just one version of "
+ "the library", 0 },
+ { 0, 0, 0, 0, 0, 0 }
+};
+
+static error_t
+parse_opt(int key, char* arg, struct argp_state* state)
+{
+ options& opts = *static_cast<options*>(state->input);
+ const string argument = arg ? string(arg) : string();
- if (!parse_command_line(argc, argv, opts))
+ switch (key)
{
- if (!opts.unknow_option.empty())
+ case OPT_APP_DEBUG_INFO_DIR:
+ opts.app_di_root_path =
+ abigail::tools_utils::make_path_absolute(argument);
+ break;
+
+#ifdef WITH_BTF
+ case OPT_BTF:
+ opts.use_btf = true;
+ break;
+#endif
+
+#ifdef WITH_CTF
+ case OPT_CTF:
+ opts.use_ctf = true;
+ break;
+#endif
+
+ case OPT_FAIL_NO_DEBUG_INFO:
+ opts.fail_no_debug_info = true;
+ break;
+
+ case OPT_IGNORE_SONAME:
+ opts.ignore_soname = true;
+ break;
+
+ case OPT_LIB_DEBUG_INFO_DIR1:
+ opts.lib1_di_root_path =
+ abigail::tools_utils::make_path_absolute(argument);
+ break;
+
+ case OPT_LIB_DEBUG_INFO_DIR2:
+ opts.lib2_di_root_path =
+ abigail::tools_utils::make_path_absolute(argument);
+ break;
+
+ case OPT_LIST_UNDEFINED_SYMBOLS:
+ opts.list_undefined_symbols_only = true;
+ break;
+
+ case OPT_NO_REDUNDANT:
+ opts.show_redundant = false;
+ opts.no_redundant_opt_set = true;
+ break;
+
+ case OPT_NO_SHOW_LOCS:
+ opts.show_locs = false;
+ break;
+
+ case OPT_REDUNDANT:
+ opts.show_redundant = true;
+ opts.redundant_opt_set = true;
+ break;
+
+ case OPT_SHOW_BASE_NAMES:
+ opts.show_base_names = true;
+ break;
+
+ case OPT_SUPPR:
+ opts.suppression_paths.push_back(argument);
+ break;
+
+ case OPT_WEAK_MODE:
+ opts.weak_mode = true;
+ break;
+
+ case ARGP_KEY_ARG:
+ if (opts.app_path.empty())
+ opts.app_path = argument;
+ else if (opts.lib1_path.empty())
+ opts.lib1_path = argument;
+ else if (opts.lib2_path.empty())
+ opts.lib2_path = argument;
+ else
+ argp_usage(state);
+ break;
+
+ case ARGP_KEY_END:
+ if (!opts.list_undefined_symbols_only)
{
- emit_prefix(argv[0], cerr)
- << "unrecognized option: " << opts.unknow_option << "\n"
- << "try the --help option for more information\n";
- return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
- | abigail::tools_utils::ABIDIFF_ERROR);
+ if (opts.app_path.empty() || opts.lib1_path.empty())
+ argp_usage(state);
+ if (!opts.weak_mode && opts.lib2_path.empty())
+ opts.weak_mode = true;
}
+ break;
- emit_prefix(argv[0], cerr)
- << "wrong invocation\n"
- << "try the --help option for more information\n";
- return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
- | abigail::tools_utils::ABIDIFF_ERROR);
+ default:
+ return ARGP_ERR_UNKNOWN;
}
- if (opts.display_help)
- {
- display_usage(argv[0], cout);
- return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
- | abigail::tools_utils::ABIDIFF_ERROR);
- }
+ return 0;
+}
- if (opts.display_version)
- {
- emit_prefix(argv[0], cout)
- << abigail::tools_utils::get_library_version_string()
- << "\n";
- return 0;
- }
+static const char* argp_args_doc = "[<application-path>] [<lib-v1-path>] [<lib-v2-path>]";
+static const char* argp_doc =
+ "Check ABI compatibility of an application against two versions of a library.";
+
+static const struct argp abicompat_argp =
+{
+ argp_options,
+ parse_opt,
+ argp_args_doc,
+ argp_doc,
+ 0,
+ 0,
+ 0
+};
+
+/// Version printing hook to be passed to ARGP.
+///
+/// @param stream the output stream.
+///
+/// @param state the ARGP state.
+static void
+print_abicompat_version(FILE *stream, struct argp_state* /*state*/)
+{
+ fprintf(stream, "abicompat %s\n",
+ abigail::tools_utils::get_library_version_string().c_str());
+}
+
+/// Parse the command line
+///
+/// @param argc number of args
+///
+/// @param argv the array of arguments.
+///
+/// @param opts the options set as result of command line parsing.
+static bool
+parse_command_line(int argc, char* argv[], options& opts)
+{
+ argp_program_bug_address = "<libabigail@sourceware.org>";
+ argp_program_version_hook = print_abicompat_version;
+
+ if (argp_parse(&abicompat_argp, argc, argv, 0, 0, &opts) != 0)
+ return false;
+ return true;
+}
+
+int
+main(int argc, char* argv[])
+{
+ options opts(argv[0]);
+
+ abigail::tools_utils::initialize();
+
+ if (!parse_command_line(argc, argv, opts))
+ return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
+ | abigail::tools_utils::ABIDIFF_ERROR);
if (opts.weak_mode && !opts.lib2_path.empty())
{
@@ -8,6 +8,7 @@
/// @file
#include "config.h"
+#include <argp.h>
#include <cstring>
#include <iostream>
#include <memory>
@@ -71,10 +72,6 @@ using namespace abigail;
struct options
{
- bool display_usage;
- bool display_version;
- bool missing_operand;
- string wrong_option;
string file1;
string file2;
vector<string> suppression_paths;
@@ -145,10 +142,7 @@ struct options
vector<string> added_bins1;
vector<string> added_bins2;
- options()
- : display_usage(),
- display_version(),
- missing_operand(),
+ options() :
drop_private_types(false),
linux_kernel_mode(true),
no_default_supprs(),
@@ -210,104 +204,581 @@ struct options
}
};//end struct options;
-static void
-display_usage(const string& prog_name, ostream& out)
+
+enum option_key
{
- emit_prefix(prog_name, out)
- << "usage: " << prog_name << " [options] [<file1> <file2>]\n"
- << " where options can be:\n"
- << " --help|-h display this message\n "
- << " --version|-v display program version information and exit\n"
- << " --debug-info-dir1|--d1 <path> the root for the debug info of file1\n"
- << " --debug-info-dir2|--d2 <path> the root for the debug info of file2\n"
- << " --headers-dir1|--hd1 <path> the path to headers of file1\n"
- << " --header-file1|--hf1 <path> the path to one header of file1\n"
- << " --headers-dir2|--hd2 <path> the path to headers of file2\n"
- << " --header-file2|--hf2 <path> the path to one header of file2\n"
- << " --added-binaries-dir1 the path to the dependencies of file1\n"
- << " --added-binaries-dir2 the path to the dependencies of file2\n"
- << " --add-binaries1 <bin1,bin2,.>. build corpus groups with "
- "extra binaries added to the first one and compare them\n"
- << " --add-binaries2 <bin1,bin2,..> build corpus groups with "
- "extra binaries added to the second one and compare them\n"
- << " --follow-dependencies|--fdeps build corpus groups with the "
- "dependencies of the input files\n"
- << " --list-dependencies|--ldeps show the dependencies of the input files\n"
- << " --drop-private-types drop private types from "
- "internal representation\n"
- << " --exported-interfaces-only analyze exported interfaces only\n"
- << " --allow-non-exported-interfaces analyze interfaces that "
- "might not be exported\n"
- << " --no-linux-kernel-mode don't consider the input binaries as "
- "linux kernel binaries\n"
- << " --kmi-whitelist|-w path to a "
- "linux kernel abi whitelist\n"
- << " --stat only display the diff stats\n"
- << " --symtabs only display the symbol tables of the corpora\n"
- << " --no-default-suppression don't load any "
- "default suppression specification\n"
- << " --no-architecture do not take architecture in account\n"
- << " --no-corpus-path do not take the path to the corpora into account\n"
- << " --ignore-soname do not take the SONAMEs into account\n"
- << " --fail-no-debug-info bail out if no debug info was found\n"
- << " --leaf-changes-only|-l only show leaf changes, "
- "so no change impact analysis (implies --redundant)\n"
- << " --deleted-fns display deleted public functions\n"
- << " --changed-fns display changed public functions\n"
- << " --added-fns display added public functions\n"
- << " --deleted-vars display deleted global public variables\n"
- << " --changed-vars display changed global public variables\n"
- << " --added-vars display added global public variables\n"
- << " --non-reachable-types|-t consider types non reachable"
- " from public interfaces\n"
- << " --no-added-syms do not display added functions or variables\n"
- << " --no-linkage-name do not display linkage names of "
- "added/removed/changed\n"
- << " --no-unreferenced-symbols do not display changes "
- "about symbols not referenced by debug info\n"
- << " --no-show-locs do now show location information\n"
- << " --show-bytes show size and offsets in bytes\n"
- << " --show-bits show size and offsets in bits\n"
- << " --show-hex show size and offset in hexadecimal\n"
- << " --show-dec show size and offset in decimal\n"
- << " --no-show-relative-offset-changes do not show relative"
- " offset changes\n"
- << " --suppressions|--suppr <path> specify a suppression file\n"
- << " --drop <regex> drop functions and variables matching a regexp\n"
- << " --drop-fn <regex> drop functions matching a regexp\n"
- << " --drop-var <regex> drop variables matching a regexp\n"
- << " --keep <regex> keep only functions and variables matching a regex\n"
- << " --keep-fn <regex> keep only functions matching a regex\n"
- << " --keep-var <regex> keep only variables matching a regex\n"
- << " --harmless display the harmless changes\n"
- << " --no-harmful do not display the harmful changes\n"
- << " --redundant display redundant changes\n"
- << " --no-redundant do not display redundant changes "
- "(this is the default)\n"
- << " --impacted-interfaces display interfaces impacted by leaf changes\n"
- << " --no-leverage-dwarf-factorization do not use DWZ optimisations to "
- "speed-up the analysis of the binary\n"
- << " --no-change-categorization | -x don't perform categorization "
- "of changes, for speed purposes\n"
- << " --no-assume-odr-for-cplusplus do not assume the ODR to speed-up the "
- "analysis of the binary\n"
- << " --dump-diff-tree emit a debug dump of the internal diff tree to "
- "the error output stream\n"
- << " --stats show statistics about various internal stuff\n"
+ OPT_ADDED_BINS_DIR1 = 256,
+ OPT_ADDED_BINS_DIR2,
+ OPT_ADDED_BINS1,
+ OPT_ADDED_BINS2,
+ OPT_ALLOW_NON_EXPORTED_INTERFACES,
+ OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS,
+ OPT_BTF,
+ OPT_CTF,
+ OPT_D1,
+ OPT_D2,
+ OPT_DEBUG_SELF_COMPARISON,
+ OPT_DEBUG_TC,
+ OPT_DELETED_FNS,
+ OPT_DELETED_VARS,
+ OPT_CHANGED_FNS,
+ OPT_CHANGED_VARS,
+ OPT_ADDED_FNS,
+ OPT_ADDED_VARS,
+ OPT_DROP,
+ OPT_DROP_FN,
+ OPT_DROP_PRIVATE_TYPES,
+ OPT_DROP_VAR,
+ OPT_DUMP_DIFF_TREE,
+ OPT_EXPORTED_INTERFACES_ONLY,
+ OPT_FAIL_NO_DEBUG_INFO,
+ OPT_FDEPS,
+ OPT_HD1,
+ OPT_HD2,
+ OPT_HF1,
+ OPT_HF2,
+ OPT_HARMLESS,
+ OPT_IGNORE_SONAME,
+ OPT_IMPACTED_INTERFACES,
+ OPT_KEEP,
+ OPT_KEEP_FN,
+ OPT_KEEP_VAR,
+ OPT_KMI_WHITELIST,
+ OPT_LDEPS,
+ OPT_LEAF_CHANGES_ONLY,
+ OPT_NO_ADDED_SYMS,
+ OPT_NO_ARCH,
+ OPT_NO_CHANGE_CATEGORIZATION,
+ OPT_NO_CORPUS_PATH,
+ OPT_NO_DEFAULT_SUPPRESSION,
+ OPT_NO_HARMFUL,
+ OPT_NO_LEVERAGE_DWARF_FACTORIZATION,
+ OPT_NO_LINKAGE_NAME,
+ OPT_NO_LINUX_KERNEL_MODE,
+ OPT_NO_REDUNDANT,
+ OPT_NO_SHOW_LOCS,
+ OPT_NO_SHOW_RELATIVE_OFFSET_CHANGES,
+ OPT_NO_UNREFERENCED_SYMBOLS,
+ OPT_NON_REACHABLE_TYPES,
+ OPT_REDUNDANT,
+ OPT_SHOW_BITS,
+ OPT_SHOW_BYTES,
+ OPT_SHOW_DEC,
+ OPT_SHOW_HEX,
+ OPT_STAT,
+ OPT_STATS,
+ OPT_SUPPR,
+ OPT_SYMTABS,
+ OPT_VERBOSE,
+};
+
+static const struct argp_option argp_options[] =
+{
+ { "add-binaries1", OPT_ADDED_BINS1, "BIN1,BIN2,..", 0,
+ "build corpus groups with extra binaries added to the first one "
+ "and compare them", 0 },
+ { "add-binaries2", OPT_ADDED_BINS2, "BIN1,BIN2,..", 0,
+ "build corpus groups with extra binaries added to the second one "
+ "and compare them", 0 },
+ { "added-binaries-dir1", OPT_ADDED_BINS_DIR1, "PATH", 0,
+ "the path to the dependencies of file1", 0 },
+ { "abd1", OPT_ADDED_BINS_DIR1, "PATH", OPTION_ALIAS, 0, 0 },
+ { "added-binaries-dir2", OPT_ADDED_BINS_DIR2, "PATH", 0,
+ "the path to the dependencies of file2", 0 },
+ { "abd2", OPT_ADDED_BINS_DIR2, "PATH", OPTION_ALIAS, 0, 0 },
+ { "added-fns", OPT_ADDED_FNS, 0, 0,
+ "display added public functions", 0 },
+ { "added-vars", OPT_ADDED_VARS, 0, 0,
+ "display added global public variables", 0 },
+ { "allow-non-exported-interfaces", OPT_ALLOW_NON_EXPORTED_INTERFACES, 0, 0,
+ "analyze interfaces that might not be exported", 0 },
+#ifdef WITH_BTF
+ { "btf", OPT_BTF, 0, 0,
+ "use BTF instead of DWARF in ELF files", 0 },
+#endif
+ { "changed-fns", OPT_CHANGED_FNS, 0, 0,
+ "display changed public functions", 0 },
+ { "changed-vars", OPT_CHANGED_VARS, 0, 0,
+ "display changed global public variables", 0 },
#ifdef WITH_CTF
- << " --ctf use CTF instead of DWARF in ELF files\n"
+ { "ctf", OPT_CTF, 0, 0,
+ "use CTF instead of DWARF in ELF files", 0 },
+#endif
+ { "debug-info-dir1", OPT_D1, "PATH", 0,
+ "the root for the debug info of file1", 0 },
+ { "d1", OPT_D1, "PATH", OPTION_ALIAS, 0, 0 },
+ { "debug-info-dir2", OPT_D2, "PATH", 0,
+ "the root for the debug info of file2", 0 },
+ { "d2", OPT_D2, "PATH", OPTION_ALIAS, 0, 0 },
+#ifdef WITH_DEBUG_SELF_COMPARISON
+ { "debug-self-comparison", OPT_DEBUG_SELF_COMPARISON, 0, 0,
+ "debug the process of comparing an ABI corpus against itself", 0 },
#endif
+#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
+ { "debug-tc", OPT_DEBUG_TC, 0, 0,
+ "debug the type canonicalization process", 0 },
+#endif
+ { "deleted-fns", OPT_DELETED_FNS, 0, 0,
+ "display deleted public functions", 0 },
+ { "deleted-vars", OPT_DELETED_VARS, 0, 0,
+ "display deleted global public variables", 0 },
+ { "drop", OPT_DROP, "REGEX", 0,
+ "drop functions and variables matching a regexp", 0 },
+ { "drop-fn", OPT_DROP_FN, "REGEX", 0,
+ "drop functions matching a regexp", 0 },
+ { "drop-private-types", OPT_DROP_PRIVATE_TYPES, 0, 0,
+ "drop private types from internal representation", 0 },
+ { "drop-var", OPT_DROP_VAR, "REGEX", 0,
+ "drop variables matching a regexp", 0 },
+ { "dump-diff-tree", OPT_DUMP_DIFF_TREE, 0, 0,
+ "emit a debug dump of the internal diff tree to the error output stream",
+ 0 },
+ { "exported-interfaces-only", OPT_EXPORTED_INTERFACES_ONLY, 0, 0,
+ "analyze exported interfaces only", 0 },
+ { "fail-no-debug-info", OPT_FAIL_NO_DEBUG_INFO, 0, 0,
+ "bail out if no debug info was found", 0 },
+ { "follow-dependencies", OPT_FDEPS, 0, 0,
+ "build corpus groups with the dependencies of the input files", 0 },
+ { "fdeps", OPT_FDEPS, 0, OPTION_ALIAS, 0, 0 },
+ { "harmless", OPT_HARMLESS, 0, 0,
+ "display the harmless changes", 0 },
+ { "header-file1", OPT_HF1, "PATH", 0,
+ "the path to one header of file1", 0 },
+ { "hf1", OPT_HF1, "PATH", OPTION_ALIAS, 0, 0 },
+ { "header-file2", OPT_HF2, "PATH", 0,
+ "the path to one header of file2", 0 },
+ { "hf2", OPT_HF2, "PATH", OPTION_ALIAS, 0, 0 },
+ { "headers-dir1", OPT_HD1, "PATH", 0,
+ "the path to headers of file1", 0 },
+ { "hd1", OPT_HD1, "PATH", OPTION_ALIAS, 0, 0 },
+ { "headers-dir2", OPT_HD2, "PATH", 0,
+ "the path to headers of file2", 0 },
+ { "hd2", OPT_HD2, "PATH", OPTION_ALIAS, 0, 0 },
+ { "ignore-soname", OPT_IGNORE_SONAME, 0, 0,
+ "do not take the SONAMEs into account", 0 },
+ { "impacted-interfaces", OPT_IMPACTED_INTERFACES, 0, 0,
+ "display interfaces impacted by leaf changes", 0 },
+ { "keep", OPT_KEEP, "REGEX", 0,
+ "keep only functions and variables matching a regex", 0 },
+ { "keep-fn", OPT_KEEP_FN, "REGEX", 0,
+ "keep only functions matching a regex", 0 },
+ { "keep-var", OPT_KEEP_VAR, "REGEX", 0,
+ "keep only variables matching a regex", 0 },
+ { "kmi-whitelist", OPT_KMI_WHITELIST, "PATH", 0,
+ "path to a linux kernel abi whitelist", 0 },
+ { "w", OPT_KMI_WHITELIST, "PATH", OPTION_ALIAS, 0, 0 },
+ { "leaf-changes-only", OPT_LEAF_CHANGES_ONLY, 0, 0,
+ "only show leaf changes, so no change impact analysis "
+ "(implies --redundant)", 0 },
+ { "l", OPT_LEAF_CHANGES_ONLY, 0, OPTION_ALIAS, 0, 0 },
+ { "list-dependencies", OPT_LDEPS, 0, 0,
+ "show the dependencies of the input files", 0 },
+ { "ldeps", OPT_LDEPS, 0, OPTION_ALIAS, 0, 0 },
+ { "no-added-syms", OPT_NO_ADDED_SYMS, 0, 0,
+ "do not display added functions or variables", 0 },
+ { "no-architecture", OPT_NO_ARCH, 0, 0,
+ "do not take architecture in account", 0 },
+ { "no-assume-odr-for-cplusplus", OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS, 0, 0,
+ "do not assume the ODR to speed-up the analysis of the binary", 0 },
+ { "no-change-categorization", OPT_NO_CHANGE_CATEGORIZATION, 0, 0,
+ "don't perform categorization of changes, for speed purposes", 0 },
+ { "x", OPT_NO_CHANGE_CATEGORIZATION, 0, OPTION_ALIAS, 0, 0 },
+ { "no-corpus-path", OPT_NO_CORPUS_PATH, 0, 0,
+ "do not take the path to the corpora into account", 0 },
+ { "no-default-suppression", OPT_NO_DEFAULT_SUPPRESSION, 0, 0,
+ "don't load any default suppression specification", 0 },
+ { "no-harmful", OPT_NO_HARMFUL, 0, 0,
+ "do not display the harmful changes", 0 },
+ { "no-leverage-dwarf-factorization", OPT_NO_LEVERAGE_DWARF_FACTORIZATION,
+ 0, 0,
+ "do not use DWZ optimisations to speed-up the analysis of the binary",
+ 0 },
+ { "no-linkage-name", OPT_NO_LINKAGE_NAME, 0, 0,
+ "do not display linkage names of added/removed/changed", 0 },
+ { "no-linux-kernel-mode", OPT_NO_LINUX_KERNEL_MODE, 0, 0,
+ "don't consider the input binaries as linux kernel binaries", 0 },
+ { "no-redundant", OPT_NO_REDUNDANT, 0, 0,
+ "do not display redundant changes (this is the default)", 0 },
+ { "no-show-locs", OPT_NO_SHOW_LOCS, 0, 0,
+ "do not show location information", 0 },
+ { "no-show-relative-offset-changes", OPT_NO_SHOW_RELATIVE_OFFSET_CHANGES,
+ 0, 0,
+ "do not show relative offset changes", 0 },
+ { "no-unreferenced-symbols", OPT_NO_UNREFERENCED_SYMBOLS, 0, 0,
+ "do not display changes about symbols not referenced by debug info", 0 },
+ { "non-reachable-types", OPT_NON_REACHABLE_TYPES, 0, 0,
+ "consider types non reachable from public interfaces", 0 },
+ { "t", OPT_NON_REACHABLE_TYPES, 0, OPTION_ALIAS, 0, 0 },
+ { "redundant", OPT_REDUNDANT, 0, 0,
+ "display redundant changes", 0 },
+ { "show-bits", OPT_SHOW_BITS, 0, 0,
+ "show size and offsets in bits", 0 },
+ { "show-bytes", OPT_SHOW_BYTES, 0, 0,
+ "show size and offsets in bytes", 0 },
+ { "show-dec", OPT_SHOW_DEC, 0, 0,
+ "show size and offset in decimal", 0 },
+ { "show-hex", OPT_SHOW_HEX, 0, 0,
+ "show size and offset in hexadecimal", 0 },
+ { "stat", OPT_STAT, 0, 0,
+ "only display the diff stats", 0 },
+ { "stats", OPT_STATS, 0, 0,
+ "show statistics about various internal stuff", 0 },
+ { "suppressions", OPT_SUPPR, "PATH", 0,
+ "specify a suppression file", 0 },
+ { "suppr", OPT_SUPPR, "PATH", OPTION_ALIAS, 0, 0 },
+ { "symtabs", OPT_SYMTABS, 0, 0,
+ "only display the symbol tables of the corpora", 0 },
+ { "verbose", OPT_VERBOSE, 0, 0,
+ "show verbose messages about internal stuff", 0 },
+ { 0, 0, 0, 0, 0, 0 }
+};
+
+static error_t
+parse_opt(int key, char* arg, struct argp_state* state)
+{
+ options& opts = *static_cast<options*>(state->input);
+ const string argument = arg ? string(arg) : string();
+
+ switch (key)
+ {
+ case OPT_ADDED_BINS1:
+ if (argument.find(',') != string::npos)
+ tools_utils::split_string(argument, ",", opts.added_bins1);
+ else
+ opts.added_bins1.push_back(argument);
+ break;
+
+ case OPT_ADDED_BINS2:
+ if (argument.find(',') != string::npos)
+ tools_utils::split_string(argument, ",", opts.added_bins2);
+ else
+ opts.added_bins2.push_back(argument);
+ break;
+
+ case OPT_ADDED_BINS_DIR1:
+ opts.added_bins_dirs1.push_back(argument);
+ break;
+
+ case OPT_ADDED_BINS_DIR2:
+ opts.added_bins_dirs2.push_back(argument);
+ break;
+
+ case OPT_ADDED_FNS:
+ opts.show_added_fns = true;
+ opts.show_all_fns = false;
+ opts.show_all_vars = false;
+ break;
+
+ case OPT_ADDED_VARS:
+ opts.show_added_vars = true;
+ opts.show_all_fns = false;
+ opts.show_all_vars = false;
+ break;
+
+ case OPT_ALLOW_NON_EXPORTED_INTERFACES:
+ opts.exported_interfaces_only = false;
+ break;
+
+ case OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS:
+ opts.assume_odr_for_cplusplus = false;
+ break;
+
#ifdef WITH_BTF
- << " --btf use BTF instead of DWARF in ELF files\n"
+ case OPT_BTF:
+ opts.use_btf = true;
+ break;
#endif
+
+ case OPT_CHANGED_FNS:
+ opts.show_changed_fns = true;
+ opts.show_all_fns = false;
+ opts.show_all_vars = false;
+ break;
+
+ case OPT_CHANGED_VARS:
+ opts.show_changed_vars = true;
+ opts.show_all_fns = false;
+ opts.show_all_vars = false;
+ break;
+
+#ifdef WITH_CTF
+ case OPT_CTF:
+ opts.use_ctf = true;
+ break;
+#endif
+
+ case OPT_D1:
+ opts.di_root_paths1.push_back
+ (abigail::tools_utils::make_path_absolute(argument));
+ break;
+
+ case OPT_D2:
+ opts.di_root_paths2.push_back
+ (abigail::tools_utils::make_path_absolute(argument));
+ break;
+
#ifdef WITH_DEBUG_SELF_COMPARISON
- << " --debug-self-comparison debug the process of comparing "
- "an ABI corpus against itself"
+ case OPT_DEBUG_SELF_COMPARISON:
+ opts.do_debug_self_comparison = true;
+ break;
#endif
+
#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
- << " --debug-tc debug the type canonicalization process"
+ case OPT_DEBUG_TC:
+ opts.do_debug_type_canonicalization = true;
+ break;
#endif
- << " --verbose show verbose messages about internal stuff\n";
+
+ case OPT_DELETED_FNS:
+ opts.show_deleted_fns = true;
+ opts.show_all_fns = false;
+ opts.show_all_vars = false;
+ break;
+
+ case OPT_DELETED_VARS:
+ opts.show_deleted_vars = true;
+ opts.show_all_fns = false;
+ opts.show_all_vars = false;
+ break;
+
+ case OPT_DROP:
+ opts.drop_fn_regex_patterns.push_back(argument);
+ opts.drop_var_regex_patterns.push_back(argument);
+ break;
+
+ case OPT_DROP_FN:
+ opts.drop_fn_regex_patterns.push_back(argument);
+ break;
+
+ case OPT_DROP_PRIVATE_TYPES:
+ opts.drop_private_types = true;
+ break;
+
+ case OPT_DROP_VAR:
+ opts.drop_var_regex_patterns.push_back(argument);
+ break;
+
+ case OPT_DUMP_DIFF_TREE:
+ opts.dump_diff_tree = true;
+ break;
+
+ case OPT_EXPORTED_INTERFACES_ONLY:
+ opts.exported_interfaces_only = true;
+ break;
+
+ case OPT_FAIL_NO_DEBUG_INFO:
+ opts.fail_no_debug_info = true;
+ break;
+
+ case OPT_FDEPS:
+ opts.follow_dependencies = true;
+ break;
+
+ case OPT_HARMLESS:
+ opts.show_harmless_changes = true;
+ break;
+
+ case OPT_HD1:
+ opts.headers_dirs1.push_back(argument);
+ break;
+
+ case OPT_HD2:
+ opts.headers_dirs2.push_back(argument);
+ break;
+
+ case OPT_HF1:
+ opts.header_files1.push_back(argument);
+ break;
+
+ case OPT_HF2:
+ opts.header_files2.push_back(argument);
+ break;
+
+ case OPT_IGNORE_SONAME:
+ opts.ignore_soname = true;
+ break;
+
+ case OPT_IMPACTED_INTERFACES:
+ opts.show_impacted_interfaces = true;
+ break;
+
+ case OPT_KEEP:
+ opts.keep_fn_regex_patterns.push_back(argument);
+ opts.keep_var_regex_patterns.push_back(argument);
+ break;
+
+ case OPT_KEEP_FN:
+ opts.keep_fn_regex_patterns.push_back(argument);
+ break;
+
+ case OPT_KEEP_VAR:
+ opts.keep_var_regex_patterns.push_back(argument);
+ break;
+
+ case OPT_KMI_WHITELIST:
+ opts.kernel_abi_whitelist_paths.push_back(argument);
+ break;
+
+ case OPT_LDEPS:
+ opts.list_dependencies = true;
+ break;
+
+ case OPT_LEAF_CHANGES_ONLY:
+ opts.leaf_changes_only = true;
+ break;
+
+ case OPT_NO_ADDED_SYMS:
+ opts.show_added_syms = false;
+ opts.show_added_vars = false;
+ opts.show_added_fns = false;
+ if (!(opts.show_changed_fns
+ || opts.show_changed_vars
+ || opts.show_deleted_fns
+ || opts.show_deleted_vars))
+ {
+ opts.show_changed_fns = true;
+ opts.show_changed_vars = true;
+ opts.show_deleted_vars = true;
+ opts.show_deleted_fns = true;
+ }
+ opts.show_all_fns = false;
+ opts.show_all_vars = false;
+ break;
+
+ case OPT_NO_ARCH:
+ opts.no_arch = true;
+ break;
+
+ case OPT_NO_CHANGE_CATEGORIZATION:
+ opts.perform_change_categorization = false;
+ break;
+
+ case OPT_NO_CORPUS_PATH:
+ opts.no_corpus = true;
+ break;
+
+ case OPT_NO_DEFAULT_SUPPRESSION:
+ opts.no_default_supprs = true;
+ break;
+
+ case OPT_NO_HARMFUL:
+ opts.show_harmful_changes = false;
+ break;
+
+ case OPT_NO_LEVERAGE_DWARF_FACTORIZATION:
+ opts.leverage_dwarf_factorization = false;
+ break;
+
+ case OPT_NO_LINKAGE_NAME:
+ opts.show_linkage_names = false;
+ break;
+
+ case OPT_NO_LINUX_KERNEL_MODE:
+ opts.linux_kernel_mode = false;
+ break;
+
+ case OPT_NO_REDUNDANT:
+ opts.show_redundant_changes = false;
+ break;
+
+ case OPT_NO_SHOW_LOCS:
+ opts.show_locs = false;
+ break;
+
+ case OPT_NO_SHOW_RELATIVE_OFFSET_CHANGES:
+ opts.show_relative_offset_changes = false;
+ break;
+
+ case OPT_NO_UNREFERENCED_SYMBOLS:
+ opts.show_symbols_not_referenced_by_debug_info = false;
+ break;
+
+ case OPT_NON_REACHABLE_TYPES:
+ opts.show_all_types = true;
+ break;
+
+ case OPT_REDUNDANT:
+ opts.show_redundant_changes = true;
+ break;
+
+ case OPT_SHOW_BITS:
+ opts.show_offsets_sizes_in_bits = true;
+ break;
+
+ case OPT_SHOW_BYTES:
+ opts.show_offsets_sizes_in_bits = false;
+ break;
+
+ case OPT_SHOW_DEC:
+ opts.show_hexadecimal_values = false;
+ break;
+
+ case OPT_SHOW_HEX:
+ opts.show_hexadecimal_values = true;
+ break;
+
+ case OPT_STAT:
+ opts.show_stats_only = true;
+ break;
+
+ case OPT_STATS:
+ opts.show_stats = true;
+ break;
+
+ case OPT_SUPPR:
+ opts.suppression_paths.push_back(argument);
+ break;
+
+ case OPT_SYMTABS:
+ opts.show_symtabs = true;
+ break;
+
+ case OPT_VERBOSE:
+ opts.do_log = true;
+ break;
+
+ case ARGP_KEY_ARG:
+ if (opts.file1.empty())
+ opts.file1 = argument;
+ else if (opts.file2.empty())
+ opts.file2 = argument;
+ else
+ argp_usage(state);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static const char* argp_args_doc = "[<file1> <file2>]";
+static const char* argp_doc =
+ "Compare the ABI of two ELF files, returning 0 if they are identical, "
+ "or non-zero if they differ.";
+
+static const struct argp abidiff_argp =
+{
+ argp_options,
+ parse_opt,
+ argp_args_doc,
+ argp_doc,
+ 0,
+ 0,
+ 0
+};
+
+/// Version printing hook to be passed to ARGP.
+///
+/// @param stream the output stream.
+///
+/// @param state the ARGP state.
+static void
+print_abidiff_version(FILE *stream, struct argp_state* /*state*/)
+{
+ fprintf(stream, "abidiff %s\n",
+ abigail::tools_utils::get_library_version_string().c_str());
}
/// Parse the command line and set the options accordingly.
@@ -324,445 +795,11 @@ display_usage(const string& prog_name, ostream& out)
bool
parse_command_line(int argc, char* argv[], options& opts)
{
- if (argc < 2)
- return false;
-
- for (int i = 1; i < argc; ++i)
- {
- if (argv[i][0] != '-')
- {
- if (opts.file1.empty())
- opts.file1 = argv[i];
- else if (opts.file2.empty())
- opts.file2 = argv[i];
- else
- return false;
- }
- else if (!strcmp(argv[i], "--version")
- || !strcmp(argv[i], "-v"))
- {
- opts.display_version = true;
- return true;
- }
- else if (!strcmp(argv[i], "--debug-info-dir1")
- || !strcmp(argv[i], "--d1"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- // elfutils wants the root path to the debug info to be
- // absolute.
- opts.di_root_paths1.push_back
- (abigail::tools_utils::make_path_absolute(string(argv[j])));
- ++i;
- }
- else if (!strcmp(argv[i], "--debug-info-dir2")
- || !strcmp(argv[i], "--d2"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- // elfutils wants the root path to the debug info to be
- // absolute.
- opts.di_root_paths2.push_back
- (abigail::tools_utils::make_path_absolute(string(argv[j])));
- ++i;
- }
- else if (!strcmp(argv[i], "--headers-dir1")
- || !strcmp(argv[i], "--hd1"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- // The user can specify several header files directories for
- // the first binary.
- opts.headers_dirs1.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--header-file1")
- || !strcmp(argv[i], "--hf1"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- opts.header_files1.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--headers-dir2")
- || !strcmp(argv[i], "--hd2"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- // The user can specify several header files directories for
- // the first binary.
- opts.headers_dirs2.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--header-file2")
- || !strcmp(argv[i], "--hf2"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- opts.header_files2.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--follow-dependencies")
- || !strcmp(argv[i], "--fdeps"))
- opts.follow_dependencies = true;
- else if (!strcmp(argv[i], "--list-dependencies")
- || !strcmp(argv[i], "--ldeps"))
- opts.list_dependencies = true;
- else if (!strcmp(argv[i], "--added-binaries-dir1")
- || !strcmp(argv[i], "--abd1"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- opts.added_bins_dirs1.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--added-binaries-dir2")
- || !strcmp(argv[i], "--abd2"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- opts.added_bins_dirs2.push_back(argv[j]);
- ++i;
- }
- else if (!strncmp(argv[i], "--add-binaries1=",
- strlen("--add-binaries1=")))
- tools_utils::get_comma_separated_args_of_option(argv[i],
- "--add-binaries1=",
- opts.added_bins1);
- else if (!strcmp(argv[i], "--add-binaries1"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- string s = argv[j];
- if (s.find(','))
- tools_utils::split_string(s, ",", opts.added_bins1);
- else
- opts.added_bins1.push_back(s);
- ++i;
- }
- else if (!strncmp(argv[i], "--add-binaries2=",
- strlen("--add-binaries2=")))
- tools_utils::get_comma_separated_args_of_option(argv[i],
- "--add-binaries2=",
- opts.added_bins2);
- else if (!strcmp(argv[i], "--add-binaries2"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- string s = argv[j];
- if (s.find(','))
- tools_utils::split_string(s, ",", opts.added_bins2);
- else
- opts.added_bins2.push_back(s);
- ++i;
- }
- else if (!strcmp(argv[i], "--kmi-whitelist")
- || !strcmp(argv[i], "-w"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- opts.kernel_abi_whitelist_paths.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--stat"))
- opts.show_stats_only = true;
- else if (!strcmp(argv[i], "--symtabs"))
- opts.show_symtabs = true;
- else if (!strcmp(argv[i], "--help")
- || !strcmp(argv[i], "-h"))
- {
- opts.display_usage = true;
- return true;
- }
- else if (!strcmp(argv[i], "--drop-private-types"))
- opts.drop_private_types = true;
- else if (!strcmp(argv[i], "--exported-interfaces-only"))
- opts.exported_interfaces_only = true;
- else if (!strcmp(argv[i], "--allow-non-exported-interfaces"))
- opts.exported_interfaces_only = false;
- else if (!strcmp(argv[i], "--no-linux-kernel-mode"))
- opts.linux_kernel_mode = false;
- else if (!strcmp(argv[i], "--no-default-suppression"))
- opts.no_default_supprs = true;
- else if (!strcmp(argv[i], "--no-architecture"))
- opts.no_arch = true;
- else if (!strcmp(argv[i], "--no-corpus-path"))
- opts.no_corpus = true;
- else if (!strcmp(argv[i], "--ignore-soname"))
- opts.ignore_soname = true;
- else if (!strcmp(argv[i], "--fail-no-debug-info"))
- opts.fail_no_debug_info = true;
- else if (!strcmp(argv[i], "--leaf-changes-only")
- ||!strcmp(argv[i], "-l"))
- opts.leaf_changes_only = true;
- else if (!strcmp(argv[i], "--deleted-fns"))
- {
- opts.show_deleted_fns = true;
- opts.show_all_fns = false;
- opts.show_all_vars = false;
- }
- else if (!strcmp(argv[i], "--changed-fns"))
- {
- opts.show_changed_fns = true;
- opts.show_all_fns = false;
- opts.show_all_vars = false;
- }
- else if (!strcmp(argv[i], "--added-fns"))
- {
- opts.show_added_fns = true;
- opts.show_all_fns = false;
- opts.show_all_vars = false;
- }
- else if (!strcmp(argv[i], "--deleted-vars"))
- {
- opts.show_deleted_vars = true;
- opts.show_all_fns = false;
- opts.show_all_vars = false;
- }
- else if (!strcmp(argv[i], "--changed-vars"))
- {
- opts.show_changed_vars = true;
- opts.show_all_fns = false;
- opts.show_all_vars = false;
- }
- else if (!strcmp(argv[i], "--added-vars"))
- {
- opts.show_added_vars = true;
- opts.show_all_fns = false;
- opts.show_all_vars = false;
- }
- else if (!strcmp(argv[i], "--non-reachable-types")
- || !strcmp(argv[i], "-t"))
- opts.show_all_types = true;
- else if (!strcmp(argv[i], "--no-added-syms"))
- {
- opts.show_added_syms = false;
- opts.show_added_vars = false;
- opts.show_added_fns = false;
-
- // If any of the {changed,deleted}_{vars,fns} is already
- // specified, --no-added-syms has no further effect. If it
- // is the only option specified (as of the time of parsing
- // it), it shall mean "show everything, except added vars,
- // fns and unreferenced symbols.
- if (!(opts.show_changed_fns
- || opts.show_changed_vars
- || opts.show_deleted_fns
- || opts.show_deleted_vars))
- {
- opts.show_changed_fns = true;
- opts.show_changed_vars = true;
-
- opts.show_deleted_vars = true;
- opts.show_deleted_fns = true;
- }
-
- opts.show_all_fns = false;
- opts.show_all_vars = false;
- }
- else if (!strcmp(argv[i], "--no-linkage-name"))
- opts.show_linkage_names = false;
- else if (!strcmp(argv[i], "--no-unreferenced-symbols"))
- opts.show_symbols_not_referenced_by_debug_info = false;
- else if (!strcmp(argv[i], "--no-show-locs"))
- opts.show_locs = false;
- else if (!strcmp(argv[i], "--show-bytes"))
- opts.show_offsets_sizes_in_bits = false;
- else if (!strcmp(argv[i], "--show-bits"))
- opts.show_offsets_sizes_in_bits = true;
- else if (!strcmp(argv[i], "--show-hex"))
- opts.show_hexadecimal_values = true;
- else if (!strcmp(argv[i], "--show-dec"))
- opts.show_hexadecimal_values = false;
- else if (!strcmp(argv[i], "--no-show-relative-offset-changes"))
- opts.show_relative_offset_changes = false;
- else if (!strcmp(argv[i], "--suppressions")
- || !strcmp(argv[i], "--suppr"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- opts.suppression_paths.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--drop"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- opts.drop_fn_regex_patterns.push_back(argv[j]);
- opts.drop_var_regex_patterns.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--drop-fn"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- opts.drop_fn_regex_patterns.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--drop-var"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- opts.drop_var_regex_patterns.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--keep"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- opts.keep_fn_regex_patterns.push_back(argv[j]);
- opts.keep_var_regex_patterns.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--keep-fn"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- opts.keep_fn_regex_patterns.push_back(argv[j]);
- }
- else if (!strcmp(argv[i], "--keep-var"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- opts.keep_var_regex_patterns.push_back(argv[j]);
- }
- else if (!strcmp(argv[i], "--harmless"))
- opts.show_harmless_changes = true;
- else if (!strcmp(argv[i], "--no-harmful"))
- opts.show_harmful_changes = false;
- else if (!strcmp(argv[i], "--redundant"))
- opts.show_redundant_changes = true;
- else if (!strcmp(argv[i], "--no-redundant"))
- opts.show_redundant_changes = false;
- else if (!strcmp(argv[i], "--impacted-interfaces"))
- opts.show_impacted_interfaces = true;
- else if (!strcmp(argv[i], "--no-leverage-dwarf-factorization"))
- opts.leverage_dwarf_factorization = false;
- else if (!strcmp(argv[i], "--no-change-categorization")
- || !strcmp(argv[i], "-x"))
- opts.perform_change_categorization = false;
- else if (!strcmp(argv[i], "--no-assume-odr-for-cplusplus"))
- opts.leverage_dwarf_factorization = false;
- else if (!strcmp(argv[i], "--dump-diff-tree"))
- opts.dump_diff_tree = true;
- else if (!strcmp(argv[i], "--stats"))
- opts.show_stats = true;
- else if (!strcmp(argv[i], "--verbose"))
- opts.do_log = true;
-#ifdef WITH_CTF
- else if (!strcmp(argv[i], "--ctf"))
- opts.use_ctf = true;
-#endif
-#ifdef WITH_BTF
- else if (!strcmp(argv[i], "--btf"))
- opts.use_btf = true;
-#endif
-#ifdef WITH_DEBUG_SELF_COMPARISON
- else if (!strcmp(argv[i], "--debug-self-comparison"))
- opts.do_debug_self_comparison = true;
-#endif
-#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
- else if (!strcmp(argv[i], "--debug-tc"))
- opts.do_debug_type_canonicalization = true;
-#endif
- else
- {
- if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
- opts.wrong_option = argv[i];
- return false;
- }
- }
+ argp_program_version_hook = print_abidiff_version;
+ argp_program_bug_address = "<libabigail@sourceware.org>";
+ if (argp_parse(&abidiff_argp, argc, argv, 0, 0, &opts) != 0)
+ return false;
return true;
}
@@ -1287,38 +1324,8 @@ main(int argc, char* argv[])
options opts;
if (!parse_command_line(argc, argv, opts))
- {
- emit_prefix(argv[0], cerr)
- << "unrecognized option: "
- << opts.wrong_option << "\n"
- << "try the --help option for more information\n";
- return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
- | abigail::tools_utils::ABIDIFF_ERROR);
- }
-
- if (opts.missing_operand)
- {
- emit_prefix(argv[0], cerr)
- << "missing operand to option: " << opts.wrong_option <<"\n"
- << "try the --help option for more information\n";
- return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
- | abigail::tools_utils::ABIDIFF_ERROR);
- }
-
- if (opts.display_usage)
- {
- display_usage(argv[0], cout);
- return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
- | abigail::tools_utils::ABIDIFF_ERROR);
- }
-
- if (opts.display_version)
- {
- emit_prefix(argv[0], cout)
- << abigail::tools_utils::get_library_version_string()
- << "\n";
- return 0;
- }
+ return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
+ | abigail::tools_utils::ABIDIFF_ERROR);
if (!maybe_check_suppression_files(opts))
return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
@@ -1859,6 +1866,15 @@ main(int argc, char* argv[])
else
status = abigail::tools_utils::ABIDIFF_ERROR;
}
+ else
+ {
+ char* prog_name = (char*) "abidiff";
+ emit_prefix(prog_name, cerr)
+ << "At least one of the files to compare is missing\n";
+ argp_help(&abidiff_argp, stderr, ARGP_HELP_USAGE, prog_name);
+ return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
+ | abigail::tools_utils::ABIDIFF_ERROR);
+ }
return status;
}
@@ -12,6 +12,7 @@
/// libabigail XML format.
#include "config.h"
+#include <argp.h>
#include <unistd.h>
#include <cassert>
#include <cstdio>
@@ -84,7 +85,6 @@ using namespace abigail;
struct options
{
- string wrong_option;
string in_file_path;
string out_file_path;
vector<string> di_root_paths;
@@ -96,7 +96,6 @@ struct options
vector<string> suppression_paths;
vector<string> kabi_whitelist_paths;
suppressions_type kabi_whitelist_supprs;
- bool display_version;
bool display_abixml_version;
bool fail_no_debug_info;
bool check_alt_debug_info_path;
@@ -145,8 +144,7 @@ struct options
#endif
options()
- : display_version(),
- display_abixml_version(),
+ : display_abixml_version(),
fail_no_debug_info(),
check_alt_debug_info_path(),
show_base_name_alt_debug_info_path(),
@@ -195,324 +193,6 @@ struct options
}
};
-static void
-display_usage(const string& prog_name, ostream& out)
-{
- emit_prefix(prog_name, out)
- << "usage: " << prog_name << " [options] [<path-to-elf-file>]\n"
- << " where options can be: \n"
- << " --abidiff compare the loaded ABI against itself\n"
- << " --abixml-version display the version of the ABIXML ABI format\n"
- << " --add-binaries <bin1,bin2,...> build a corpus group with "
- "the added inaries\n"
- << " --allow-non-exported-interfaces analyze interfaces that"
- "might not be exported\n"
- << " --annotate annotate the ABI artifacts emitted in the output\n"
-#ifdef WITH_BTF
- << " --btf use BTF instead of DWARF in ELF files\n"
-#endif
- << " --check-alternate-debug-info <elf-path> check alternate debug info "
- "of <elf-path>\n"
- << " --check-alternate-debug-info-base-name <elf-path> check alternate "
- "debug info of <elf-path>, and show its base name\n"
-#ifdef WITH_CTF
- << " --ctf use CTF instead of DWARF in ELF files\n"
-#endif
-#ifdef WITH_DEBUG_SELF_COMPARISON
- << " --debug-abidiff debug the process of comparing the loaded ABI against itself\n"
-#endif
- << " --debug-info-dir|-d <dir-path> look for debug info under 'dir-path'\n"
-#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
- << " --debug-tc debug the type canonicalization process\n"
- << " --debug-dc debug the DIE canonicalization process\n"
-#endif
- << " --drop-undefined-syms drop undefined symbols from representation\n"
- << " --exported-interfaces-only analyze exported interfaces only\n"
- << " --follow-dependencies build a corpus group with the dependencies\n"
- << " --follow-dependencies build a corpus group with the dependencies\n"
- << " --force-early-suppression drop IR nodes that match suppression specifications\n"
- << " --headers-dir|--hd <path> the path to headers of the elf file\n"
- << " --header-file|--hf <path> the path one header of the elf file\n"
- << " --help|-h display this message\n"
- << " --kmi-whitelist|--kmi-stablelist|-w path to a linux kernel "
- "abi whitelist\n"
- << " --list-dependencies list the dependencies of a given binary\n"
- << " --added-binaries-dir|--abd <dir-of-deps> where to look for dependencies "
- "or added binaries\n"
- << " --linux-tree|--lt emit the ABI for the union of a "
- "vmlinux and its modules\n"
- << " --load-all-types read all types including those not reachable from "
- "exported declarations\n"
- << " --no-architecture do not emit architecture info in the output\n"
- << " --no-assume-odr-for-cplusplus do not assume the ODR to speed-up the "
- "analysis of the binary\n"
- << " --no-comp-dir-path do not show compilation path information\n"
- << " --no-corpus-path do not take the path to the corpora into account\n"
- << " --no-elf-needed do not show the DT_NEEDED information\n"
- << " --no-leverage-dwarf-factorization do not use DWZ optimisations to "
- "speed-up the analysis of the binary\n"
- << " --no-linux-kernel-mode don't consider the input binary as "
- "a Linux Kernel binary\n"
- << " --no-load-undefined-interfaces do not consider undefined "
- "interfaces from the binary"
- << " --no-parameter-names do not show names of function parameters\n"
- << " --no-show-locs do not show location information\n"
- << " --no-write-default-sizes do not emit pointer size when it equals"
- " the default address size of the translation unit\n"
- << " --noout do not emit anything after reading the binary\n"
- << " --out-file|-o <file-path> write the output to 'file-path'\n"
- << " --short-locs only print filenames rather than paths\n"
- << " --suppressions|--suppr <path> specify a suppression file\n"
- << " --type-id-style <sequence|hash> type id style (sequence(default): "
- "\"type-id-\" + number; hash: hex-digits)\n"
- << " --stats show statistics about various internal stuff\n"
- << " --verbose show verbose messages about internal stuff\n"
- << " --version|-v display program version information and exit\n"
- << " --vmlinux <path> the path to the vmlinux binary to consider to emit "
- "the ABI of the union of vmlinux and its modules\n" ;
-}
-
-static bool
-parse_command_line(int argc, char* argv[], options& opts)
-{
- if (argc < 2)
- return false;
-
- for (int i = 1; i < argc; ++i)
- {
- if (argv[i][0] != '-')
- {
- if (opts.in_file_path.empty())
- opts.in_file_path = argv[i];
- else
- return false;
- }
- else if (!strcmp(argv[i], "--version")
- || !strcmp(argv[i], "-v"))
- opts.display_version = true;
- else if (!strcmp(argv[i], "--abixml-version")
- || !strcmp(argv[i], "-v"))
- opts.display_abixml_version = true;
- else if (!strcmp(argv[i], "--fail-no-debug-info"))
- opts.fail_no_debug_info = true;
- else if (!strcmp(argv[i], "--debug-info-dir")
- || !strcmp(argv[i], "-d"))
- {
- if (argc <= i + 1
- || argv[i + 1][0] == '-')
- return false;
- // elfutils wants the root path to the debug info to be
- // absolute.
- opts.di_root_paths.push_back
- (abigail::tools_utils::make_path_absolute(string(argv[i + 1])));
- ++i;
- }
- else if (!strcmp(argv[i], "--headers-dir")
- || !strcmp(argv[i], "--hd"))
- {
- int j = i + 1;
- if (j >= argc)
- return false;
- opts.headers_dirs.push_back(argv[j]);
- // The user is indirectly defining private types so she
- // really wants those types to be removed from the ABIXML.
- // So let's drop them from the IR.
- opts.drop_private_types = true;
- ++i;
- }
- else if (!strcmp(argv[i], "--added-binaries-dir")
- || !strcmp(argv[i], "--abd"))
- {
- int j = i + 1;
- if (j >= argc)
- return false;
- opts.added_bins_dirs.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--header-file")
- || !strcmp(argv[i], "--hf"))
- {
- int j = i + 1;
- if (j >= argc)
- return false;
- opts.header_files.push_back(argv[j]);
- // The user is indirectly defining private types so she
- // really wants those types to be removed from the ABIXML.
- // So let's drop them from the IR.
- opts.drop_private_types = true;
- ++i;
- }
- else if (!strcmp(argv[i], "--out-file")
- || !strcmp(argv[i], "-o"))
- {
- if (argc <= i + 1
- || argv[i + 1][0] == '-'
- || !opts.out_file_path.empty())
- return false;
-
- opts.out_file_path = argv[i + 1];
- ++i;
- }
- else if (!strcmp(argv[i], "--suppressions")
- || !strcmp(argv[i], "--suppr"))
- {
- int j = i + 1;
- if (j >= argc)
- return false;
- opts.suppression_paths.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--kmi-whitelist")
- || !strcmp(argv[i], "--kmi-stablelist")
- || !strcmp(argv[i], "-w"))
- {
- int j = i + 1;
- if (j >= argc)
- return false;
- opts.kabi_whitelist_paths.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--linux-tree")
- || !strcmp(argv[i], "--lt"))
- opts.corpus_group_for_linux = true;
- else if (!strcmp(argv[i], "--vmlinux"))
- {
- int j = i + 1;
- if (j >= argc)
- return false;
- opts.vmlinux = argv[j];
- ++i;
- }
- else if (!strcmp(argv[i], "--noout"))
- opts.noout = true;
- else if (!strcmp(argv[i], "--follow-dependencies"))
- opts.follow_dependencies = true;
- else if (!strcmp(argv[i], "--list-dependencies"))
- opts.list_dependencies = true;
- else if (!strncmp(argv[i], "--add-binaries=",
- strlen("--add-binaries=")))
- tools_utils::get_comma_separated_args_of_option(argv[i],
- "--add-binaries=",
- opts.added_bins);
- else if (!strcmp(argv[i], "--add-binaries"))
- {
- int j = i + 1;
- if (j >= argc)
- return false;
-
- string s = argv[j];
- if (s.find(','))
- tools_utils::split_string(s, ",", opts.added_bins);
- else
- opts.added_bins.push_back(s);
- ++i;
- }
-#ifdef WITH_CTF
- else if (!strcmp(argv[i], "--ctf"))
- opts.use_ctf = true;
-#endif
-#ifdef WITH_BTF
- else if (!strcmp(argv[i], "--btf"))
- opts.use_btf = true;
-#endif
- else if (!strcmp(argv[i], "--no-architecture"))
- opts.write_architecture = false;
- else if (!strcmp(argv[i], "--no-corpus-path"))
- opts.write_corpus_path = false;
- else if (!strcmp(argv[i], "--no-show-locs"))
- opts.show_locs = false;
- else if (!strcmp(argv[i], "--short-locs"))
- opts.short_locs = true;
- else if (!strcmp(argv[i], "--no-comp-dir-path"))
- opts.write_comp_dir = false;
- else if (!strcmp(argv[i], "--no-elf-needed"))
- opts.write_elf_needed = false;
- else if (!strcmp(argv[i], "--no-write-default-sizes"))
- opts.default_sizes = false;
- else if (!strcmp(argv[i], "--no-parameter-names"))
- opts.write_parameter_names = false;
- else if (!strcmp(argv[i], "--type-id-style"))
- {
- ++i;
- if (i >= argc)
- return false;
- if (!strcmp(argv[i], "sequence"))
- opts.type_id_style = SEQUENCE_TYPE_ID_STYLE;
- else if (!strcmp(argv[i], "hash"))
- opts.type_id_style = HASH_TYPE_ID_STYLE;
- else
- return false;
- }
- else if (!strcmp(argv[i], "--check-alternate-debug-info")
- || !strcmp(argv[i], "--check-alternate-debug-info-base-name"))
- {
- if (argc <= i + 1
- || argv[i + 1][0] == '-'
- || !opts.in_file_path.empty())
- return false;
- if (!strcmp(argv[i], "--check-alternate-debug-info-base-name"))
- opts.show_base_name_alt_debug_info_path = true;
- opts.check_alt_debug_info_path = true;
- opts.in_file_path = argv[i + 1];
- ++i;
- }
- else if (!strcmp(argv[i], "--load-all-types"))
- opts.load_all_types = true;
- else if (!strcmp(argv[i], "--no-load-undefined-interfaces"))
- opts.load_undefined_interfaces = false;
- else if (!strcmp(argv[i], "--drop-private-types"))
- opts.drop_private_types = true;
- else if (!strcmp(argv[i], "--force-early-suppression"))
- opts.force_early_suppression = true;
- else if (!strcmp(argv[i], "--drop-undefined-syms"))
- opts.drop_undefined_syms = true;
- else if (!strcmp(argv[i], "--exported-interfaces-only"))
- opts.exported_interfaces_only = true;
- else if (!strcmp(argv[i], "--allow-non-exported-interfaces"))
- opts.exported_interfaces_only = false;
- else if (!strcmp(argv[i], "--no-linux-kernel-mode"))
- opts.linux_kernel_mode = false;
- else if (!strcmp(argv[i], "--abidiff"))
- opts.abidiff = true;
-#ifdef WITH_DEBUG_SELF_COMPARISON
- else if (!strcmp(argv[i], "--debug-abidiff"))
- {
- opts.abidiff = true;
- opts.debug_abidiff = true;
- }
-#endif
-#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
- else if (!strcmp(argv[i], "--debug-tc")
- || !strcmp(argv[i], "debug-type-canonicalization"))
- opts.debug_type_canonicalization = true;
- else if (!strcmp(argv[i], "--debug-dc")
- || !strcmp(argv[i], "debug-die-canonicalization"))
- opts.debug_die_canonicalization = true;
-#endif
- else if (!strcmp (argv[i], "--no-assume-odr-for-cplusplus"))
- opts.assume_odr_for_cplusplus = false;
- else if (!strcmp (argv[i], "--no-leverage-dwarf-factorization"))
- opts.leverage_dwarf_factorization = false;
- else if (!strcmp(argv[i], "--annotate"))
- opts.annotate = true;
- else if (!strcmp(argv[i], "--stats"))
- opts.show_stats = true;
- else if (!strcmp(argv[i], "--verbose"))
- opts.do_log = true;
- else if (!strcmp(argv[i], "--help")
- || !strcmp(argv[i], "--h"))
- return false;
- else
- {
- if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
- opts.wrong_option = argv[i];
- return false;
- }
- }
-
- return true;
-}
-
/// Initialize the context use for driving ABI comparison.
///
/// @param ctxt the context to initialize.
@@ -1159,6 +839,445 @@ load_kernel_corpus_group_and_write_abixml(char* argv[],
return exit_code;
}
+enum option_key
+{
+ OPT_ABIDIFF = 256,
+ OPT_ABIXML_VERSION,
+ OPT_ADD_BINARIES,
+ OPT_ADDED_BINS_DIR,
+ OPT_ALLOW_NON_EXPORTED_INTERFACES,
+ OPT_ANNOTATE,
+ OPT_BTF,
+ OPT_CHECK_ALT_DEBUG_INFO,
+ OPT_CHECK_ALT_DEBUG_INFO_BASE_NAME,
+ OPT_CTF,
+ OPT_DEBUG_ABIDIFF,
+ OPT_DEBUG_DC,
+ OPT_DEBUG_TC,
+ OPT_DEBUG_INFO_DIR,
+ OPT_DROP_PRIVATE_TYPES,
+ OPT_DROP_UNDEFINED_SYMS,
+ OPT_EXPORTED_INTERFACES_ONLY,
+ OPT_FAIL_NO_DEBUG_INFO,
+ OPT_FDEPS,
+ OPT_FORCE_EARLY_SUPPRESSION,
+ OPT_HD,
+ OPT_HF,
+ OPT_KMI_WHITELIST,
+ OPT_LDEPS,
+ OPT_LINUX_TREE,
+ OPT_LOAD_ALL_TYPES,
+ OPT_NO_ARCH,
+ OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS,
+ OPT_NO_COMP_DIR_PATH,
+ OPT_NO_CORPUS_PATH,
+ OPT_NO_ELF_NEEDED,
+ OPT_NO_LEVERAGE_DWARF_FACTORIZATION,
+ OPT_NO_LINUX_KERNEL_MODE,
+ OPT_NO_LOAD_UNDEFINED_INTERFACES,
+ OPT_NO_PARAMETER_NAMES,
+ OPT_NO_SHOW_LOCS,
+ OPT_NO_WRITE_DEFAULT_SIZES,
+ OPT_NOOUT,
+ OPT_OUT_FILE,
+ OPT_SHORT_LOCS,
+ OPT_SHOW_STATS,
+ OPT_SUPPR,
+ OPT_TYPE_ID_STYLE,
+ OPT_VERBOSE,
+ OPT_VMLINUX,
+};
+
+static const struct argp_option argp_options[] =
+{
+ { "abidiff", OPT_ABIDIFF, 0, 0,
+ "compare the loaded ABI against itself", 0 },
+ { "abixml-version", OPT_ABIXML_VERSION, 0, 0,
+ "display the version of the ABIXML ABI format", 0 },
+ { "add-binaries", OPT_ADD_BINARIES, "BIN1,BIN2,..", 0,
+ "build a corpus group with the added binaries", 0 },
+ { "added-binaries-dir", OPT_ADDED_BINS_DIR, "PATH", 0,
+ "where to look for dependencies or added binaries", 0 },
+ { "abd", OPT_ADDED_BINS_DIR, "PATH", OPTION_ALIAS, 0, 0 },
+ { "allow-non-exported-interfaces", OPT_ALLOW_NON_EXPORTED_INTERFACES, 0, 0,
+ "analyze interfaces that might not be exported", 0 },
+ { "annotate", OPT_ANNOTATE, 0, 0,
+ "annotate the ABI artifacts emitted in the output", 0 },
+#ifdef WITH_BTF
+ { "btf", OPT_BTF, 0, 0,
+ "use BTF instead of DWARF in ELF files", 0 },
+#endif
+ { "check-alternate-debug-info", OPT_CHECK_ALT_DEBUG_INFO, "ELF-PATH", 0,
+ "check alternate debug info of <elf-path>", 0 },
+ { "check-alternate-debug-info-base-name",
+ OPT_CHECK_ALT_DEBUG_INFO_BASE_NAME, "ELF-PATH", 0,
+ "check alternate debug info of <elf-path>, and show its base name", 0 },
+#ifdef WITH_CTF
+ { "ctf", OPT_CTF, 0, 0,
+ "use CTF instead of DWARF in ELF files", 0 },
+#endif
+#ifdef WITH_DEBUG_SELF_COMPARISON
+ { "debug-abidiff", OPT_DEBUG_ABIDIFF, 0, 0,
+ "debug the process of comparing the loaded ABI against itself", 0 },
+#endif
+#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
+ { "debug-dc", OPT_DEBUG_DC, 0, 0,
+ "debug the DIE canonicalization process", 0 },
+ { "debug-tc", OPT_DEBUG_TC, 0, 0,
+ "debug the type canonicalization process", 0 },
+#endif
+ { "debug-info-dir", OPT_DEBUG_INFO_DIR, "PATH", 0,
+ "look for debug info under 'dir-path'", 0 },
+ { "d", OPT_DEBUG_INFO_DIR, "PATH", OPTION_ALIAS, 0, 0 },
+ { "drop-private-types", OPT_DROP_PRIVATE_TYPES, 0, 0,
+ "drop private types from internal representation", 0 },
+ { "drop-undefined-syms", OPT_DROP_UNDEFINED_SYMS, 0, 0,
+ "drop undefined symbols from representation", 0 },
+ { "exported-interfaces-only", OPT_EXPORTED_INTERFACES_ONLY, 0, 0,
+ "analyze exported interfaces only", 0 },
+ { "fail-no-debug-info", OPT_FAIL_NO_DEBUG_INFO, 0, 0,
+ "bail out if no debug info was found", 0 },
+ { "follow-dependencies", OPT_FDEPS, 0, 0,
+ "build a corpus group with the dependencies", 0 },
+ { "force-early-suppression", OPT_FORCE_EARLY_SUPPRESSION, 0, 0,
+ "drop IR nodes that match suppression specifications", 0 },
+ { "headers-dir", OPT_HD, "PATH", 0,
+ "the path to headers of the elf file", 0 },
+ { "hd", OPT_HD, "PATH", OPTION_ALIAS, 0, 0 },
+ { "header-file", OPT_HF, "PATH", 0,
+ "the path to one header of the elf file", 0 },
+ { "hf", OPT_HF, "PATH", OPTION_ALIAS, 0, 0 },
+ { "kmi-whitelist", OPT_KMI_WHITELIST, "PATH", 0,
+ "path to a linux kernel abi whitelist", 0 },
+ { "kmi-stablelist", OPT_KMI_WHITELIST, "PATH", OPTION_ALIAS, 0, 0 },
+ { "w", OPT_KMI_WHITELIST, "PATH", OPTION_ALIAS, 0, 0 },
+ { "list-dependencies", OPT_LDEPS, 0, 0,
+ "list the dependencies of a given binary", 0 },
+ { "linux-tree", OPT_LINUX_TREE, 0, 0,
+ "emit the ABI for the union of a vmlinux and its modules", 0 },
+ { "lt", OPT_LINUX_TREE, 0, OPTION_ALIAS, 0, 0 },
+ { "load-all-types", OPT_LOAD_ALL_TYPES, 0, 0,
+ "read all types including those not reachable from exported declarations",
+ 0 },
+ { "no-architecture", OPT_NO_ARCH, 0, 0,
+ "do not emit architecture info in the output", 0 },
+ { "no-assume-odr-for-cplusplus", OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS, 0, 0,
+ "do not assume the ODR to speed-up the analysis of the binary", 0 },
+ { "no-comp-dir-path", OPT_NO_COMP_DIR_PATH, 0, 0,
+ "do not show compilation path information", 0 },
+ { "no-corpus-path", OPT_NO_CORPUS_PATH, 0, 0,
+ "do not take the path to the corpora into account", 0 },
+ { "no-elf-needed", OPT_NO_ELF_NEEDED, 0, 0,
+ "do not show the DT_NEEDED information", 0 },
+ { "no-leverage-dwarf-factorization", OPT_NO_LEVERAGE_DWARF_FACTORIZATION,
+ 0, 0,
+ "do not use DWZ optimisations to speed-up the analysis of the binary", 0 },
+ { "no-linux-kernel-mode", OPT_NO_LINUX_KERNEL_MODE, 0, 0,
+ "don't consider the input binary as a Linux Kernel binary", 0 },
+ { "no-load-undefined-interfaces", OPT_NO_LOAD_UNDEFINED_INTERFACES, 0, 0,
+ "do not consider undefined interfaces from the binary", 0 },
+ { "no-parameter-names", OPT_NO_PARAMETER_NAMES, 0, 0,
+ "do not show names of function parameters", 0 },
+ { "no-show-locs", OPT_NO_SHOW_LOCS, 0, 0,
+ "do not show location information", 0 },
+ { "no-write-default-sizes", OPT_NO_WRITE_DEFAULT_SIZES, 0, 0,
+ "do not emit pointer size when it equals the default address size of "
+ "the translation unit", 0 },
+ { "noout", OPT_NOOUT, 0, 0,
+ "do not emit anything after reading the binary", 0 },
+ { "out-file", OPT_OUT_FILE, "PATH", 0,
+ "write the output to 'file-path'", 0 },
+ { "o", OPT_OUT_FILE, "PATH", OPTION_ALIAS, 0, 0 },
+ { "short-locs", OPT_SHORT_LOCS, 0, 0,
+ "only print filenames rather than paths", 0 },
+ { "stats", OPT_SHOW_STATS, 0, 0,
+ "show statistics about various internal stuff", 0 },
+ { "suppressions", OPT_SUPPR, "PATH", 0,
+ "specify a suppression file", 0 },
+ { "suppr", OPT_SUPPR, "PATH", OPTION_ALIAS, 0, 0 },
+ { "type-id-style", OPT_TYPE_ID_STYLE, "STYLE", 0,
+ "type id style (sequence(default): \"type-id-\" + number; hash: "
+ "hex-digits)", 0 },
+ { "verbose", OPT_VERBOSE, 0, 0,
+ "show verbose messages about internal stuff", 0 },
+ { "vmlinux", OPT_VMLINUX, "PATH", 0,
+ "the path to the vmlinux binary to consider to emit the ABI of the union "
+ "of vmlinux and its modules", 0 },
+ { 0, 0, 0, 0, 0, 0 }
+};
+
+static error_t
+parse_opt(int key, char* arg, struct argp_state* state)
+{
+ options& opts = *static_cast<options*>(state->input);
+ const string argument = arg ? string(arg) : string();
+
+ switch (key)
+ {
+ case OPT_ABIDIFF:
+ opts.abidiff = true;
+ break;
+
+ case OPT_ABIXML_VERSION:
+ opts.display_abixml_version = true;
+ break;
+
+ case OPT_ADD_BINARIES:
+ if (argument.find(',') != string::npos)
+ tools_utils::split_string(argument, ",", opts.added_bins);
+ else
+ opts.added_bins.push_back(argument);
+ break;
+
+ case OPT_ADDED_BINS_DIR:
+ opts.added_bins_dirs.push_back(argument);
+ break;
+
+ case OPT_ALLOW_NON_EXPORTED_INTERFACES:
+ opts.exported_interfaces_only = false;
+ break;
+
+ case OPT_ANNOTATE:
+ opts.annotate = true;
+ break;
+
+#ifdef WITH_BTF
+ case OPT_BTF:
+ opts.use_btf = true;
+ break;
+#endif
+
+ case OPT_CHECK_ALT_DEBUG_INFO:
+ if (opts.in_file_path.empty())
+ opts.in_file_path = argument;
+ else
+ argp_usage(state);
+ opts.check_alt_debug_info_path = true;
+ break;
+
+ case OPT_CHECK_ALT_DEBUG_INFO_BASE_NAME:
+ if (opts.in_file_path.empty())
+ opts.in_file_path = argument;
+ else
+ argp_usage(state);
+ opts.check_alt_debug_info_path = true;
+ opts.show_base_name_alt_debug_info_path = true;
+ break;
+
+#ifdef WITH_CTF
+ case OPT_CTF:
+ opts.use_ctf = true;
+ break;
+#endif
+
+#ifdef WITH_DEBUG_SELF_COMPARISON
+ case OPT_DEBUG_ABIDIFF:
+ opts.abidiff = true;
+ opts.debug_abidiff = true;
+ break;
+#endif
+
+#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
+ case OPT_DEBUG_DC:
+ opts.debug_die_canonicalization = true;
+ break;
+
+ case OPT_DEBUG_TC:
+ opts.debug_type_canonicalization = true;
+ break;
+#endif
+
+ case OPT_DEBUG_INFO_DIR:
+ opts.di_root_paths.push_back
+ (abigail::tools_utils::make_path_absolute(argument));
+ break;
+
+ case OPT_DROP_PRIVATE_TYPES:
+ opts.drop_private_types = true;
+ break;
+
+ case OPT_DROP_UNDEFINED_SYMS:
+ opts.drop_undefined_syms = true;
+ break;
+
+ case OPT_EXPORTED_INTERFACES_ONLY:
+ opts.exported_interfaces_only = true;
+ break;
+
+ case OPT_FAIL_NO_DEBUG_INFO:
+ opts.fail_no_debug_info = true;
+ break;
+
+ case OPT_FDEPS:
+ opts.follow_dependencies = true;
+ break;
+
+ case OPT_FORCE_EARLY_SUPPRESSION:
+ opts.force_early_suppression = true;
+ break;
+
+ case OPT_HD:
+ opts.headers_dirs.push_back(argument);
+ opts.drop_private_types = true;
+ break;
+
+ case OPT_HF:
+ opts.header_files.push_back(argument);
+ opts.drop_private_types = true;
+ break;
+
+ case OPT_KMI_WHITELIST:
+ opts.kabi_whitelist_paths.push_back(argument);
+ break;
+
+ case OPT_LDEPS:
+ opts.list_dependencies = true;
+ break;
+
+ case OPT_LINUX_TREE:
+ opts.corpus_group_for_linux = true;
+ break;
+
+ case OPT_LOAD_ALL_TYPES:
+ opts.load_all_types = true;
+ break;
+
+ case OPT_NO_ARCH:
+ opts.write_architecture = false;
+ break;
+
+ case OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS:
+ opts.assume_odr_for_cplusplus = false;
+ break;
+
+ case OPT_NO_COMP_DIR_PATH:
+ opts.write_comp_dir = false;
+ break;
+
+ case OPT_NO_CORPUS_PATH:
+ opts.write_corpus_path = false;
+ break;
+
+ case OPT_NO_ELF_NEEDED:
+ opts.write_elf_needed = false;
+ break;
+
+ case OPT_NO_LEVERAGE_DWARF_FACTORIZATION:
+ opts.leverage_dwarf_factorization = false;
+ break;
+
+ case OPT_NO_LINUX_KERNEL_MODE:
+ opts.linux_kernel_mode = false;
+ break;
+
+ case OPT_NO_LOAD_UNDEFINED_INTERFACES:
+ opts.load_undefined_interfaces = false;
+ break;
+
+ case OPT_NO_PARAMETER_NAMES:
+ opts.write_parameter_names = false;
+ break;
+
+ case OPT_NO_SHOW_LOCS:
+ opts.show_locs = false;
+ break;
+
+ case OPT_NO_WRITE_DEFAULT_SIZES:
+ opts.default_sizes = false;
+ break;
+
+ case OPT_NOOUT:
+ opts.noout = true;
+ break;
+
+ case OPT_OUT_FILE:
+ if (!opts.out_file_path.empty())
+ argp_usage(state);
+ opts.out_file_path = argument;
+ break;
+
+ case OPT_SHORT_LOCS:
+ opts.short_locs = true;
+ break;
+
+ case OPT_SHOW_STATS:
+ opts.show_stats = true;
+ break;
+
+ case OPT_SUPPR:
+ opts.suppression_paths.push_back(argument);
+ break;
+
+ case OPT_TYPE_ID_STYLE:
+ if (argument == "sequence")
+ opts.type_id_style = SEQUENCE_TYPE_ID_STYLE;
+ else if (argument == "hash")
+ opts.type_id_style = HASH_TYPE_ID_STYLE;
+ else
+ argp_usage(state);
+ break;
+
+ case OPT_VERBOSE:
+ opts.do_log = true;
+ break;
+
+ case OPT_VMLINUX:
+ opts.vmlinux = argument;
+ break;
+
+ case ARGP_KEY_ARG:
+ if (opts.in_file_path.empty())
+ opts.in_file_path = argument;
+ else
+ argp_usage(state);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static const char* argp_args_doc = "[<path-to-elf-file>]";
+static const char* argp_doc =
+ "Read an ELF file, load its debug info and emit it in the native "
+ "libabigail XML format.";
+
+static const struct argp abidw_argp =
+{
+ argp_options,
+ parse_opt,
+ argp_args_doc,
+ argp_doc,
+ 0,
+ 0,
+ 0
+};
+
+static void
+print_abidw_version(FILE *stream, struct argp_state* /*state*/)
+{
+ fprintf(stream, "abidw %s\n",
+ abigail::tools_utils::get_library_version_string().c_str());
+}
+
+/// Version printing hook to be passed to ARGP.
+///
+/// @param stream the output stream.
+///
+/// @param state the ARGP state.
+static bool
+parse_command_line(int argc, char* argv[], options& opts)
+{
+ argp_program_version_hook = print_abidw_version;
+ argp_program_bug_address = "<libabigail@sourceware.org>";
+
+ if (argp_parse(&abidw_argp, argc, argv, 0, 0, &opts) != 0)
+ return false;
+ return true;
+}
+
int
main(int argc, char* argv[])
{
@@ -1168,24 +1287,13 @@ main(int argc, char* argv[])
if (!parse_command_line(argc, argv, opts)
|| (opts.in_file_path.empty()
- && !opts.display_version
&& !opts.display_abixml_version))
{
- if (!opts.wrong_option.empty())
- emit_prefix(argv[0], cerr)
- << "unrecognized option: " << opts.wrong_option << "\n";
- display_usage(argv[0], cerr);
+ char* prog_name = (char*) "abidw";
+ argp_help(&abidw_argp, stderr, ARGP_HELP_USAGE, prog_name);
return 1;
}
- if (opts.display_version)
- {
- emit_prefix(argv[0], cout)
- << abigail::tools_utils::get_library_version_string()
- << "\n";
- return 0;
- }
-
if (opts.display_abixml_version)
{
emit_prefix(argv[0], cout)
@@ -14,6 +14,7 @@
/// be empty.
#include "config.h"
+#include <argp.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
@@ -74,9 +75,7 @@ using abigail::xml_writer::write_corpus_to_archive;
struct options
{
- string wrong_option;
string file_path;
- bool display_version;
bool read_from_stdin;
bool read_tu;
bool diff;
@@ -95,8 +94,7 @@ struct options
#endif
options()
- : display_version(false),
- read_from_stdin(false),
+ : read_from_stdin(false),
read_tu(false),
diff(false),
noout(false),
@@ -480,157 +478,6 @@ show_how_type_is_used(abigail::fe_iface &iface, const string& type_id)
}
#endif // WITH_SHOW_TYPE_USE_IN_ABILINT
-static void
-display_usage(const string& prog_name, ostream& out)
-{
- emit_prefix(prog_name, out)
- << "usage: " << prog_name << " [options] [<abi-file1>]\n"
- << " where options can be:\n"
- << " --annotate annotate the ABI artifacts emitted in the output\n"
- << " --verbose show verbose messages about internal stuff\n"
-#ifdef WITH_CTF
- << " --ctf use CTF instead of DWARF in ELF files\n"
-#endif
- << " --debug-info-dir <path> the path under which to look for "
- "debug info for the elf <abi-file>\n"
- << " --diff for xml inputs, perform a text diff between "
- "the input and the memory model saved back to disk\n"
- << " --header-file|--hf <path> the path to one header of the elf file\n"
- << " --headers-dir|--hd <path> the path to headers of the elf file\n"
- << " --help display this message\n"
- << " --noout do not display anything on stdout\n"
-#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
- << " --show-type-use <type-id> show how a type is used from the abixml file\n"
-#endif
- << " --stdin read abi-file content from stdin\n"
- << " --suppressions|--suppr <path> specify a suppression file\n"
- << " --tu expect a single translation unit file\n"
- << " --version|-v display program version information and exit\n"
- ;
-}
-
-bool
-parse_command_line(int argc, char* argv[], options& opts)
-{
- if (argc < 2)
- {
- opts.read_from_stdin = true;
- return true;
- }
-
- for (int i = 1; i < argc; ++i)
- {
- if (argv[i][0] != '-')
- {
- if (opts.file_path.empty())
- opts.file_path = argv[i];
- else
- return false;
- }
- else if (!strcmp(argv[i], "--help"))
- return false;
- else if (!strcmp(argv[i], "--version")
- || !strcmp(argv[i], "-v"))
- {
- opts.display_version = true;
- return true;
- }
- else if (!strcmp(argv[i], "--debug-info-dir"))
- {
- if (argc <= i + 1
- || argv[i + 1][0] == '-')
- return false;
- // elfutils wants the root path to the debug info to be
- // absolute.
- opts.di_root_path =
- abigail::tools_utils::make_path_absolute(string(argv[i + 1]));
- ++i;
- }
- else if (!strcmp(argv[i], "--headers-dir")
- || !strcmp(argv[i], "--hd"))
- {
- int j = i + 1;
- if (j >= argc)
- return false;
- opts.headers_dir = argv[j];
- ++i;
- }
- else if (!strcmp(argv[i], "--header-file")
- || !strcmp(argv[i], "--hf"))
- {
- int j = i + 1;
- if (j >= argc)
- return false;
- opts.header_files.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--suppressions")
- || !strcmp(argv[i], "--suppr"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.wrong_option = argv[i];
- return true;
- }
- opts.suppression_paths.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--stdin"))
- opts.read_from_stdin = true;
- else if (!strcmp(argv[i], "--tu"))
- opts.read_tu = true;
-#ifdef WITH_CTF
- else if (!strcmp(argv[i], "--ctf"))
- opts.use_ctf = true;
-#endif
- else if (!strcmp(argv[i], "--diff"))
- opts.diff = true;
- else if (!strcmp(argv[i], "--noout"))
- opts.noout = true;
- else if (!strcmp(argv[i], "--annotate"))
- opts.annotate = true;
- else if (!strcmp(argv[i], "--verbose"))
- opts.do_log = true;
-#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
- else if (!strcmp(argv[i], "--show-type-use"))
- {
- ++i;
- if (i >= argc || argv[i][0] == '-')
- return false;
- opts.type_id_to_show = argv[i];
- }
-#endif
- else
- {
- if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
- opts.wrong_option = argv[i];
- return false;
- }
- }
-
-#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
- if (!opts.type_id_to_show.empty()
- && opts.file_path.empty())
- emit_prefix(argv[0], cout)
- << "WARNING: --show-type-use <type-id> "
- "must be accompanied with an abixml file\n";
-
- if (opts.file_path.empty()
- && opts.type_id_to_show.empty())
- opts.read_from_stdin = true;
-#endif
-
- if (opts.read_from_stdin && !opts.file_path.empty())
- {
- emit_prefix(argv[0], cout)
- << "WARNING: The \'--stdin\' option is used. The "
- << opts.file_path << " will be ignored automatically\n";
- }
-
- return true;
-}
-
/// Check that the suppression specification files supplied are
/// present. If not, emit an error on stderr.
///
@@ -696,6 +543,203 @@ set_reader_options(abigail::fe_iface& reader, const options& opts)
reader.options().do_log = opts.do_log;
}
+enum option_key
+{
+ OPT_ANNOTATE = 256,
+#ifdef WITH_CTF
+ OPT_CTF,
+#endif
+ OPT_DEBUG_INFO_DIR,
+ OPT_DIFF,
+ OPT_HD,
+ OPT_HF,
+ OPT_NOOUT,
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+ OPT_SHOW_TYPE_USE,
+#endif
+ OPT_STDIN,
+ OPT_SUPPR,
+ OPT_TU,
+ OPT_VERBOSE
+};
+
+static const struct argp_option argp_options[] =
+{
+ { "annotate", OPT_ANNOTATE, 0, 0,
+ "annotate the ABI artifacts emitted in the output", 0 },
+#ifdef WITH_CTF
+ { "ctf", OPT_CTF, 0, 0,
+ "use CTF instead of DWARF in ELF files", 0 },
+#endif
+ { "debug-info-dir", OPT_DEBUG_INFO_DIR, "PATH", 0,
+ "the path under which to look for debug info for the elf <abi-file>", 0 },
+ { "diff", OPT_DIFF, 0, 0,
+ "for xml inputs, perform a text diff between "
+ "the input and the memory model saved back to disk", 0 },
+ { "headers-dir", OPT_HD, "PATH", 0,
+ "the path to headers of the elf file", 0 },
+ { "hd", OPT_HD, "PATH", OPTION_ALIAS, 0, 0 },
+ { "header-file", OPT_HF, "PATH", 0,
+ "the path to one header of the elf file", 0 },
+ { "hf", OPT_HF, "PATH", OPTION_ALIAS, 0, 0 },
+ { "noout", OPT_NOOUT, 0, 0,
+ "do not display anything on stdout", 0 },
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+ { "show-type-use", OPT_SHOW_TYPE_USE, "TYPE-ID", 0,
+ "show how a type is used from the abixml file", 0 },
+#endif
+ { "stdin", OPT_STDIN, 0, 0,
+ "read abi-file content from stdin", 0 },
+ { "suppressions", OPT_SUPPR, "PATH", 0,
+ "specify a suppression file", 0 },
+ { "suppr", OPT_SUPPR, "PATH", OPTION_ALIAS, 0, 0 },
+ { "tu", OPT_TU, 0, 0,
+ "expect a single translation unit file", 0 },
+ { "verbose", OPT_VERBOSE, 0, 0,
+ "show verbose messages about internal stuff", 0 },
+ { 0, 0, 0, 0, 0, 0 }
+};
+
+static error_t
+parse_opt(int key, char* arg, struct argp_state* state)
+{
+ options& opts = *static_cast<options*>(state->input);
+ const string argument = arg ? string(arg) : string();
+
+ switch (key)
+ {
+ case OPT_ANNOTATE:
+ opts.annotate = true;
+ break;
+
+#ifdef WITH_CTF
+ case OPT_CTF:
+ opts.use_ctf = true;
+ break;
+#endif
+
+ case OPT_DEBUG_INFO_DIR:
+ opts.di_root_path =
+ abigail::tools_utils::make_path_absolute(argument);
+ break;
+
+ case OPT_DIFF:
+ opts.diff = true;
+ break;
+
+ case OPT_HD:
+ opts.headers_dir = argument;
+ break;
+
+ case OPT_HF:
+ opts.header_files.push_back(argument);
+ break;
+
+ case OPT_NOOUT:
+ opts.noout = true;
+ break;
+
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+ case OPT_SHOW_TYPE_USE:
+ opts.type_id_to_show = argument;
+ break;
+#endif
+
+ case OPT_STDIN:
+ opts.read_from_stdin = true;
+ break;
+
+ case OPT_SUPPR:
+ opts.suppression_paths.push_back(argument);
+ break;
+
+ case OPT_TU:
+ opts.read_tu = true;
+ break;
+
+ case OPT_VERBOSE:
+ opts.do_log = true;
+ break;
+
+ case ARGP_KEY_ARG:
+ if (opts.file_path.empty())
+ opts.file_path = argument;
+ else
+ argp_usage(state);
+ break;
+
+ case ARGP_KEY_END:
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+ if (!opts.type_id_to_show.empty() && opts.file_path.empty())
+ emit_prefix("abilint", cout)
+ << "WARNING: --show-type-use <type-id> "
+ "must be accompanied with an abixml file\n";
+
+ if (opts.file_path.empty() && opts.type_id_to_show.empty())
+ opts.read_from_stdin = true;
+#else
+ if (opts.file_path.empty())
+ opts.read_from_stdin = true;
+#endif
+ if (opts.read_from_stdin && !opts.file_path.empty())
+ emit_prefix("abilint", cout)
+ << "WARNING: The '--stdin' option is used. The "
+ << opts.file_path << " will be ignored automatically\n";
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static const char* argp_args_doc = "[<abi-file>]";
+static const char* argp_doc =
+ "Read an ABI instrumentation file in native XML format and check its "
+ "integrity.";
+
+static const struct argp abilint_argp =
+{
+ argp_options,
+ parse_opt,
+ argp_args_doc,
+ argp_doc,
+ 0,
+ 0,
+ 0
+};
+
+/// Version printing hook to be passed to ARGP.
+///
+/// @param stream the output stream.
+///
+/// @param state the ARGP state.
+void
+print_abilint_version(FILE *stream, struct argp_state* /*state*/)
+{
+ fprintf(stream, "abilint %s\n",
+ abigail::tools_utils::get_library_version_string().c_str());
+}
+
+/// Parse the command line
+///
+/// @param argc number of args
+///
+/// @param argv the array of arguments.
+///
+/// @param opts the options set as result of command line parsing.
+bool
+parse_command_line(int argc, char* argv[], options& opts)
+{
+ argp_program_bug_address = "<libabigail@sourceware.org>";
+ argp_program_version_hook = print_abilint_version;
+
+ if (argp_parse(&abilint_argp, argc, argv, 0, 0, &opts) != 0)
+ return false;
+ return true;
+}
+
/// Reads a bi (binary instrumentation) file, saves it back to a
/// temporary file and run a diff on the two versions.
int
@@ -706,21 +750,11 @@ main(int argc, char* argv[])
options opts;
if (!parse_command_line(argc, argv, opts))
{
- if (!opts.wrong_option.empty())
- emit_prefix(argv[0], cerr)
- << "unrecognized option: " << opts.wrong_option << "\n";
- display_usage(argv[0], cerr);
+ char* prog_name = (char*)"abilint";
+ argp_help(&abilint_argp, stderr, ARGP_HELP_USAGE, prog_name);
return 1;
}
- if (opts.display_version)
- {
- emit_prefix(argv[0], cout)
- << abigail::tools_utils::get_library_version_string()
- << "\n";
- return 0;
- }
-
if (!maybe_check_suppression_files(opts))
return 1;
@@ -55,6 +55,7 @@
#include "config.h"
#include <assert.h>
+#include <argp.h>
#include <fcntl.h>
#include <sys/stat.h>
@@ -178,12 +179,8 @@ class options
options();
public:
- string wrong_option;
- string wrong_arg;
+ vector<string> wrong_args;
string prog_name;
- bool display_usage;
- bool display_version;
- bool missing_operand;
bool nonexistent_file;
bool abignore;
bool parallel;
@@ -194,6 +191,7 @@ public:
string devel_package1;
string devel_package2;
size_t num_workers;
+ unsigned last_seen_set;
bool verbose;
bool verbose_diff;
bool drop_private_types;
@@ -238,12 +236,10 @@ public:
options(const string& program_name)
: prog_name(program_name),
- display_usage(),
- display_version(),
- missing_operand(),
nonexistent_file(),
abignore(true),
parallel(true),
+ last_seen_set(0),
verbose(),
verbose_diff(),
drop_private_types(),
@@ -971,82 +967,6 @@ package::extracted_packages_parent_dir()
/// A convenience typedef for shared_ptr of package.
typedef shared_ptr<package> package_sptr;
-/// Show the usage of this program.
-///
-/// @param prog_name the name of the program.
-///
-/// @param out the output stream to emit the usage to .
-static void
-display_usage(const string& prog_name, ostream& out)
-{
- emit_prefix(prog_name, out)
- << "usage: " << prog_name << " [options] <package1> <package2>\n"
- << " where options can be:\n"
- << " --debug-info-pkg1|--d1 <path> path of debug-info package of package1\n"
- << " --debug-info-pkg2|--d2 <path> path of debug-info package of package2\n"
- << " --devel-pkg1|--devel1 <path> path of devel package of pakage1\n"
- << " --devel-pkg2|--devel2 <path> path of devel package of pakage1\n"
- << " --drop-private-types drop private types from "
- "internal representation\n"
- << " --no-default-suppression don't load any default "
- "suppression specifications\n"
- << " --suppressions|--suppr <path> specify supression specification path\n"
- << " --linux-kernel-abi-whitelist|-w path to a "
- "linux kernel abi whitelist\n"
- << " --wp <path> path to a linux kernel abi whitelist package\n"
- << " --keep-tmp-files don't erase created temporary files\n"
- << " --dso-only compare shared libraries only\n"
- << " --private-dso compare DSOs that are private "
- "to the package as well\n"
- << " --leaf-changes-only|-l only show leaf changes, "
- "so no change impact analysis (implies --redundant)\n"
- << " --impacted-interfaces|-i display interfaces impacted by leaf changes\n"
- << " --full-impact|-f when comparing kernel packages, show the "
- "full impact analysis report rather than the default leaf changes reports\n"
- << " --non-reachable-types|-t consider types non reachable"
- " from public interfaces\n"
- << " --exported-interfaces-only analyze exported interfaces only\n"
- << " --allow-non-exported-interfaces analyze interfaces that "
- "might not be exported\n"
- << " --no-linkage-name do not display linkage names of "
- "added/removed/changed\n"
- << " --redundant display redundant changes\n"
- << " --harmless display the harmless changes\n"
- << " --no-show-locs do not show location information\n"
- << " --ignore-soname do not take the SONAMEs into account\n"
- << " --show-bytes show size and offsets in bytes\n"
- << " --show-bits show size and offsets in bits\n"
- << " --show-hex show size and offset in hexadecimal\n"
- << " --show-dec show size and offset in decimal\n"
- << " --no-show-relative-offset-changes do not show relative"
- " offset changes\n"
- << " --no-added-syms do not display added functions or variables\n"
- << " --no-unreferenced-symbols do not display changes "
- "about symbols not referenced by debug info\n"
- << " --no-added-binaries do not display added binaries\n"
- << " --no-abignore do not look for *.abignore files\n"
- << " --no-parallel do not execute in parallel\n"
- << " --fail-no-dbg fail if no debug info was found\n"
- << " --show-identical-binaries show the names of identical binaries\n"
- << " --no-leverage-dwarf-factorization do not use DWZ optimisations to "
- "speed-up the analysis of the binary\n"
- << " --no-assume-odr-for-cplusplus do not assume the ODR to speed-up the"
- "analysis of the binary\n"
- << " --verbose emit verbose progress messages\n"
- << " --verbose-diff emit verbose diff progress messages\n"
- << " --self-check perform a sanity check by comparing "
- "binaries inside the input package against their ABIXML representation\n"
-#ifdef WITH_CTF
- << " --ctf use CTF instead of DWARF in ELF files\n"
-#endif
-#ifdef WITH_BTF
- << " --btf use BTF instead of DWARF in ELF files\n"
-#endif
- << " --help|-h display this help message\n"
- << " --version|-v display program version information"
- " and exit\n";
-}
-
#ifdef WITH_RPM
/// Extract an RPM package.
@@ -3759,278 +3679,489 @@ compare(const package_set_sptr& first_ps,
return compare(first_ps, second_ps, diff, opts);
}
-/// Parse the command line of the current program.
-///
-/// @param argc the number of arguments in the @p argv parameter.
-///
-/// @param argv the array of arguemnts passed to the function. The
-/// first argument is the name of this program.
-///
-/// @param opts the resulting options.
-///
-/// @return true upon successful parsing.
-static bool
-parse_command_line(int argc, char* argv[], options& opts)
-{
- if (argc < 2)
- return false;
- for (int i = 1; i < argc; ++i)
- {
- if (argv[i][0] != '-')
- {
- if (opts.package_set_paths1.empty())
- {
- string path = make_path_absolute(argv[i]).get();
- opts.package_set_paths1.insert(path);
- opts.nonexistent_file = !file_exists(path);
- }
- else if (opts.package_set_paths2.empty())
- {
- string path = make_path_absolute(argv[i]).get();
- opts.package_set_paths2.insert(path);
- opts.nonexistent_file = !file_exists(path);
- }
- else
- {
- opts.wrong_arg = argv[i];
- return false;
- }
+enum option_key
+{
+ OPT_ALLOW_NON_EXPORTED_INTERFACES = 256,
+ OPT_BTF,
+ OPT_CTF,
+ OPT_D1,
+ OPT_D2,
+ OPT_DEVEL1,
+ OPT_DEVEL2,
+ OPT_DROP_PRIVATE_TYPES,
+ OPT_DSO_ONLY,
+ OPT_EXPORTED_INTERFACES_ONLY,
+ OPT_FAIL_NO_DBG,
+ OPT_FULL_IMPACT,
+ OPT_HARMLESS,
+ OPT_IGNORE_SONAME,
+ OPT_IMPACTED_INTERFACES,
+ OPT_KEEP_TMP_FILES,
+ OPT_KMI_WHITELIST,
+ OPT_KMI_WHITELIST_PKG,
+ OPT_LEAF_CHANGES_ONLY,
+ OPT_NO_ABIGNORE,
+ OPT_NO_ADDED_BINARIES,
+ OPT_NO_ADDED_SYMS,
+ OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS,
+ OPT_NO_DEFAULT_SUPPRESSION,
+ OPT_NO_LEVERAGE_DWARF_FACTORIZATION,
+ OPT_NO_LINKAGE_NAME,
+ OPT_NO_PARALLEL,
+ OPT_NO_SHOW_LOCS,
+ OPT_NO_SHOW_RELATIVE_OFFSET_CHANGES,
+ OPT_NO_UNREFERENCED_SYMBOLS,
+ OPT_NON_REACHABLE_TYPES,
+ OPT_PRIVATE_DSO,
+ OPT_REDUNDANT,
+ OPT_SELF_CHECK,
+ OPT_SET1,
+ OPT_SET2,
+ OPT_SHOW_BITS,
+ OPT_SHOW_BYTES,
+ OPT_SHOW_DEC,
+ OPT_SHOW_HEX,
+ OPT_SHOW_IDENTICAL_BINARIES,
+ OPT_SUPPR,
+ OPT_VERBOSE,
+ OPT_VERBOSE_DIFF
+};
- if (opts.nonexistent_file)
- {
- opts.wrong_arg = argv[i];
- return true;
- }
- }
- else if (!strcmp(argv[i], "--set1")
- || !strcmp(argv[i], "--set2"))
- {
- bool is_set1 = !strcmp(argv[i], "--set1");
- set<string>& set =
- is_set1 ? opts.package_set_paths1 : opts.package_set_paths2;
- for (int j = i + 1; j < argc; ++j)
- {
- if (argv[j][0] == '-')
- {
- i = j - 1;
- break;
- }
- const char* p = argv[j];
- string pkg_path = make_path_absolute(p).get();
- if (!opts.nonexistent_file)
- opts.nonexistent_file = !file_exists(pkg_path);
- if (opts.nonexistent_file && opts.wrong_arg.empty())
- opts.wrong_arg = pkg_path;
- set.insert(pkg_path);
- i = j;
- }
- if (!opts.wrong_arg.empty())
- return false;
- }
- else if (!strcmp(argv[i], "--debug-info-pkg1")
- || !strcmp(argv[i], "--d1"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- opts.debug_packages1.push_back
- (abigail::tools_utils::make_path_absolute(argv[j]).get());
- ++i;
- }
- else if (!strcmp(argv[i], "--debug-info-pkg2")
- || !strcmp(argv[i], "--d2"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- opts.debug_packages2.push_back
- (abigail::tools_utils::make_path_absolute(argv[j]).get());
- ++i;
- }
- else if (!strcmp(argv[i], "--devel-pkg1")
- || !strcmp(argv[i], "--devel1"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- opts.devel_package1 =
- abigail::tools_utils::make_path_absolute(argv[j]).get();
- ++i;
- }
- else if (!strcmp(argv[i], "--devel-pkg2")
- || !strcmp(argv[i], "--devel2"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- opts.devel_package2 =
- abigail::tools_utils::make_path_absolute(argv[j]).get();
- ++i;
- }
- else if (!strcmp(argv[i], "--drop-private-types"))
- opts.drop_private_types = true;
- else if (!strcmp(argv[i], "--no-default-suppression"))
- opts.no_default_suppression = true;
- else if (!strcmp(argv[i], "--keep-tmp-files"))
- opts.keep_tmp_files = true;
- else if (!strcmp(argv[i], "--dso-only"))
- opts.compare_dso_only = true;
- else if (!strcmp(argv[i], "--private-dso"))
- opts.compare_private_dsos = true;
- else if (!strcmp(argv[i], "--leaf-changes-only")
- ||!strcmp(argv[i], "-l"))
- opts.leaf_changes_only = true;
- else if (!strcmp(argv[i], "--impacted-interfaces")
- ||!strcmp(argv[i], "-i"))
- opts.show_impacted_interfaces = true;
- else if (!strcmp(argv[i], "--non-reachable-types")
- ||!strcmp(argv[i], "-t"))
- opts.show_all_types = true;
- else if (!strcmp(argv[i], "--full-impact")
- ||!strcmp(argv[i], "-f"))
- opts.show_full_impact_report = true;
- else if (!strcmp(argv[i], "--exported-interfaces-only"))
- opts.exported_interfaces_only = true;
- else if (!strcmp(argv[i], "--allow-non-exported-interfaces"))
- opts.exported_interfaces_only = false;
- else if (!strcmp(argv[i], "--no-linkage-name"))
- opts.show_linkage_names = false;
- else if (!strcmp(argv[i], "--redundant"))
- opts.show_redundant_changes = true;
- else if (!strcmp(argv[i], "--harmless"))
- opts.show_harmless_changes = true;
- else if (!strcmp(argv[i], "--no-show-locs"))
- opts.show_locs = false;
- else if (!strcmp(argv[i], "--show-bytes"))
- opts.show_offsets_sizes_in_bits = false;
- else if (!strcmp(argv[i], "--show-bits"))
- opts.show_offsets_sizes_in_bits = true;
- else if (!strcmp(argv[i], "--show-hex"))
- opts.show_hexadecimal_values = true;
- else if (!strcmp(argv[i], "--show-dec"))
- opts.show_hexadecimal_values = false;
- else if (!strcmp(argv[i], "--no-show-relative-offset-changes"))
- opts.show_relative_offset_changes = false;
- else if (!strcmp(argv[i], "--no-added-syms"))
- opts.show_added_syms = false;
- else if (!strcmp(argv[i], "--no-unreferenced-symbols"))
- opts.show_symbols_not_referenced_by_debug_info = false;
- else if (!strcmp(argv[i], "--no-added-binaries"))
- opts.show_added_binaries = false;
- else if (!strcmp(argv[i], "--fail-no-dbg"))
- opts.fail_if_no_debug_info = true;
- else if (!strcmp(argv[i], "--no-leverage-dwarf-factorization"))
- opts.leverage_dwarf_factorization = false;
- else if (!strcmp(argv[i], "--no-assume-odr-for-cplusplus"))
- opts.assume_odr_for_cplusplus = false;
- else if (!strcmp(argv[i], "--verbose"))
- opts.verbose = true;
- else if (!strcmp(argv[i], "--verbose-diff"))
- {
- opts.verbose_diff = true;
- opts.verbose = true;
- }
- else if (!strcmp(argv[i], "--no-abignore"))
- opts.abignore = false;
- else if (!strcmp(argv[i], "--no-parallel"))
- opts.parallel = false;
- else if (!strcmp(argv[i], "--show-identical-binaries"))
- opts.show_identical_binaries = true;
- else if (!strcmp(argv[i], "--self-check"))
- opts.self_check = true;
- else if (!strcmp(argv[i], "--suppressions")
- || !strcmp(argv[i], "--suppr"))
- {
- int j = i + 1;
- if (j >= argc)
- return false;
- opts.suppression_paths.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--linux-kernel-abi-whitelist")
- || !strcmp(argv[i], "-w"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- if (guess_file_type(argv[j]) == abigail::tools_utils::FILE_TYPE_RPM)
- // The kernel abi stablelist is actually a stablelist
- // *package*. Take that into account.
- opts.kabi_stablelist_packages.push_back
- (make_path_absolute(argv[j]).get());
- else
- // We assume the kernel abi stablelist is a white list
- // file.
- opts.kabi_stablelist_paths.push_back(argv[j]);
- ++i;
- }
- else if (!strcmp(argv[i], "--wp"))
- {
- int j = i + 1;
- if (j >= argc)
- {
- opts.missing_operand = true;
- opts.wrong_option = argv[i];
- return true;
- }
- opts.kabi_stablelist_packages.push_back
- (make_path_absolute(argv[j]).get());
- ++i;
- }
+static const struct argp_option argp_options[] =
+{
+ { "allow-non-exported-interfaces", OPT_ALLOW_NON_EXPORTED_INTERFACES, 0, 0,
+ "analyze interfaces that might not be exported", 0 },
+#ifdef WITH_BTF
+ { "btf", OPT_BTF, 0, 0,
+ "use BTF instead of DWARF in ELF files", 0 },
+#endif
#ifdef WITH_CTF
- else if (!strcmp(argv[i], "--ctf"))
- opts.use_ctf = true;
+ { "ctf", OPT_CTF, 0, 0,
+ "use CTF instead of DWARF in ELF files", 0 },
#endif
+ { "debug-info-pkg1", OPT_D1, "PATH", 0,
+ "path of debug-info package of package1", 0 },
+ { "d1", OPT_D1, "PATH", OPTION_ALIAS, 0, 0 },
+ { "debug-info-pkg2", OPT_D2, "PATH", 0,
+ "path of debug-info package of package2", 0 },
+ { "d2", OPT_D2, "PATH", OPTION_ALIAS, 0, 0 },
+ { "devel-pkg1", OPT_DEVEL1, "PATH", 0,
+ "path of devel package of package1", 0 },
+ { "devel1", OPT_DEVEL1, "PATH", OPTION_ALIAS, 0, 0 },
+ { "devel-pkg2", OPT_DEVEL2, "PATH", 0,
+ "path of devel package of package2", 0 },
+ { "devel2", OPT_DEVEL2, "PATH", OPTION_ALIAS, 0, 0 },
+ { "drop-private-types", OPT_DROP_PRIVATE_TYPES, 0, 0,
+ "drop private types from internal representation", 0 },
+ { "dso-only", OPT_DSO_ONLY, 0, 0,
+ "compare shared libraries only", 0 },
+ { "exported-interfaces-only", OPT_EXPORTED_INTERFACES_ONLY, 0, 0,
+ "analyze exported interfaces only", 0 },
+ { "fail-no-dbg", OPT_FAIL_NO_DBG, 0, 0,
+ "fail if no debug info was found", 0 },
+ { "full-impact", OPT_FULL_IMPACT, 0, 0,
+ "when comparing kernel packages, show the full impact analysis report "
+ "rather than the default leaf changes reports", 0 },
+ { "f", OPT_FULL_IMPACT, 0, OPTION_ALIAS, 0, 0 },
+ { "harmless", OPT_HARMLESS, 0, 0,
+ "display the harmless changes", 0 },
+ { "ignore-soname", OPT_IGNORE_SONAME, 0, 0,
+ "do not take the SONAMEs into account", 0 },
+ { "impacted-interfaces", OPT_IMPACTED_INTERFACES, 0, 0,
+ "display interfaces impacted by leaf changes", 0 },
+ { "i", OPT_IMPACTED_INTERFACES, 0, OPTION_ALIAS, 0, 0 },
+ { "keep-tmp-files", OPT_KEEP_TMP_FILES, 0, 0,
+ "don't erase created temporary files", 0 },
+ { "linux-kernel-abi-whitelist", OPT_KMI_WHITELIST, "PATH", 0,
+ "path to a linux kernel abi whitelist", 0 },
+ { "w", OPT_KMI_WHITELIST, "PATH", OPTION_ALIAS, 0, 0 },
+ { "wp", OPT_KMI_WHITELIST_PKG, "PATH", 0,
+ "path to a linux kernel abi whitelist package", 0 },
+ { "leaf-changes-only", OPT_LEAF_CHANGES_ONLY, 0, 0,
+ "only show leaf changes, so no change impact analysis "
+ "(implies --redundant)", 0 },
+ { "l", OPT_LEAF_CHANGES_ONLY, 0, OPTION_ALIAS, 0, 0 },
+ { "no-abignore", OPT_NO_ABIGNORE, 0, 0,
+ "do not look for *.abignore files", 0 },
+ { "no-added-binaries", OPT_NO_ADDED_BINARIES, 0, 0,
+ "do not display added binaries", 0 },
+ { "no-added-syms", OPT_NO_ADDED_SYMS, 0, 0,
+ "do not display added functions or variables", 0 },
+ { "no-assume-odr-for-cplusplus", OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS, 0, 0,
+ "do not assume the ODR to speed-up the analysis of the binary", 0 },
+ { "no-default-suppression", OPT_NO_DEFAULT_SUPPRESSION, 0, 0,
+ "don't load any default suppression specifications", 0 },
+ { "no-leverage-dwarf-factorization", OPT_NO_LEVERAGE_DWARF_FACTORIZATION,
+ 0, 0,
+ "do not use DWZ optimisations to speed-up the analysis of the binary",
+ 0 },
+ { "no-linkage-name", OPT_NO_LINKAGE_NAME, 0, 0,
+ "do not display linkage names of added/removed/changed", 0 },
+ { "no-parallel", OPT_NO_PARALLEL, 0, 0,
+ "do not execute in parallel", 0 },
+ { "no-show-locs", OPT_NO_SHOW_LOCS, 0, 0,
+ "do not show location information", 0 },
+ { "no-show-relative-offset-changes",
+ OPT_NO_SHOW_RELATIVE_OFFSET_CHANGES, 0, 0,
+ "do not show relative offset changes", 0 },
+ { "no-unreferenced-symbols", OPT_NO_UNREFERENCED_SYMBOLS, 0, 0,
+ "do not display changes about symbols not referenced by debug info", 0 },
+ { "non-reachable-types", OPT_NON_REACHABLE_TYPES, 0, 0,
+ "consider types non reachable from public interfaces", 0 },
+ { "t", OPT_NON_REACHABLE_TYPES, 0, OPTION_ALIAS, 0, 0 },
+ { "private-dso", OPT_PRIVATE_DSO, 0, 0,
+ "compare DSOs that are private to the package as well", 0 },
+ { "redundant", OPT_REDUNDANT, 0, 0,
+ "display redundant changes", 0 },
+ { "self-check", OPT_SELF_CHECK, 0, 0,
+ "perform a sanity check by comparing binaries inside the input package "
+ "against their ABIXML representation", 0 },
+ { "set1", OPT_SET1, "PKG1[,PKG2,..] | PKG1[ PKG2 ..]", 0,
+ "the first set of packages to compare", 0 },
+ { "set2", OPT_SET2, "PKG1[,PKG2,..] | PKG1[ PKG2 ..]", 0,
+ "the second set of packages to compare", 0 },
+ { "show-bits", OPT_SHOW_BITS, 0, 0,
+ "show size and offsets in bits", 0 },
+ { "show-bytes", OPT_SHOW_BYTES, 0, 0,
+ "show size and offsets in bytes", 0 },
+ { "show-dec", OPT_SHOW_DEC, 0, 0,
+ "show size and offset in decimal", 0 },
+ { "show-hex", OPT_SHOW_HEX, 0, 0,
+ "show size and offset in hexadecimal", 0 },
+ { "show-identical-binaries", OPT_SHOW_IDENTICAL_BINARIES, 0, 0,
+ "show the names of identical binaries", 0 },
+ { "suppressions", OPT_SUPPR, "PATH", 0,
+ "specify a suppression file", 0 },
+ { "suppr", OPT_SUPPR, "PATH", OPTION_ALIAS, 0, 0 },
+ { "verbose", OPT_VERBOSE, 0, 0,
+ "emit verbose progress messages", 0 },
+ { "verbose-diff", OPT_VERBOSE_DIFF, 0, 0,
+ "emit verbose diff progress messages", 0 },
+ { 0, 0, 0, 0, 0, 0 }
+};
+
+static error_t
+parse_opt(int key, char* arg, struct argp_state* state)
+{
+ options& opts = *static_cast<options*>(state->input);
+ const string argument = arg ? string(arg) : string();
+
+ if (key != OPT_SET1
+ && key != OPT_SET2
+ && key != ARGP_KEY_ARG)
+ // Reset the opts.last_seen_set if we see an option that is neiter
+ // --set1 not --set2.
+ opts.last_seen_set = 0;
+
+
+ switch (key)
+ {
+ case OPT_ALLOW_NON_EXPORTED_INTERFACES:
+ opts.exported_interfaces_only = false;
+ break;
+
#ifdef WITH_BTF
- else if (!strcmp(argv[i], "--btf"))
- opts.use_btf = true;
+ case OPT_BTF:
+ opts.use_btf = true;
+ break;
#endif
- else if (!strcmp(argv[i], "--ignore-soname"))
- {
- opts.ignore_soname = true;
- opts.show_identical_binaries = true;
- }
- else if (!strcmp(argv[i], "--help")
- || !strcmp(argv[i], "-h"))
- {
- opts.display_usage = true;
- return true;
- }
- else if (!strcmp(argv[i], "--version")
- || !strcmp(argv[i], "-v"))
- {
- opts.display_version = true;
- return true;
- }
+
+#ifdef WITH_CTF
+ case OPT_CTF:
+ opts.use_ctf = true;
+ break;
+#endif
+
+ case OPT_D1:
+ opts.debug_packages1.push_back
+ (abigail::tools_utils::make_path_absolute(argument));
+ break;
+
+ case OPT_D2:
+ opts.debug_packages2.push_back
+ (abigail::tools_utils::make_path_absolute(argument));
+ break;
+
+ case OPT_DEVEL1:
+ opts.devel_package1 =
+ abigail::tools_utils::make_path_absolute(argument);
+ break;
+
+ case OPT_DEVEL2:
+ opts.devel_package2 =
+ abigail::tools_utils::make_path_absolute(argument);
+ break;
+
+ case OPT_DROP_PRIVATE_TYPES:
+ opts.drop_private_types = true;
+ break;
+
+ case OPT_DSO_ONLY:
+ opts.compare_dso_only = true;
+ break;
+
+ case OPT_EXPORTED_INTERFACES_ONLY:
+ opts.exported_interfaces_only = true;
+ break;
+
+ case OPT_FAIL_NO_DBG:
+ opts.fail_if_no_debug_info = true;
+ break;
+
+ case OPT_FULL_IMPACT:
+ opts.show_full_impact_report = true;
+ break;
+
+ case OPT_HARMLESS:
+ opts.show_harmless_changes = true;
+ break;
+
+ case OPT_IGNORE_SONAME:
+ opts.ignore_soname = true;
+ opts.show_identical_binaries = true;
+ break;
+
+ case OPT_IMPACTED_INTERFACES:
+ opts.show_impacted_interfaces = true;
+ break;
+
+ case OPT_KEEP_TMP_FILES:
+ opts.keep_tmp_files = true;
+ break;
+
+ case OPT_KMI_WHITELIST:
+ if (guess_file_type(argument) == abigail::tools_utils::FILE_TYPE_RPM)
+ opts.kabi_stablelist_packages.push_back(make_path_absolute(argument));
else
- {
- if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
- opts.wrong_option = argv[i];
- return false;
- }
+ opts.kabi_stablelist_paths.push_back(argument);
+ break;
+
+ case OPT_KMI_WHITELIST_PKG:
+ opts.kabi_stablelist_packages.push_back
+ (make_path_absolute(argument));
+ break;
+
+ case OPT_LEAF_CHANGES_ONLY:
+ opts.leaf_changes_only = true;
+ break;
+
+ case OPT_NO_ABIGNORE:
+ opts.abignore = false;
+ break;
+
+ case OPT_NO_ADDED_BINARIES:
+ opts.show_added_binaries = false;
+ break;
+
+ case OPT_NO_ADDED_SYMS:
+ opts.show_added_syms = false;
+ break;
+
+ case OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS:
+ opts.assume_odr_for_cplusplus = false;
+ break;
+
+ case OPT_NO_DEFAULT_SUPPRESSION:
+ opts.no_default_suppression = true;
+ break;
+
+ case OPT_NO_LEVERAGE_DWARF_FACTORIZATION:
+ opts.leverage_dwarf_factorization = false;
+ break;
+
+ case OPT_NO_LINKAGE_NAME:
+ opts.show_linkage_names = false;
+ break;
+
+ case OPT_NO_PARALLEL:
+ opts.parallel = false;
+ break;
+
+ case OPT_NO_SHOW_LOCS:
+ opts.show_locs = false;
+ break;
+
+ case OPT_NO_SHOW_RELATIVE_OFFSET_CHANGES:
+ opts.show_relative_offset_changes = false;
+ break;
+
+ case OPT_NO_UNREFERENCED_SYMBOLS:
+ opts.show_symbols_not_referenced_by_debug_info = false;
+ break;
+
+ case OPT_NON_REACHABLE_TYPES:
+ opts.show_all_types = true;
+ break;
+
+ case OPT_PRIVATE_DSO:
+ opts.compare_private_dsos = true;
+ break;
+
+ case OPT_REDUNDANT:
+ opts.show_redundant_changes = true;
+ break;
+
+ case OPT_SELF_CHECK:
+ opts.self_check = true;
+ break;
+
+ case OPT_SET1:
+ {
+ opts.last_seen_set = 1;
+ vector<string> paths;
+ if (!tools_utils::split_string(argument, ",", paths)
+ && !argument.empty())
+ paths.push_back(argument);
+ for (auto& p : paths)
+ {
+ string abs_p = make_path_absolute(p);
+ if (!file_exists(abs_p))
+ {
+ opts.nonexistent_file = true;
+ opts.wrong_args.push_back(abs_p);
+ }
+ else
+ opts.package_set_paths1.insert(abs_p);
+ }
+ }
+ break;
+
+ case OPT_SET2:
+ {
+ opts.last_seen_set = 2;
+ vector<string> paths;
+ if (!tools_utils::split_string(argument, ",", paths)
+ && !argument.empty())
+ paths.push_back(argument);
+ for (auto& p : paths)
+ {
+ string abs_p = make_path_absolute(p);
+ if (!file_exists(abs_p))
+ {
+ opts.nonexistent_file = true;
+ opts.wrong_args.push_back(abs_p);
+ }
+ else
+ opts.package_set_paths2.insert(abs_p);
+ }
+ }
+ break;
+
+ case OPT_SHOW_BITS:
+ opts.show_offsets_sizes_in_bits = true;
+ break;
+
+ case OPT_SHOW_BYTES:
+ opts.show_offsets_sizes_in_bits = false;
+ break;
+
+ case OPT_SHOW_DEC:
+ opts.show_hexadecimal_values = false;
+ break;
+
+ case OPT_SHOW_HEX:
+ opts.show_hexadecimal_values = true;
+ break;
+
+ case OPT_SHOW_IDENTICAL_BINARIES:
+ opts.show_identical_binaries = true;
+ break;
+
+ case OPT_SUPPR:
+ opts.suppression_paths.push_back(argument);
+ break;
+
+ case OPT_VERBOSE:
+ opts.verbose = true;
+ break;
+
+ case OPT_VERBOSE_DIFF:
+ opts.verbose_diff = true;
+ opts.verbose = true;
+ break;
+
+ case ARGP_KEY_ARG:
+ {
+ string path = make_path_absolute(argument);
+ if (!file_exists(path))
+ {
+ // This is later handled in the main function so that the
+ // program can return an appropriate return code from
+ // there, so we don't need an ARGP_KEY_END case to handle
+ // it.
+ opts.nonexistent_file = true;
+ opts.wrong_args.push_back(path);
+ }
+ else if (opts.last_seen_set == 1)
+ opts.package_set_paths1.insert(path);
+ else if (opts.last_seen_set == 2)
+ opts.package_set_paths2.insert(path);
+ else
+ {
+ if (opts.package_set_paths1.empty())
+ opts.package_set_paths1.insert(path);
+ else if (opts.package_set_paths2.empty())
+ opts.package_set_paths2.insert(path);
+ else
+ argp_usage(state);
+ }
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
}
+ return 0;
+}
+
+static const char* argp_args_doc = "[<package1> <package2>]";
+static const char* argp_doc =
+ "Compare the ABI of binaries in two packages, returning 0 if they are "
+ "identical, or non-zero if they differ.";
+
+static const struct argp abipkgdiff_argp =
+{
+ argp_options,
+ parse_opt,
+ argp_args_doc,
+ argp_doc,
+ 0,
+ 0,
+ 0
+};
+
+/// Version printing hook to be passed to ARGP.
+///
+/// @param stream the output stream.
+///
+/// @param state the ARGP state.
+static void
+print_abipkgdiff_version(FILE *stream, struct argp_state* /*state*/)
+{
+ fprintf(stream, "abipkgdiff %s\n",
+ abigail::tools_utils::get_library_version_string().c_str());
+}
+
+typedef void (*program_version_hook_type) (FILE *__restrict __stream,
+ struct argp_state *__restrict __state);
+
+/// Parse the command line
+///
+/// @param argc number of args
+///
+/// @param argv the array of arguments.
+///
+/// @param opts the options set as result of command line parsing.
+static bool
+parse_command_line(int argc, char* argv[], options& opts)
+{
+ argp_program_version_hook = print_abipkgdiff_version;
+ argp_program_bug_address = "<libabigail@sourceware.org>";
+
+ if (argp_parse(&abipkgdiff_argp, argc, argv, 0, 0, &opts) != 0)
+ return false;
return true;
}
@@ -4043,34 +4174,39 @@ main(int argc, char* argv[])
if (!parse_command_line(argc, argv, opts))
{
- if (!opts.wrong_option.empty())
- emit_prefix("abipkgdiff", cerr)
- << "unrecognized option: " << opts.wrong_option
- << "\ntry the --help option for more information\n";
- if (!opts.wrong_arg.empty())
- emit_prefix("abipkgdiff", cerr)
- << "unrecognized argument: " << opts.wrong_arg
- << "\ntry the --help option for more information\n";
- return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
- | abigail::tools_utils::ABIDIFF_ERROR);
- }
-
- if (opts.missing_operand)
- {
- emit_prefix("abipkgdiff", cerr)
- << "missing operand\n"
- "try the --help option for more information\n";
+ char* prog_name = (char*) "abipkgdiff";
+ argp_help(&abipkgdiff_argp, stderr, ARGP_HELP_USAGE, prog_name);
return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
| abigail::tools_utils::ABIDIFF_ERROR);
}
if (opts.nonexistent_file)
{
+ vector<string> input_files;
string input_file;
- base_name(opts.wrong_arg, input_file);
- emit_prefix("abipkgdiff", cerr)
- << "The input file " << input_file << " doesn't exist\n"
- "try the --help option for more information\n";
+ for (auto& f : opts.wrong_args)
+ {
+ base_name(f, input_file);
+ input_files.push_back(input_file);
+ }
+ bool plural = input_files.size() > 1;
+
+ if (plural)
+ emit_prefix("abipkgdiff", cerr)
+ << "Input files";
+ else
+ emit_prefix("abipkgdiff", cerr)
+ << "The input file";
+ for (auto& f : input_files)
+ cerr << " " << f;
+
+ if (plural)
+ cerr << " don't exist\n";
+ else
+ cerr << " doesn't exist\n";
+
+ cerr << "try the --help option for more information\n";
+
return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
| abigail::tools_utils::ABIDIFF_ERROR);
}
@@ -4083,21 +4219,6 @@ main(int argc, char* argv[])
| abigail::tools_utils::ABIDIFF_ERROR);
}
- if (opts.display_usage)
- {
- display_usage(argv[0], cout);
- return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
- | abigail::tools_utils::ABIDIFF_ERROR);
- }
-
- if (opts.display_version)
- {
- emit_prefix(argv[0], cout)
- << abigail::tools_utils::get_library_version_string()
- << "\n";
- return 0;
- }
-
if (!opts.no_default_suppression && opts.suppression_paths.empty())
{
// Load the default system and user suppressions.
@@ -4129,9 +4250,11 @@ main(int argc, char* argv[])
}
if (opts.package_set_paths1.empty())
{
+ char* prog_name = (char*) "abipkgdiff";
// We need at least one package to work with!
emit_prefix("abipkgdiff", cerr)
<< "missing input package\n";
+ argp_help(&abipkgdiff_argp, stderr, ARGP_HELP_USAGE, prog_name);
if (bail_out)
return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
| abigail::tools_utils::ABIDIFF_ERROR);
@@ -4139,8 +4262,10 @@ main(int argc, char* argv[])
}
else if(opts.package_set_paths1.empty() && opts.package_set_paths2.empty())
{
+ char* prog_name = (char*) "abipkgdiff";
emit_prefix("abipkgdiff", cerr)
<< "please enter two packages to compare" << "\n";
+ argp_help(&abipkgdiff_argp, stderr, ARGP_HELP_USAGE, prog_name);
return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
| abigail::tools_utils::ABIDIFF_ERROR);
}
@@ -4217,9 +4342,11 @@ main(int argc, char* argv[])
if (is_kernel_package(first_package_set)
!= is_kernel_package(second_package_set))
{
+ char* prog_name = (char*) "abipkgdiff";
emit_prefix("abipkgdiff", cerr)
<< "a Linux kernel package can only be compared to another "
"Linux kernel package\n";
+ argp_help(&abipkgdiff_argp, stderr, ARGP_HELP_USAGE, prog_name);
return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
| abigail::tools_utils::ABIDIFF_ERROR);
}
@@ -4228,9 +4355,11 @@ main(int argc, char* argv[])
|| (!second_package_set->path().empty()
&& second_package_set->debug_info_packages().empty()))
{
+ char* prog_name = (char*) "abipkgdiff";
emit_prefix("abipkgdiff", cerr)
<< "a Linux Kernel package must be accompanied with its "
"debug info package\n";
+ argp_help(&abipkgdiff_argp, stderr, ARGP_HELP_USAGE, prog_name);
return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
| abigail::tools_utils::ABIDIFF_ERROR);
}
@@ -10,6 +10,7 @@
/// This program takes parameters to open an elf file, lookup a symbol
/// in its symbol tables and report what it sees.
+#include <argp.h>
#include <libgen.h>
#include <elf.h>
#include <cstring>
@@ -34,76 +35,112 @@ using abigail::elf_symbol_sptr;
struct options
{
- bool show_help;
- bool display_version;
- char* elf_path;
- char* symbol_name;
+ string elf_path;
+ string symbol_name;
bool demangle;
bool absolute_path;
options()
- : show_help(false),
- display_version(false),
- elf_path(0),
- symbol_name(0),
- demangle(false),
+ : demangle(false),
absolute_path(true)
{}
};
+
+enum option_key
+{
+ OPT_DEMANGLE = 256,
+ OPT_NO_ABSOLUTE_PATH,
+};
+
+static const struct argp_option argp_options[] =
+{
+ { "demangle", OPT_DEMANGLE, 0, 0,
+ "demangle the symbols from the symbol table", 0 },
+ { "no-absolute-path", OPT_NO_ABSOLUTE_PATH, 0, 0,
+ "do not show absolute paths in messages", 0 },
+ { 0, 0, 0, 0, 0, 0 }
+};
+
+static error_t
+parse_opt(int key, char* arg, struct argp_state* state)
+{
+ options& opts = *static_cast<options*>(state->input);
+ const string argument = arg ? string(arg) : string();
+
+ switch (key)
+ {
+ case OPT_DEMANGLE:
+ opts.demangle = true;
+ break;
+
+ case OPT_NO_ABSOLUTE_PATH:
+ opts.absolute_path = false;
+ break;
+
+ case ARGP_KEY_ARG:
+ if (opts.elf_path.empty())
+ opts.elf_path = argument;
+ else if (opts.symbol_name.empty())
+ opts.symbol_name = argument;
+ else
+ argp_usage(state);
+ break;
+
+ case ARGP_KEY_END:
+ if (opts.elf_path.empty() || opts.symbol_name.empty())
+ argp_usage(state);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static const char* argp_args_doc = "<elf-file> <symbol-name>";
+static const char* argp_doc =
+ "Look up a symbol in the symbol table of an ELF file and report "
+ "information about it.";
+
+static const struct argp abisym_argp =
+{
+ argp_options,
+ parse_opt,
+ argp_args_doc,
+ argp_doc,
+ 0,
+ 0,
+ 0
+};
+
+/// Version printing hook to be passed to ARGP.
+///
+/// @param stream the output stream.
+///
+/// @param state the ARGP state.
static void
-display_usage(const string& prog_name, ostream &out)
+print_abisym_version(FILE *stream, struct argp_state* /*state*/)
{
- out << "usage: " << prog_name << " [options] <elf file> <symbol-name>\n"
- << "where [options] can be:\n"
- << " --help display this help string\n"
- << " --version|-v display program version information and exit\n"
- << " --demangle demangle the symbols from the symbol table\n"
- << " --no-absolute-path do not show absolute paths in messages\n";
+ fprintf(stream, "abisym %s\n",
+ abigail::tools_utils::get_library_version_string().c_str());
}
+/// Parse the command line
+///
+/// @param argc number of args
+///
+/// @param argv the array of arguments.
+///
+/// @param opts the options set as result of command line parsing.
static void
parse_command_line(int argc, char* argv[], options& opts)
{
- if (argc < 2)
- {
- opts.show_help = true;
- return;
- }
+ argp_program_version_hook = print_abisym_version;
+ argp_program_bug_address = "<libabigail@sourceware.org>";
- for (int i = 1; i < argc; ++i)
- {
- if (argv[i][0] != '-')
- {
- if (!opts.elf_path)
- opts.elf_path = argv[i];
- else if (!opts.symbol_name)
- opts.symbol_name = argv[i] ;
- else
- {
- opts.show_help = true;
- return;
- }
- }
- else if (!strcmp(argv[i], "--help")
- || !strcmp(argv[i], "-h"))
- {
- opts.show_help = true;
- return;
- }
- else if (!strcmp(argv[i], "--version")
- || !strcmp(argv[i], "-v"))
- {
- opts.display_version = true;
- return;
- }
- else if (!strcmp(argv[i], "--demangle"))
- opts.demangle = true;
- else if (!strcmp(argv[i], "--no-absolute-path"))
- opts.absolute_path = false;
- else
- opts.show_help = true;
- }
+ argp_parse(&abisym_argp, argc, argv, 0, 0, &opts);
}
int
@@ -112,23 +149,6 @@ main(int argc, char* argv[])
options opts;
parse_command_line(argc, argv, opts);
- if (opts.show_help)
- {
- display_usage(argv[0], cout);
- return 1;
- }
-
- if (opts.display_version)
- {
- abigail::tools_utils::emit_prefix(argv[0], cout)
- << abigail::tools_utils::get_library_version_string()
- << "\n";
- return 0;
- }
-
- assert(opts.elf_path != 0
- && opts.symbol_name != 0);
-
string p = opts.elf_path, n = opts.symbol_name;
environment env;
vector<elf_symbol_sptr> syms;
@@ -140,7 +160,11 @@ main(int argc, char* argv[])
if (opts.absolute_path)
cout << opts.elf_path << "'\n";
else
- cout << basename(opts.elf_path);
+ {
+ string b;
+ abigail::tools_utils::base_name(opts.elf_path, b);
+ cout << b;
+ }
return 0;
}
@@ -12,9 +12,11 @@
/// abigail::ini::* functions, but one could also use it to make sure
/// that an ini file can be handled by the abigail::ini::* facilities
+#include <argp.h>
#include <cstring>
#include <iostream>
#include "abg-ini.h"
+#include "abg-tools-utils.h"
using std::cout;
using std::cerr;
@@ -28,83 +30,108 @@ using abigail::ini::write_config;
struct options
{
- bool display_usage;
bool read_from_stdin;
bool no_out;
- bool wrong_command_line_usage;
string path;
options ()
- : display_usage(false),
- read_from_stdin(false),
- no_out(false),
- wrong_command_line_usage(false)
+ : read_from_stdin(false),
+ no_out(false)
{}
};
+enum option_key
+{
+ OPT_FROM_STDIN = 256,
+ OPT_NOOUT,
+};
+
+static const struct argp_option argp_options[] =
+{
+ { "from-stdin", OPT_FROM_STDIN, 0, 0,
+ "read the input ini file from stdin", 0 },
+ { "noout", OPT_NOOUT, 0, 0,
+ "do not output anything on stdout", 0 },
+ { 0, 0, 0, 0, 0, 0 }
+};
+
+static error_t
+parse_opt(int key, char* arg, struct argp_state* state)
+{
+ options& opts = *static_cast<options*>(state->input);
+ const string argument = arg ? string(arg) : string();
+
+ switch (key)
+ {
+ case OPT_FROM_STDIN:
+ opts.read_from_stdin = true;
+ break;
+
+ case OPT_NOOUT:
+ opts.no_out = true;
+ break;
+
+ case ARGP_KEY_ARG:
+ if (opts.path.empty())
+ opts.path = argument;
+ else
+ argp_usage(state);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static const char* argp_args_doc = "[<ini-file>]";
+static const char* argp_doc =
+ "Read an ini file and print it back on standard output.";
+
+static const struct argp binilint_argp =
+{
+ argp_options,
+ parse_opt,
+ argp_args_doc,
+ argp_doc,
+ 0,
+ 0,
+ 0
+};
+
static void
-display_usage(const string& prog_name, ostream& out)
+print_binilint_version(FILE *stream, struct argp_state* /*state*/)
{
- out << "usage: " << prog_name << " [options] <ini file>\n"
- << "where options can be:\n"
- << "--help display this help\n"
- << "--from-stdin read the input ini file from stdin\n"
- << "--noout do not output anything on stdout\n"
- ;
+ fprintf(stream, "abinilint %s\n",
+ abigail::tools_utils::get_library_version_string().c_str());
}
static bool
parse_command_line(int argc, char* argv[], options& opts)
{
- if (argc == 1)
- return false;
+ argp_program_version_hook = print_binilint_version;
+ argp_program_bug_address = "<libabigail@sourceware.org>";
- for (int i = 1; i < argc; ++i)
- {
- if (argv[i][0] != '-')
- {
- if (opts.path.empty())
- opts.path = argv[i];
- else
- opts.wrong_command_line_usage = true;
- }
- else if (!strcmp(argv[i], "--help"))
- opts.display_usage = true;
- else if (!strcmp(argv[i], "--from-stdin"))
- opts.read_from_stdin = true;
- else if (!strcmp(argv[i], "--noout"))
- opts.no_out = true;
- else
- return false;
- }
+ if (argp_parse(&binilint_argp, argc, argv, 0, 0, &opts) != 0)
+ return false;
return true;
}
int
main(int argc, char* argv[])
{
- // First, parse the command line.
-
options opts;
-
- if (argc == 1 || !parse_command_line(argc, argv, opts))
+ if (!parse_command_line(argc, argv, opts))
{
- cerr << argv[0] << ": bad command usage\n"
- << "Try " << argv[0] << " --help for more information\n";
+ char* prog_name = (char*) "abinilint";
+ argp_help(&binilint_argp, stderr, ARGP_HELP_USAGE, prog_name);
return 1;
}
- // Then if we need to display the help, display it and get out.
-
- if (opts.display_usage)
- {
- display_usage(argv[0], cout);
- return 0;
- }
-
- // Otherwise, do the real work we are supposed to do after all.
- // That real work is driven by the options the user set; these
- // options are recorded in the opts variable.
+ // Do the real work we are supposed to do after all. That real work
+ // is driven by the options the user set; these options are recorded
+ // in the opts variable.
config_sptr conf;