abipkgdiff: Add a new --self-check option

After the report of
https://sourceware.org/bugzilla/show_bug.cgi?id=26769 it became
apparent that we want to be able compare a binary against its ABIXML
representation, including for cases where the binaries are embedded in
RPM packages.

This patches thus introduces the --self-check option to abipkgdiff so
that it can be invoked like this:

    $ abipkgdiff --self-check --d1 libstdc++-debuginfo-10.2.1-8.fc34.x86_64.rpm --d1 gcc-debuginfo-10.2.1-8.fc34.x86_64.rpm libstdc++-10.2.1-8.fc34.x86_64.rpm

    ==== SELF CHECK SUCCEEDED for 'libstdc++.so.6.0.28'====
    $

With this option, libabigail compares each binary in the RPM against
its own ABIXML representation.

This should hopefully help to write regression tests which have as
sole inputs the links to download the RPMs.  It's also useful to ease
the process of reproducing the issue raised.

This option can now be used, for instance, by the libabigail-selfcheck
program over at https://pagure.io/libabigail-selfcheck.

	* tools/abipkgdiff.cc (options::self_check): Define new data
	member.
	(options::options): Initialize it.
	(display_usage): Add help string for the --self-check option.
	(parse_command): Parse the new --self-check option.
	(extract_deb): Add missing newline.
	(compare): Remove useless white space.
	(compare_to_self, self_compare_prepared_userspace_package)
	(self_compare_prepared_package, compare_to_self): Add new static
	functions.
	(class self_compare_task): Add new class.
	(prepare_package): Add a new overload that takes just one
	parameter.
	(elf_size_is_greater): Don't crash if the args are empty.
	(main): If the --self-check option is given, make sure we have
	just one package in argument.  Use the new compare_to_self
	function to handle the --self-check option.
	* doc/manuals/abipkgdiff.rst: Add documentation for the new
	--self-check option.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
This commit is contained in:
Dodji Seketeli 2020-11-19 17:52:08 +01:00
parent cac59a176a
commit edfe5a1dec
2 changed files with 505 additions and 16 deletions

View File

@ -451,6 +451,23 @@ Options
Emit verbose progress messages. Emit verbose progress messages.
* ``self-check``
This is used to test the underlying Libabigail library. When in
used, the command expects only on input package, along with its
associated debug info packages. The command then compares each
binary inside the package against its own ABIXML
representation. The result of the comparison should yield the
empty set if Libabigail behaves correctly. Otherwise, it means
there is an issue that ought to be fixed. This option is used by
people interested in Libabigail development for regression testing
purposes. Here is an example of the use of this option: ::
$ abipkgdiff --self-check --d1 mesa-libGLU-debuginfo-9.0.1-3.fc33.x86_64.rpm mesa-libGLU-9.0.1-3.fc33.x86_64.rpm
==== SELF CHECK SUCCEEDED for 'libGLU.so.1.3.1' ====
$
.. _abipkgdiff_return_value_label: .. _abipkgdiff_return_value_label:
Return value Return value

View File

@ -87,6 +87,7 @@
#endif #endif
#include <iostream> #include <iostream>
#include <fstream>
#include <string> #include <string>
#include <cstring> #include <cstring>
#include <cstdlib> #include <cstdlib>
@ -101,11 +102,14 @@
#include "abg-comparison.h" #include "abg-comparison.h"
#include "abg-suppression.h" #include "abg-suppression.h"
#include "abg-dwarf-reader.h" #include "abg-dwarf-reader.h"
#include "abg-reader.h"
#include "abg-writer.h"
using std::cout; using std::cout;
using std::cerr; using std::cerr;
using std::string; using std::string;
using std::ostream; using std::ostream;
using std::ofstream;
using std::vector; using std::vector;
using std::map; using std::map;
using abg_compat::unordered_set; using abg_compat::unordered_set;
@ -158,6 +162,13 @@ using abigail::dwarf_reader::create_read_context;
using abigail::dwarf_reader::get_soname_of_elf_file; using abigail::dwarf_reader::get_soname_of_elf_file;
using abigail::dwarf_reader::get_type_of_elf_file; using abigail::dwarf_reader::get_type_of_elf_file;
using abigail::dwarf_reader::read_corpus_from_elf; using abigail::dwarf_reader::read_corpus_from_elf;
using abigail::xml_reader::read_corpus_from_native_xml_file;
using abigail::xml_writer::HASH_TYPE_ID_STYLE;
using abigail::xml_writer::create_write_context;
using abigail::xml_writer::type_id_style_kind;
using abigail::xml_writer::write_context_sptr;
using abigail::xml_writer::write_corpus;
/// The options passed to the current program. /// The options passed to the current program.
class options class options
@ -203,6 +214,7 @@ public:
bool show_added_binaries; bool show_added_binaries;
bool fail_if_no_debug_info; bool fail_if_no_debug_info;
bool show_identical_binaries; bool show_identical_binaries;
bool self_check;
vector<string> kabi_whitelist_packages; vector<string> kabi_whitelist_packages;
vector<string> suppression_paths; vector<string> suppression_paths;
vector<string> kabi_whitelist_paths; vector<string> kabi_whitelist_paths;
@ -216,8 +228,8 @@ public:
nonexistent_file(), nonexistent_file(),
abignore(true), abignore(true),
parallel(true), parallel(true),
verbose(false), verbose(),
drop_private_types(false), drop_private_types(),
show_relative_offset_changes(true), show_relative_offset_changes(true),
no_default_suppression(), no_default_suppression(),
keep_tmp_files(), keep_tmp_files(),
@ -237,7 +249,8 @@ public:
show_symbols_not_referenced_by_debug_info(true), show_symbols_not_referenced_by_debug_info(true),
show_added_binaries(true), show_added_binaries(true),
fail_if_no_debug_info(), fail_if_no_debug_info(),
show_identical_binaries() show_identical_binaries(),
self_check()
{ {
// set num_workers to the default number of threads of the // set num_workers to the default number of threads of the
// underlying maching. This is the default value for the number // underlying maching. This is the default value for the number
@ -854,6 +867,8 @@ display_usage(const string& prog_name, ostream& out)
<< " --fail-no-dbg fail if no debug info was found\n" << " --fail-no-dbg fail if no debug info was found\n"
<< " --show-identical-binaries show the names of identical binaries\n" << " --show-identical-binaries show the names of identical binaries\n"
<< " --verbose emit verbose progress messages\n" << " --verbose emit verbose progress messages\n"
<< " --self-check perform a sanity check by comparing "
"binaries inside the input package against their ABIXML representation\n"
<< " --help|-h display this help message\n" << " --help|-h display this help message\n"
<< " --version|-v display program version information" << " --version|-v display program version information"
" and exit\n"; " and exit\n";
@ -927,7 +942,7 @@ extract_deb(const string& package_path,
<< package_path << package_path
<< " to " << " to "
<< extracted_package_dir_path << extracted_package_dir_path
<< " ..."; << " ...\n";
string cmd = "mkdir -p " + extracted_package_dir_path + " && dpkg -x " + string cmd = "mkdir -p " + extracted_package_dir_path + " && dpkg -x " +
package_path + " " + extracted_package_dir_path; package_path + " " + extracted_package_dir_path;
@ -1272,7 +1287,7 @@ compare(const elf_file& elf1,
if (opts.verbose) if (opts.verbose)
emit_prefix("abipkgdiff", cerr) emit_prefix("abipkgdiff", cerr)
<< " Reading file " << "Reading file "
<< elf1.path << elf1.path
<< " ...\n"; << " ...\n";
@ -1352,13 +1367,13 @@ compare(const elf_file& elf1,
if (opts.verbose) if (opts.verbose)
emit_prefix("abipkgdiff", cerr) emit_prefix("abipkgdiff", cerr)
<< " DONE reading file " << "DONE reading file "
<< elf1.path << elf1.path
<< "\n"; << "\n";
if (opts.verbose) if (opts.verbose)
emit_prefix("abipkgdiff", cerr) emit_prefix("abipkgdiff", cerr)
<< " Reading file " << "Reading file "
<< elf2.path << elf2.path
<< " ...\n"; << " ...\n";
@ -1466,6 +1481,190 @@ compare(const elf_file& elf1,
return s; return s;
} }
/// Compare an ELF file to its ABIXML representation.
///
/// @param elf the ELF file to compare.
///
/// @param debug_dir the debug directory of the ELF file.
///
/// @param opts the options passed the user.
///
/// @param env the environment to use for the comparison.
///
/// @param diff the diff object resulting from the comparison of @p
/// elf against its ABIXML representation.
///
/// @param ctxt the resulting diff context used for the comparison
/// that yielded @p diff.
///
/// @param detailed_error_status the detailed error satus returned by
/// this function.
///
/// @return the status of the self comparison.
static abidiff_status
compare_to_self(const elf_file& elf,
const string& debug_dir,
const options& opts,
abigail::ir::environment_sptr &env,
corpus_diff_sptr &diff,
diff_context_sptr &ctxt,
abigail::dwarf_reader::status *detailed_error_status = 0)
{
char *di_dir = (char*) debug_dir.c_str();
vector<char**> di_dirs;
di_dirs.push_back(&di_dir);
abigail::dwarf_reader::status c_status = abigail::dwarf_reader::STATUS_OK;
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
<< "Comparing the ABI of file '"
<< elf.path
<< "' against itself ...\n";
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
<< "Reading file "
<< elf.path
<< " ...\n";
corpus_sptr corp;
{
read_context_sptr c =
create_read_context(elf.path, di_dirs, env.get(),
/*load_all_types=*/opts.show_all_types);
corp = read_corpus_from_elf(*c, c_status);
if (!(c_status & abigail::dwarf_reader::STATUS_OK))
{
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
<< "Could not read file '"
<< elf.path
<< "' propertly\n";
if (detailed_error_status)
*detailed_error_status = c_status;
return abigail::tools_utils::ABIDIFF_ERROR;
}
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
<< "Read file '"
<< elf.path
<< "' OK\n";
ABG_ASSERT(corp);
}
corpus_sptr reread_corp;
string abi_file_path;
{
abi_file_path = elf.path + ".abi";
ofstream of(abi_file_path.c_str(), std::ios_base::trunc);
{
const abigail::xml_writer::write_context_sptr c =
abigail::xml_writer::create_write_context(env.get(), of);
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
<< "Writting ABIXML file '"
<< abi_file_path
<< "' ...\n";
if (!write_corpus(*c, corp, 0))
{
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
<< "Could not write the ABIXML file to '"
<< abi_file_path << "'\n";
return abigail::tools_utils::ABIDIFF_ERROR;
}
of.flush();
of.close();
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
<< "Wrote ABIXML file '"
<< abi_file_path
<< "' OK\n";
}
{
abigail::xml_reader::read_context_sptr c =
abigail::xml_reader::create_native_xml_read_context(abi_file_path,
env.get());
if (!c)
{
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
<< "Could not create read context for ABIXML file '"
<< abi_file_path << "'\n";
return abigail::tools_utils::ABIDIFF_ERROR;
}
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
<< "Reading ABIXML file '"
<< abi_file_path
<< "' ...\n";
reread_corp = read_corpus_from_input(*c);
if (!reread_corp)
{
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
<< "Could not read temporary ABIXML file '"
<< abi_file_path << "'\n";
return abigail::tools_utils::ABIDIFF_ERROR;
}
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
<< "Read file '"
<< abi_file_path
<< "' OK\n";
}
}
ctxt.reset(new diff_context);
set_diff_context_from_opts(ctxt, opts);
if (opts.verbose)
emit_prefix("abipkgdiff", cerr)
<< "Comparing the ABIs of: \n"
<< " '" << corp->get_path() << "' against \n"
<< " '" << abi_file_path << "'...\n";
diff = compute_diff(corp, reread_corp, ctxt);
if (opts.verbose)
emit_prefix("abipkgdfiff", cerr)
<< "... Comparing the ABIs: DONE\n";
abidiff_status s = abigail::tools_utils::ABIDIFF_OK;
if (diff->has_net_changes())
s |= abigail::tools_utils::ABIDIFF_ABI_CHANGE;
if (diff->has_incompatible_changes())
s |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
if (opts.verbose)
emit_prefix("abipkgdfiff", cerr)
<< (s == abigail::tools_utils::ABIDIFF_OK)
? string("Comparison against self SUCCEEDED\n")
: string("Comparison against self FAILED\n");
return s;
}
/// If devel packages were associated to the main package we are /// If devel packages were associated to the main package we are
/// looking at, use the names of the header files (extracted from the /// looking at, use the names of the header files (extracted from the
/// package) to generate suppression specification to filter out types /// package) to generate suppression specification to filter out types
@ -1834,6 +2033,86 @@ public:
/// Convenience typedef for a shared_ptr of @ref compare_task. /// Convenience typedef for a shared_ptr of @ref compare_task.
typedef shared_ptr<compare_task> compare_task_sptr; typedef shared_ptr<compare_task> compare_task_sptr;
/// The worker task which job is to compare an ELF binary to its ABI
/// representation.
class self_compare_task : public compare_task
{
public:
compare_args_sptr args;
abidiff_status status;
ostringstream out;
string pretty_output;
self_compare_task()
: status(abigail::tools_utils::ABIDIFF_OK)
{}
self_compare_task(const compare_args_sptr& a)
: args(a),
status(abigail::tools_utils::ABIDIFF_OK)
{}
/// The job performed by the task.
///
/// This compares an ELF file to its ABIXML representation and
/// expects the result to be the empty set.
virtual void
perform()
{
abigail::ir::environment_sptr env(new abigail::ir::environment);
diff_context_sptr ctxt;
corpus_diff_sptr diff;
abigail::dwarf_reader::status detailed_status =
abigail::dwarf_reader::STATUS_UNKNOWN;
status |= compare_to_self(args->elf1, args->debug_dir1,
args->opts, env, diff, ctxt,
&detailed_status);
string name = args->elf1.name;
if (status == abigail::tools_utils::ABIDIFF_OK)
pretty_output += "==== SELF CHECK SUCCEEDED for '"+ name + "' ====\n";
else if ((status & abigail::tools_utils::ABIDIFF_ABI_CHANGE)
||( diff && diff->has_net_changes()))
{
// There is an ABI change, tell the user about it.
diff->report(out, /*prefix=*/" ");
pretty_output +=
string("======== comparing'") + name +
"' to itself wrongly yielded result: ===========\n"
+ out.str()
+ "===SELF CHECK FAILED for '"+ name + "'\n";
}
// If an error happened while comparing the two binaries, tell the
// user about it.
if (status & abigail::tools_utils::ABIDIFF_ERROR)
{
string diagnostic =
abigail::dwarf_reader::status_to_diagnostic_string(detailed_status);
if (!diagnostic.empty())
{
string name = args->elf1.name;
pretty_output +=
"==== Error happened during self check of " + name + ": ====\n";
pretty_output += diagnostic;
pretty_output +=
"==== SELF CHECK FAILED for " + name + " ====\n";
}
}
}
}; // end class self_compare
/// Convenience typedef for a shared_ptr of @ref compare_task.
typedef shared_ptr<self_compare_task> self_compare_task_sptr;
/// This function is a sub-routine of create_maps_of_package_content. /// This function is a sub-routine of create_maps_of_package_content.
/// ///
/// It's called during the walking of the directory tree containing /// It's called during the walking of the directory tree containing
@ -2220,6 +2499,21 @@ prepare_packages(package_sptr &first_package,
return first_pkg_prepare->is_ok && second_pkg_prepare->is_ok; return first_pkg_prepare->is_ok && second_pkg_prepare->is_ok;
} }
/// Prepare one package for the sake of comparing it to its ABIXML
/// representation.
///
/// The preparation entails unpacking the content of the package into
/// a temporary directory and mapping its content.
///
/// @param pkg the package to prepare.
///
/// @param opts the options provided by the user.
///
/// @return true iff the preparation succeeded.
static bool
prepare_package(package_sptr& pkg, options &opts)
{return extract_package_and_map_its_content(pkg, opts);}
/// Compare the added sizes of an ELF pair (specified by a comparison /// Compare the added sizes of an ELF pair (specified by a comparison
/// task that compares two ELF files) against the added sizes of a /// task that compares two ELF files) against the added sizes of a
/// second ELF pair. /// second ELF pair.
@ -2244,8 +2538,8 @@ elf_size_is_greater(const task_sptr &task1,
compare_task_sptr t1 = dynamic_pointer_cast<compare_task>(task1); compare_task_sptr t1 = dynamic_pointer_cast<compare_task>(task1);
compare_task_sptr t2 = dynamic_pointer_cast<compare_task>(task2); compare_task_sptr t2 = dynamic_pointer_cast<compare_task>(task2);
off_t s1 = t1->args->elf1.size + t1->args->elf2.size; off_t s1 = t1->args ? t1->args->elf1.size + t1->args->elf2.size : 0;
off_t s2 = t2->args->elf1.size + t2->args->elf2.size; off_t s2 = t2->args ? t2->args->elf1.size + t2->args->elf2.size: 0;
return s1 > s2; return s1 > s2;
} }
@ -2483,6 +2777,112 @@ compare_prepared_userspace_packages(package& first_package,
return status; return status;
} }
/// In the context of the unpacked content of a given package, compare
/// the binaries inside the package against their ABIXML
/// representation. This should yield the empty set.
///
/// @param pkg (unpacked) package
///
/// @param diff the representation of the changes between the binaries
/// and their ABIXML. This should obviously be the empty set.
///
/// @param diff a textual representation of the diff.
///
/// @param opts the options provided by the user.
static abidiff_status
self_compare_prepared_userspace_package(package& pkg,
abi_diff& diff,
options& opts)
{
abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
abigail::workers::queue::tasks_type self_compare_tasks;
string pkg_name = pkg.base_name();
// Setting debug-info path of libraries
string debug_dir, relative_debug_path = "/usr/lib/debug/";
if (!pkg.debug_info_packages().empty())
debug_dir =
pkg.debug_info_packages().front()->extracted_dir_path() +
relative_debug_path;
suppressions_type supprs;
for (map<string, elf_file_sptr>::iterator it =
pkg.path_elf_file_sptr_map().begin();
it != pkg.path_elf_file_sptr_map().end();
++it)
{
if (it != pkg.path_elf_file_sptr_map().end()
&& (it->second->type == abigail::dwarf_reader::ELF_TYPE_DSO
|| it->second->type == abigail::dwarf_reader::ELF_TYPE_EXEC
|| it->second->type == abigail::dwarf_reader::ELF_TYPE_PI_EXEC
|| it->second->type == abigail::dwarf_reader::ELF_TYPE_RELOCATABLE))
{
if (it->second->type != abigail::dwarf_reader::ELF_TYPE_RELOCATABLE)
{
compare_args_sptr args
(new compare_args(*it->second,
debug_dir,
supprs,
*it->second,
debug_dir,
supprs,
opts));
self_compare_task_sptr t(new self_compare_task(args));
self_compare_tasks.push_back(t);
}
}
}
if (self_compare_tasks.empty())
{
maybe_erase_temp_dirs(pkg, pkg, opts);
return abigail::tools_utils::ABIDIFF_OK;
}
// Larger elfs are processed first, since it's usually safe to assume
// their debug-info is larger as well, but the results are still
// in a map ordered by looked up in elf.name order.
std::sort(self_compare_tasks.begin(),
self_compare_tasks.end(),
elf_size_is_greater);
// There's no reason to spawn more workers than there are ELF pairs
// to be compared.
size_t num_workers = (opts.parallel
? std::min(opts.num_workers, self_compare_tasks.size())
: 1);
assert(num_workers >= 1);
comparison_done_notify notifier(diff);
abigail::workers::queue comparison_queue(num_workers, notifier);
// Compare all the binaries, in parallel and then wait for the
// comparisons to complete.
comparison_queue.schedule_tasks(self_compare_tasks);
comparison_queue.wait_for_workers_to_complete();
// Get the set of comparison tasks that were perform and sort them.
queue::tasks_type& done_tasks = comparison_queue.get_completed_tasks();
std::sort(done_tasks.begin(), done_tasks.end(), elf_size_is_greater);
// Print the reports of the comparison to standard output.
for (queue::tasks_type::const_iterator i = done_tasks.begin();
i != done_tasks.end();
++i)
{
self_compare_task_sptr t = dynamic_pointer_cast<self_compare_task>(*i);
if (t)
cout << t->pretty_output;
}
// Erase temporary directory tree we might have left behind.
maybe_erase_temp_dirs(pkg, pkg, opts);
status = notifier.status;
return status;
}
/// Compare the ABI of two prepared packages that contain linux kernel /// Compare the ABI of two prepared packages that contain linux kernel
/// binaries. /// binaries.
/// ///
@ -2626,6 +3026,29 @@ compare_prepared_package(package& first_package, package& second_package,
return status; return status;
} }
/// Compare binaries in a package against their ABIXML
/// representations.
///
/// @param pkg the package to consider.
///
/// @param diff the textual representation of the resulting
/// comparison.
///
/// @param opts the options provided by the user
///
/// @return the status of the comparison.
static abidiff_status
self_compare_prepared_package(package& pkg,
abi_diff& diff,
options& opts)
{
abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
status = self_compare_prepared_userspace_package(pkg, diff, opts);
return status;
}
/// Compare the ABI of two packages /// Compare the ABI of two packages
/// ///
/// @param first_package the first package to consider. /// @param first_package the first package to consider.
@ -2658,6 +3081,24 @@ compare(package_sptr& first_package, package_sptr& second_package,
return compare_prepared_package(*first_package, *second_package, diff, opts); return compare_prepared_package(*first_package, *second_package, diff, opts);
} }
/// Compare binaries in a package against their ABIXML
/// representations.
///
/// @param pkg the package to consider.
///
/// @param opts the options provided by the user
///
/// @return the status of the comparison.
static abidiff_status
compare_to_self(package_sptr& pkg, options& opts)
{
if (!prepare_package(pkg, opts))
return abigail::tools_utils::ABIDIFF_ERROR;
abi_diff diff;
return self_compare_prepared_package(*pkg, diff, opts);
}
/// Compare the ABI of two packages. /// Compare the ABI of two packages.
/// ///
/// @param first_package the first package to consider. /// @param first_package the first package to consider.
@ -2830,6 +3271,8 @@ parse_command_line(int argc, char* argv[], options& opts)
opts.parallel = false; opts.parallel = false;
else if (!strcmp(argv[i], "--show-identical-binaries")) else if (!strcmp(argv[i], "--show-identical-binaries"))
opts.show_identical_binaries = true; opts.show_identical_binaries = true;
else if (!strcmp(argv[i], "--self-check"))
opts.self_check = true;
else if (!strcmp(argv[i], "--suppressions") else if (!strcmp(argv[i], "--suppressions")
|| !strcmp(argv[i], "--suppr")) || !strcmp(argv[i], "--suppr"))
{ {
@ -2976,10 +3419,31 @@ main(int argc, char* argv[])
return (abigail::tools_utils::ABIDIFF_USAGE_ERROR return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
| abigail::tools_utils::ABIDIFF_ERROR); | abigail::tools_utils::ABIDIFF_ERROR);
if (opts.package1.empty() || opts.package2.empty()) bool need_just_one_input_package = opts.self_check;
if (need_just_one_input_package)
{
bool bail_out = false;
if (!opts.package2.empty())
{
// We don't need the second package, we'll ignore it later
// down below.
;
}
if (opts.package1.empty())
{
// We need at least one package to work with!
emit_prefix("abipkgdiff", cerr)
<< "missing input package\n";
if (bail_out)
return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
| abigail::tools_utils::ABIDIFF_ERROR);
}
}
else if(opts.package1.empty() || opts.package2.empty())
{ {
emit_prefix("abipkgdiff", cerr) emit_prefix("abipkgdiff", cerr)
<< "Please enter two packages to compare" << "\n"; << "please enter two packages to compare" << "\n";
return (abigail::tools_utils::ABIDIFF_USAGE_ERROR return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
| abigail::tools_utils::ABIDIFF_ERROR); | abigail::tools_utils::ABIDIFF_ERROR);
} }
@ -3036,7 +3500,8 @@ main(int argc, char* argv[])
switch (first_package->type()) switch (first_package->type())
{ {
case abigail::tools_utils::FILE_TYPE_RPM: case abigail::tools_utils::FILE_TYPE_RPM:
if (second_package->type() != abigail::tools_utils::FILE_TYPE_RPM) if (!second_package->path().empty()
&& second_package->type() != abigail::tools_utils::FILE_TYPE_RPM)
{ {
base_name(opts.package2, package_name); base_name(opts.package2, package_name);
emit_prefix("abipkgdiff", cerr) emit_prefix("abipkgdiff", cerr)
@ -3063,7 +3528,8 @@ main(int argc, char* argv[])
} }
if (first_package->debug_info_packages().empty() if (first_package->debug_info_packages().empty()
|| second_package->debug_info_packages().empty()) || (!second_package->path().empty()
&& second_package->debug_info_packages().empty()))
{ {
emit_prefix("abipkgdiff", cerr) emit_prefix("abipkgdiff", cerr)
<< "a Linux Kernel package must be accompanied with its " << "a Linux Kernel package must be accompanied with its "
@ -3086,7 +3552,8 @@ main(int argc, char* argv[])
break; break;
case abigail::tools_utils::FILE_TYPE_DEB: case abigail::tools_utils::FILE_TYPE_DEB:
if (second_package->type() != abigail::tools_utils::FILE_TYPE_DEB) if (!second_package->path().empty()
&& second_package->type() != abigail::tools_utils::FILE_TYPE_DEB)
{ {
base_name(opts.package2, package_name); base_name(opts.package2, package_name);
emit_prefix("abipkgdiff", cerr) emit_prefix("abipkgdiff", cerr)
@ -3097,7 +3564,8 @@ main(int argc, char* argv[])
break; break;
case abigail::tools_utils::FILE_TYPE_DIR: case abigail::tools_utils::FILE_TYPE_DIR:
if (second_package->type() != abigail::tools_utils::FILE_TYPE_DIR) if (!second_package->path().empty()
&& second_package->type() != abigail::tools_utils::FILE_TYPE_DIR)
{ {
base_name(opts.package2, package_name); base_name(opts.package2, package_name);
emit_prefix("abipkgdiff", cerr) emit_prefix("abipkgdiff", cerr)
@ -3108,7 +3576,8 @@ main(int argc, char* argv[])
break; break;
case abigail::tools_utils::FILE_TYPE_TAR: case abigail::tools_utils::FILE_TYPE_TAR:
if (second_package->type() != abigail::tools_utils::FILE_TYPE_TAR) if (!second_package->path().empty()
&& second_package->type() != abigail::tools_utils::FILE_TYPE_TAR)
{ {
base_name(opts.package2, package_name); base_name(opts.package2, package_name);
emit_prefix("abipkgdiff", cerr) emit_prefix("abipkgdiff", cerr)
@ -3126,5 +3595,8 @@ main(int argc, char* argv[])
| abigail::tools_utils::ABIDIFF_ERROR); | abigail::tools_utils::ABIDIFF_ERROR);
} }
if (opts.self_check)
return compare_to_self(first_package, opts);
return compare(first_package, second_package, opts); return compare(first_package, second_package, opts);
} }