Initial support for diffing ABI corpus files

* include/abg-comparison.h (string_function_ptr_map)
	(changed_function_ptr, string_changed_function_ptr_map)
	(corpus_diff_sptr): New convenience typedefs.
	(translation_unit_diff): Add comments.
	(class corpus_diff): New type.
	(compute_diff): New overload for corpus_diff.
	* include/abg-corpus.h (corpus::{functions, variables}): New
	typedefs.
	(corpus::{operator==, get_functions, get_variables}): New members.
	* include/abg-diff-utils.h (struct deep_ptr_eq_functor): New
	functor.
	* include/abg-ir.h (translation_unit::operator==): New member
	equality operator.
	* src/abg-comparison.cc (struct corpus_diff::priv): New private
	struct holding the private members of corpus_diff.
	(corpus_diff::priv::{lookup_tables_empty, clear_lookup_tables,
	ensure_lookup_tables_populated}): Define new private member functions.
	(corpus_diff::{corpus_diff, first_corpus, second_corpus,
	function_changes, variable_changes, length, report}): New public members.
	(struct noop_deleter): New struct.
	(compute_diff): New implementation for corpus_diff.
	* src/abg-corpus.cc (struct corpus::priv): Renamed corpus::impl
	into this.  Add new fns, vars and is_symbol_table_built data
	members.
	(corpus::priv::build_symbol_table): New member function.
	(class symtab_build_visitor_type): New visitor type to build the
	symbol table.
	(struct func_comp, struct var_comp): New comparison functors.
	(corpus::priv::build_symbol_table): Define new member function.
	(corpus::{corpus, add, get_translation_units, operator==,
	get_functions, get_variables}): Define new members.
	* src/abg-ir.cc (translation_unit::operator==): Define new member
	equality operator.
	(operator==(translation_unit_sptr l, translation_unit_sptr r)):
	Define new equality operator.
	* tools/abg-tools-utils.h (enum file_type): New enum.
	(guess_file_type): Declare new function.
	* tools/abg-tools-utils.cc (guess_file_type): define new function.
	* tools/bidiff.cc (main): Guess the type of the files given in
	input and support elf files reading and diffing.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
This commit is contained in:
Dodji Seketeli 2013-12-23 14:05:19 +01:00
parent dbc0225415
commit fbb6b1bc73
10 changed files with 830 additions and 67 deletions

View File

@ -23,7 +23,7 @@
/// @file
#include <tr1/unordered_map>
#include "abg-ir.h"
#include "abg-corpus.h"
#include "abg-diff-utils.h"
namespace abigail
@ -91,6 +91,21 @@ typedef std::pair<enum_type_decl::enumerator,
/// The key is the name of the changed enumerator.
typedef unordered_map<string, changed_enumerator> string_changed_enumerator_map;
/// Convenience typedef for a map which key is a string and which
/// value is a pointer to @ref decl_base.
typedef unordered_map<string, function_decl*> string_function_ptr_map;
/// Convenience typedef for a pair of pointer to @ref function_decl
/// representing a change a @ref function_decl change. The first
/// member of the pair represent the initial function and the second
/// member represents the the changed function.
typedef std::pair<function_decl*, function_decl*> changed_function_ptr;
/// Convenience typedef for a map which key is a string and which
/// value is a @ref changed_function_ptr.
typedef unordered_map<string,
changed_function_ptr> string_changed_function_ptr_map;
/// This type encapsulates an edit script (a set of insertions and
/// deletions) for two constructs that are to be diff'ed. The two
/// constructs are called the "subjects" of the diff.
@ -690,6 +705,7 @@ class translation_unit_diff;
/// @ref translation_unit_diff type.
typedef shared_ptr<translation_unit_diff> translation_unit_diff_sptr;
/// An abstraction of a diff between two translation units.
class translation_unit_diff : public scope_diff
{
protected:
@ -706,12 +722,54 @@ public:
virtual void
report(ostream& out, const string& indent = "") const;
};//end clss translation_unit_diff
};//end class translation_unit_diff
translation_unit_diff_sptr
compute_diff(const translation_unit_sptr first,
const translation_unit_sptr second);
class corpus_diff;
/// A convenience typedef for a shared pointer to @ref corpus_diff.
typedef shared_ptr<corpus_diff> corpus_diff_sptr;
/// An abstraction of a diff between between two abi corpus.
class corpus_diff
{
struct priv;
typedef shared_ptr<priv> priv_sptr;
priv_sptr priv_;
protected:
corpus_diff(corpus_sptr first,
corpus_sptr second);
public:
corpus_sptr
first_corpus() const;
corpus_sptr
second_corpus() const;
edit_script&
function_changes() const;
edit_script&
variable_changes() const;
unsigned
length() const;
void
report(ostream& out, const string& indent = "") const;
friend corpus_diff_sptr
compute_diff(const corpus_sptr, const corpus_sptr);
}; // end class corpus_diff
corpus_diff_sptr
compute_diff(const corpus_sptr, const corpus_sptr);
}// end namespace comparison
}// end namespace abigail

View File

@ -40,11 +40,14 @@ typedef shared_ptr<corpus> corpus_sptr;
class corpus
{
public:
struct impl;
typedef std::string string;
struct priv;
typedef shared_ptr<priv> priv_sptr;
typedef std::string string;
typedef vector<function_decl*> functions;
typedef vector<var_decl*> variables;
private:
shared_ptr<impl> m_priv;
shared_ptr<priv> priv_;
corpus();
@ -69,6 +72,15 @@ public:
bool
is_empty() const;
bool
operator==(const corpus&) const;
const functions&
get_functions() const;
const variables&
get_variables() const;
};// end class corpus.
}//end namespace abigail

View File

@ -733,6 +733,34 @@ struct default_eq_functor
{return a == b;}
};
/// An equality functor to deeply compare pointers.
struct deep_ptr_eq_functor
{
/// This equality operator compares pointers by comparing the
/// pointed-to objects.
///
/// @param first the first comparison argument.
///
/// @param second the second comparison argument.
///
/// @return true if the objects pointed to by the pointers are
/// equal, false otherwise.
template<typename T>
bool
operator()(const T* first,
const T* second)
{
if (!!first != !!second)
return false;
if (!first)
return true;
return *first == *second;
}
};
/// Find the end of the furthest reaching d-path on diagonal k, for
/// two sequences. In the paper This is referred to as "the basic
/// algorithm".

View File

@ -171,10 +171,16 @@ public:
void
set_address_size(char);
bool
operator==(const translation_unit&) const;
virtual void
traverse(ir_node_visitor& v);
};//end class translation_unit
bool
operator==(translation_unit_sptr, translation_unit_sptr);
/// The base type of all declarations.
class decl_base : public ir_traversable_base
{

View File

@ -3296,5 +3296,262 @@ compute_diff(const translation_unit_sptr first,
}
// </translation_unit_diff stuff>
// <corpus stuff>
struct corpus_diff::priv
{
corpus_sptr first_;
corpus_sptr second_;
edit_script fns_edit_script_;
edit_script vars_edit_script_;
string_function_ptr_map deleted_fns_;
string_function_ptr_map added_fns_;
string_changed_function_ptr_map changed_fns_;
bool
lookup_tables_empty() const;
void
clear_lookup_tables();
void
ensure_lookup_tables_populated();
}; // end corpus::priv
/// Tests if the lookup tables are empty.
///
/// @return true if the lookup tables are empty, false otherwise.
bool
corpus_diff::priv::lookup_tables_empty() const
{
return (deleted_fns_.empty()
&& added_fns_.empty()
&& changed_fns_.empty());
}
/// Clear the lookup tables useful for reporting an enum_diff.
void
corpus_diff::priv::clear_lookup_tables()
{
deleted_fns_.clear();
added_fns_.clear();
changed_fns_.clear();
}
/// If the lookup tables are not yet built, walk the differences and
/// fill the lookup tables.
void
corpus_diff::priv::ensure_lookup_tables_populated()
{
if (!lookup_tables_empty())
return;
{
edit_script& e = fns_edit_script_;
for (vector<deletion>::const_iterator it = e.deletions().begin();
it != e.deletions().end();
++it)
{
unsigned i = it->index();
assert(i < first_->get_functions().size());
function_decl* deleted_fn = first_->get_functions()[i];
string n = deleted_fn->get_mangled_name();
if (n.empty())
n = deleted_fn->get_name();
assert(!n.empty());
assert(deleted_fns_.find(n) == deleted_fns_.end());
deleted_fns_[n] = deleted_fn;
}
for (vector<insertion>::const_iterator it = e.insertions().begin();
it != e.insertions().end();
++it)
{
for (vector<unsigned>::const_iterator iit =
it->inserted_indexes().begin();
iit != it->inserted_indexes().end();
++iit)
{
unsigned i = *iit;
function_decl* added_fn = second_->get_functions()[i];
string n = added_fn->get_mangled_name();
if (n.empty())
n = added_fn->get_name();
assert(!n.empty());
assert(added_fns_.find(n) == added_fns_.end());
added_fns_[n] = added_fn;
}
}
for (string_function_ptr_map::const_iterator it = deleted_fns_.begin();
it != deleted_fns_.end();
++it)
{
string_function_ptr_map::const_iterator it2 =
added_fns_.find(it->first);
if (it2 != added_fns_.end())
changed_fns_[it->first] = std::make_pair(it->second,
it2->second);
}
}
}
/// Constructor for @ref corpus_diff.
///
/// @param first the first corpus of the diff.
///
/// @param second the second corpus of the diff.
corpus_diff::corpus_diff(corpus_sptr first,
corpus_sptr second)
: priv_(new priv)
{
priv_->first_ = first;
priv_->second_ = second;
}
/// @return the first corpus of the diff.
corpus_sptr
corpus_diff::first_corpus() const
{return priv_->first_;}
/// @return the second corpus of the diff.
corpus_sptr
corpus_diff::second_corpus() const
{return priv_->second_;}
/// @return the bare edit script of the functions changed as recorded
/// by the diff.
edit_script&
corpus_diff::function_changes() const
{return priv_->fns_edit_script_;}
/// @return the bare edit script of the variables changed as recorded
/// by the diff.
edit_script&
corpus_diff::variable_changes() const
{return priv_->vars_edit_script_;}
/// @return the length of the changes as recorded by the diff.
unsigned
corpus_diff::length() const
{
return (priv_->deleted_fns_.size()
+ priv_->added_fns_.size()
- priv_->changed_fns_.size());
}
/// A deleter for shared pointers that ... doesn't delete the object
/// managed by the shared pointer.
struct noop_deleter
{
template<typename T>
void
operator()(T*)
{}
};
/// Report the diff in a serialieed form.
///
/// @param out the stream to serialize the diff to.
///
/// @param the prefix to use for the indentation of this
/// serialization.
void
corpus_diff::report(ostream& out, const string& indent) const
{
unsigned removed = 0, added = 0;
for (string_function_ptr_map::const_iterator i =
priv_->deleted_fns_.begin();
i != priv_->deleted_fns_.end();
++i)
{
if (priv_->added_fns_.find(i->first) == priv_->added_fns_.end())
{
out << indent
<< " '"
<< i->second->get_pretty_representation()
<< "' was removed\n";
++removed;
}
}
if (removed)
out << "\n";
for (string_function_ptr_map::const_iterator i =
priv_->added_fns_.begin();
i != priv_->added_fns_.end();
++i)
{
if (priv_->deleted_fns_.find(i->first) == priv_->deleted_fns_.end())
{
out << indent
<< " '"
<< i->second->get_pretty_representation()
<< "' was added\n";
++added;
}
}
if (added)
out << "\n";
for (string_changed_function_ptr_map::const_iterator i =
priv_->changed_fns_.begin();
i != priv_->changed_fns_.end();
++i)
{
out << indent << " '"
<< i->second.first->get_pretty_representation()
<< "' was changed to '"
<< i->second.second->get_pretty_representation()
<< "':\n";
{
function_decl_sptr f(i->second.first, noop_deleter());
function_decl_sptr s(i->second.second, noop_deleter());
diff_sptr diff = compute_diff_for_decls(f, s);
if (diff)
diff->report(out, indent + " ");
}
}
if (priv_->changed_fns_.size())
out << "\n";
}
/// Compute the diff between two instances fo the @ref corpus
///
/// @param f the first @ref corpus to consider for the diff.
///
/// @param s the second @ref corpus to consider for the diff.
///
/// @return the resulting diff between the two @ref corpus.
corpus_diff_sptr
compute_diff(const corpus_sptr f, const corpus_sptr s)
{
typedef corpus::functions::const_iterator fns_it_type;
typedef corpus::variables::const_iterator vars_it_type;
typedef diff_utils::deep_ptr_eq_functor eq_type;
corpus_diff_sptr r(new corpus_diff(f, s));
diff_utils::compute_diff<fns_it_type, eq_type>(f->get_functions().begin(),
f->get_functions().end(),
s->get_functions().begin(),
s->get_functions().end(),
r->priv_->fns_edit_script_);
diff_utils::compute_diff<vars_it_type, eq_type>(f->get_variables().begin(),
f->get_variables().end(),
s->get_variables().begin(),
s->get_variables().end(),
r->priv_->vars_edit_script_);
r->priv_->ensure_lookup_tables_populated();
return r;
}
// </corpus stuff>
}// end namespace comparison
} // end namespace abigail

View File

@ -22,9 +22,11 @@
#include <cstdio>
#include <cstring>
#include <cassert>
#include <ext/stdio_filebuf.h>
#include <sstream>
#include <stdexcept>
#include <algorithm>
#include "abg-ir.h"
#include "abg-corpus.h"
#include "abg-reader.h"
@ -52,46 +54,238 @@ struct array_deleter
}
};//end array_deleter
struct corpus::impl
struct corpus::priv
{
string path;
translation_units members;
vector<function_decl*> fns;
vector<var_decl*> vars;
bool is_symbol_table_built;
private:
impl();
priv();
public:
impl(const string &p)
: path(p)
{}
};
priv(const string &p)
: path(p),
is_symbol_table_built(false)
{}
/// @param path the path to the file containing the ABI corpus.
corpus::corpus(const string& path)
{
m_priv.reset(new impl(path));
}
void
build_symbol_table();
};
/// Add a translation unit to the current ABI Corpus. Next time
/// corpus::save is called, all the translation unit that got added
/// to the corpus are going to be serialized on disk in the file
/// associated to the current corpus.
///
/// @param tu
void
corpus::add(const translation_unit_sptr tu)
{
m_priv->members.push_back(tu);
}
/// A visitor type to be used while traversing functions and variables
/// of the translations units of the corpus. The goal of this visitor
/// is to build a symbol table containing all the public functions and
/// global variables of the all the translation units of the the
/// corpus.
class symtab_build_visitor_type : public ir_node_visitor
{
vector<function_decl*>& functions;
vector<var_decl*>& variables;
list<function_decl*> wip_fns;
int wip_fns_size;
list<var_decl*> wip_vars;
int wip_vars_size;
/// Return the list of translation units of the current corpus.
///
/// @return the list of translation units of the current corpus.
const translation_units&
corpus::get_translation_units() const
{
return m_priv->members;
}
symtab_build_visitor_type();
friend class corpus::priv;
public:
symtab_build_visitor_type(vector<function_decl*>&fns,
vector<var_decl*>&vars)
: functions(fns),
variables(vars),
wip_fns_size(0),
wip_vars_size(0)
{}
/// This function is called while visiting a @ref function_decl IR
/// node of a translation unit.
///
/// Add the function to the symbol table being constructed (WIP
/// meaning work in progress).
///
/// @param fn the function being visited.
void
visit(function_decl* fn)
{
wip_fns.push_back(fn);
++wip_fns_size;
}
/// This function is called while visiting a
/// class_decl::function_decl IR node of a translation unit.
///
/// Add the member function to the symbol table being constructed
/// (WIP meaning work in progress).
///
/// @param fn the member function being visited.
void
visit(class_decl::member_function* fn)
{
wip_fns.push_back(fn);
++wip_fns_size;
}
/// This function is called while visiting a @ref var_decl IR node
/// of a translation unit.
///
/// Add the variable to the symbol table being constructed (WIP
/// meaning work in progress).
///
/// @param var the variable being visited.
void
visit(var_decl* var)
{
wip_vars.push_back(var);
++wip_vars_size;
}
/// This function is called while visiting a @ref var_decl IR node
/// of a translation unit.
///
/// Add the variable to the symbol table being constructed (WIP
/// meaning work in progress).
///
/// @param var the variable being visited.
void
visit(class_decl::data_member* var)
{
if (var->is_static())
{
wip_vars.push_back(var);
++wip_vars_size;
}
}
};// end struct symtab_build_visitor_type
/// This is a comparison functor for comparing pointers to @ref
/// function_decl.
struct func_comp
{
/// The comparisong operator for pointers to @ref function_decl. It
/// performs a string comparison of the mangled names of the
/// functions. If the functions don't have mangled names, it
/// compares their names instead.
///
/// @param first the first function to consider in the comparison.
///
/// @param second the second function to consider in the comparison.
///
/// @return true if the (mangled) name of the first function is less
/// than the (mangled)name of the second one, false otherwise.
bool
operator()(const function_decl* first,
const function_decl* second) const
{
assert(first != 0 && second != 0);
string first_name, second_name;
first_name = first->get_mangled_name();
if (first_name.empty())
first_name = first->get_name();
assert(!first_name.empty());
second_name = second->get_mangled_name();
if (second_name.empty())
second_name = second->get_name();
assert(!second_name.empty());
return first_name < second_name;
}
};
/// This is a comparison functor for comparing pointers to @ref
/// var_decl.
struct var_comp
{
/// The comparison operator for pointers to @ref var_decl.
///
/// It perform a string comparison on the names of the variables.
///
/// @param first the first variable to consider for the comparison.
///
/// @param second the second variable to consider for the comparison.
///
/// @return true if first is less than second, false otherwise.
bool
operator()(const var_decl* first,
const var_decl* second) const
{
assert(first != 0 && second != 0);
string first_name, second_name;
first_name = first->get_qualified_name();
assert(!first_name.empty());
second_name = second->get_qualified_name();
assert(!second_name.empty());
return first_name < second_name;
}
};
/// Build the symbol tables for the corpus. That is, walk all the
/// functions of all translation units of the corpus, stuff them in a
/// vector and sort the vector. Likewise for the variables.
void
corpus::priv::build_symbol_table()
{
symtab_build_visitor_type v(fns, vars);
for (translation_units::iterator i = members.begin();
i != members.end();
++i)
(*i)->traverse(v);
fns.reserve(v.wip_fns_size);
for (list<function_decl*>::iterator i = v.wip_fns.begin();
i != v.wip_fns.end();
++i)
fns.push_back(*i);
v.wip_fns.clear();
v.wip_fns_size = 0;
vars.reserve(v.wip_vars_size);
for (list<var_decl*>::iterator i = v.wip_vars.begin();
i != v.wip_vars.end();
++i)
vars.push_back(*i);
v.wip_vars.clear();
v.wip_vars_size = 0;
func_comp fc;
std::sort(fns.begin(), fns.end(), fc);
var_comp vc;
std::sort(vars.begin(), vars.end(), vc);
is_symbol_table_built = true;
}
/// @param path the path to the file containing the ABI corpus.
corpus::corpus(const string& path)
{priv_.reset(new priv(path));}
/// Add a translation unit to the current ABI Corpus. Next time
/// corpus::save is called, all the translation unit that got added
/// to the corpus are going to be serialized on disk in the file
/// associated to the current corpus.
///
/// @param tu the new translation unit to add.
void
corpus::add(const translation_unit_sptr tu)
{priv_->members.push_back(tu);}
/// Return the list of translation units of the current corpus.
///
/// @return the list of translation units of the current corpus.
const translation_units&
corpus::get_translation_units() const
{return priv_->members;}
/// Erase the translation units contained in this in-memory object.
///
@ -99,23 +293,19 @@ public:
/// representation of this object is not modified.
void
corpus::drop_translation_units()
{
m_priv->members.clear();
}
{priv_->members.clear();}
/// Get the file path associated to the corpus file.
///
/// A subsequent call to corpus::read will deserialize the content of
/// the abi file expected at this path; likewise, a call to
/// corpus::write will serialize the translation units contained in
/// the corpus object into the on-disk file at this path.
/// Get the file path associated to the corpus file.
///
/// A subsequent call to corpus::read will deserialize the content of
/// the abi file expected at this path; likewise, a call to
/// corpus::write will serialize the translation units contained in
/// the corpus object into the on-disk file at this path.
/// @return the file path associated to the current corpus.
string&
corpus::get_path() const
{
return m_priv->path;
}
/// @return the file path associated to the current corpus.
string&
corpus::get_path() const
{return priv_->path;}
/// Set the file path associated to the corpus file.
///
@ -126,17 +316,59 @@ corpus::drop_translation_units()
/// @param the new file path to assciate to the current corpus.
void
corpus::set_path(const string& path)
{
m_priv->path = path;
}
{priv_->path = path;}
/// Tests if the corpus contains no translation unit.
///
/// @return true if the corpus contains no translation unit.
bool
corpus::is_empty() const
{
return m_priv->members.empty();
}
bool
corpus::is_empty() const
{return priv_->members.empty();}
/// Compare the current @ref corpus against another one.
///
/// @param other the other corpus to compare against.
///
/// @return true if the two corpus are equal, false otherwise.
bool
corpus::operator==(const corpus& other) const
{
translation_units::const_iterator i, j;
for (i = get_translation_units().begin(),
j = other.get_translation_units().begin();
(i != get_translation_units().end()
&& j != other.get_translation_units().end());
++i, ++j)
if ((**i) != (**j))
return false;
return (i == get_translation_units().end()
&& j == other.get_translation_units().end());
}
/// @return the vector of functions of the symbol table. The
/// functions are sorted using their mangled name or name if they
/// don't have mangle names.
const corpus::functions&
corpus::get_functions() const
{
if (!priv_->is_symbol_table_built)
priv_->build_symbol_table();
assert(priv_->is_symbol_table_built);
return priv_->fns;
}
/// @return the vector of variables of the symbol table. The
/// variables are sorted using their name.
const corpus::variables&
corpus::get_variables() const
{
if (!priv_->is_symbol_table_built)
priv_->build_symbol_table();
assert(priv_->is_symbol_table_built);
return priv_->vars;
}
}// end namespace abigail

View File

@ -298,7 +298,22 @@ void
translation_unit::set_address_size(char a)
{priv_->address_size_= a;}
/// This implements the traversable_base::traverse pure virtual
/// Compare the current translation unit against another one.
///
/// @param other the other tu to compare against.
///
/// @return true if the two translation units are equal, false
/// otherwise.
bool
translation_unit::operator==(const translation_unit& other)const
{
if (get_address_size() != other.get_address_size())
return false;
return *get_global_scope() == *other.get_global_scope();
}
/// This implements the ir_traversable_base::traverse virtual
/// function.
///
/// @param v the visitor used on the member nodes of the translation
@ -310,6 +325,25 @@ translation_unit::traverse(ir_node_visitor& v)
translation_unit::~translation_unit()
{}
/// A deep comparison operator for pointers to translation units.
///
/// @param l the first translation unit to consider for the comparison.
///
/// @param r the second translation unit to consider for the comparison.
///
/// @return true if the two translation units are equal, false otherwise.
bool
operator==(translation_unit_sptr l, translation_unit_sptr r)
{
if (l.get() == r.get())
return true;
if (!!l != !!r)
return false;
return *l == *r;
}
// </translation_unit stuff>
// <Decl definition>

View File

@ -25,6 +25,7 @@
#include <cstdlib>
#include <cstring>
#include <libgen.h>
#include <fstream>
#include "abg-tools-utils.h"
using std::string;
@ -35,6 +36,8 @@ namespace tools
{
using std::ostream;
using std::istream;
using std::ifstream;
using std::string;
#define DECLARE_STAT(st) \
@ -213,5 +216,62 @@ check_file(const string& path,
return true;
}
/// Guess the type of the content of an input stream.
///
/// @param in the input stream to guess the content type for.
///
/// @return the type of content guessed.
file_type
guess_file_type(istream& in)
{
const unsigned BUF_LEN = 12;
const unsigned NB_BYTES_TO_READ = 11;
char buf[BUF_LEN];
memset(buf, 0, BUF_LEN);
std::streampos initial_pos = in.tellg();
in.read(buf, NB_BYTES_TO_READ);
in.seekg(initial_pos);
if (in.gcount() < 4 || in.bad())
return FILE_TYPE_UNKNOWN;
if (buf[0] == 0x7f
&& buf[1] == 'E'
&& buf[2] == 'L'
&& buf[3] == 'F')
return FILE_TYPE_ELF;
if (buf[0] == '<'
&& buf[1] == 'a'
&& buf[2] == 'b'
&& buf[3] == 'i'
&& buf[4] == '-'
&& buf[5] == 'i'
&& buf[6] == 'n'
&& buf[7] == 's'
&& buf[8] == 't'
&& buf[9] == 'r'
&& buf[10] == ' ')
return FILE_TYPE_NATIVE_BI;
return FILE_TYPE_UNKNOWN;
}
/// Guess the type of the content of an file.
///
/// @param file_path the path to the file to consider.
///
/// @return the type of content guessed.
file_type
guess_file_type(const std::string& file_path)
{
ifstream in(file_path.c_str(), ifstream::binary);
file_type r = guess_file_type(in);
in.close();
return r;
}
}//end namespace tools
}//end namespace abigail

View File

@ -22,6 +22,7 @@
#include <string>
#include <ostream>
#include <istream>
namespace abigail
{
@ -40,5 +41,20 @@ bool ensure_dir_path_created(const std::string&);
bool ensure_parent_dir_created(const std::string&);
bool check_file(const std::string& path, std::ostream& out);
/// The different types of files understood the bi* suite of tools.
enum file_type
{
/// A file type we don't know about.
FILE_TYPE_UNKNOWN,
/// The native xml file format representing a translation unit.
FILE_TYPE_NATIVE_BI,
/// An elf file. Read this kind of file should yield an
/// abigail::corpus type.
FILE_TYPE_ELF,
};
file_type guess_file_type(std::istream& in);
file_type guess_file_type(const std::string& file_path);
}// end namespace tools
}//end namespace abigail

View File

@ -28,6 +28,7 @@
#include "abg-comparison.h"
#include "abg-tools-utils.h"
#include "abg-reader.h"
#include "abg-dwarf-reader.h"
using std::string;
using std::ostream;
@ -35,9 +36,12 @@ using std::cout;
using std::cerr;
using abigail::translation_unit;
using abigail::translation_unit_sptr;
using abigail::corpus_sptr;
using abigail::comparison::translation_unit_diff_sptr;
using abigail::comparison::corpus_diff_sptr;
using abigail::comparison::compute_diff;
using abigail::tools::check_file;
using abigail::tools::guess_file_type;
struct options
{
@ -108,26 +112,82 @@ main(int argc, char* argv[])
if (!check_file(opts.file2, cerr))
return true;
translation_unit_sptr t1 =
abigail::xml_reader::read_translation_unit_from_file(opts.file1);
abigail::tools::file_type t1_type, t2_type;
translation_unit_sptr t2 =
abigail::xml_reader::read_translation_unit_from_file(opts.file2);
t1_type = guess_file_type(opts.file1);
if (t1_type == abigail::tools::FILE_TYPE_UNKNOWN)
{
cerr << "Unknown content type for file " << opts.file1 << "\n";
return true;
}
if (!t1)
t2_type = guess_file_type(opts.file2);
if (t2_type == abigail::tools::FILE_TYPE_UNKNOWN)
{
cerr << "Unknown content type for file " << opts.file2 << "\n";
return true;
}
translation_unit_sptr t1, t2;
corpus_sptr c1, c2;
switch (t1_type)
{
case abigail::tools::FILE_TYPE_UNKNOWN:
cerr << "Unknown content type for file " << opts.file1 << "\n";
return true;
break;
case abigail::tools::FILE_TYPE_NATIVE_BI:
t1 = abigail::xml_reader::read_translation_unit_from_file(opts.file1);
break;
case abigail::tools::FILE_TYPE_ELF:
c1 = abigail::dwarf_reader::read_corpus_from_elf(opts.file1);
break;
}
switch (t2_type)
{
case abigail::tools::FILE_TYPE_UNKNOWN:
cerr << "Unknown content type for file " << opts.file2 << "\n";
return true;
break;
case abigail::tools::FILE_TYPE_NATIVE_BI:
t2 = abigail::xml_reader::read_translation_unit_from_file(opts.file2);
break;
case abigail::tools::FILE_TYPE_ELF:
c2 = abigail::dwarf_reader::read_corpus_from_elf(opts.file2);
break;
}
if (!t1 && !c1)
{
cerr << "failed to read input file " << opts.file1 << "\n";
return true;
}
if (!t2)
if (!t2 && !c2)
{
cerr << "failed to read input file" << opts.file2 << "\n";
return true;
}
translation_unit_diff_sptr changes = compute_diff(t1, t2);
changes->report(cout);
if (!!c1 != !!c2
|| !!t1 != !!t2)
{
cerr << "the two input should be of the same kind\n";
return true;
}
if (t1)
{
translation_unit_diff_sptr changes = compute_diff(t1, t2);
changes->report(cout);
}
else if (c1)
{
corpus_diff_sptr changes = compute_diff(c1, c2);
changes->report(cout);
}
return false;
}