Initial writing/reading of an ABI corpus to an archive

* configure.ac: Support detection of libzip dependency. Define
        new DEPS_CFLAGS and DEPS_LIBS variables for use in
        Makefile.am to refer to the dependency headers and
        libraries.
	* doc/website/mainpage.txt: Update this to talk about the new
	libzip dependency.
	* include/Makefile.am: Add abg-libzip-utils.h to the build system.
	* include/abg-corpus.h (corps): Hide abigail::corpus's private behind a
	pimpl idiom.
	(corpus::{drop_translation_units, get_file_path, set_file_path,
	write, read}): New methods.
	* include/abg-libxml-utils.h (new_reader_from_buffer): Declare new
	function.
	* include/abg-libzip-utils.h: New file.
	* src/Makefile.am: Add abg-corpus.cc and abg-libzip-utils.cc to
	the build system.  Refer to the library and headers dependencies
	via the new DEPS_LIBS and DEPS_CFLAGS variables.
	* src/abg-corpus.cc: New file.
	* src/abg-ir.cc (translation::set_path): New method.
	* src/abg-libxml-utils.cc (new_reader_from_buffer): Define new
	function.
	* src/abg-libzip-utils.cc: New file.
	* src/abg-reader.cc (translation_unit::read): New overload.
	* src/abg-writer.cc: Inject the names from the std namespace into
	the abigail namespace, rather than into abigail::writer.
	(abigail::translation_unit::write): New overload.  This can now
	use ofstream and the other stuff from std that are injected in the
	abigail:: namespace.
	* tests/Makefile.am: Add tests/test-write-read-archive.cc to the
	build system; use that to build runtestwritereadarchive.  Also add
	the input test data from
	tests/data/test-write-read-archive/test[0-4].xml.
	* /tests/data/test-write-read-archive/test[0-4].xml: New test
	input data files.
	* tests/test-write-read-archive.cc: New test for this archive
	write/read support.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
This commit is contained in:
Dodji Seketeli 2013-08-27 15:18:59 +02:00
parent 8884b58638
commit c57e950ec7
20 changed files with 810 additions and 16 deletions

View File

@ -48,6 +48,20 @@ AC_SUBST(LIBXML2_VERSION)
AC_SUBST(XML_LIBS)
AC_SUBST(XML_CFLAGS)
dnl Check for dependency: libzip
LIBZIP_VERSION=0.10
PKG_CHECK_MODULES(LIBZIP, libzip >= $LIBZIP_VERSION)
AC_SUBST(LIBZIP_VERSION)
AC_SUBST(LIBZIP_LIBS)
AC_SUBST(LIBZIP_CFLAGS)
DEPS_CPPFLAGS="$XML_CFLAGS $LIBZIP_CFLAGS"
AC_SUBST(DEPS_CPPFLAGS)
DEPS_LIBS="$XML_LIBS $LIBZIP_LIBS"
AC_SUBST(DEPS_LIBS)
if test x$ABIGAIL_DEVEL != x; then
DEVEL_CFLAGS="-g -Wall -Wextra -Werror"
DEVEL_CXXFLAGS="-g -Wall -Wextra -Werror"

View File

@ -11,7 +11,7 @@ constructions like types, variables, functions and declarations of a
given library or program. For a given program or library, this set of
constructions is called an ABI corpus.
Thus This project aims at providing a library to manipulate ABI
Thus the project aims at providing a library to manipulate ABI
corpuses, compare them, provide detailed information about their
differences and help build tools to infer interesting conclusions
about these differences.
@ -47,7 +47,7 @@ the moment the dependencies are the following Free Software packages:
\li <a href="http://www.xmlsoft.org">libxml2</a>
\li <a href="http://www.gnu.org/software/autoconf/">autoconf</a>
\li <a href="http://www.nih.at/libzip">libzip</a>
Then go to your local libabigail.git directory where the source code
you've checked out lies and create a 'build' directory that will

View File

@ -2,6 +2,7 @@ headers = \
abg-ir.h \
abg-corpus.h \
abg-libxml-utils.h \
abg-libzip-utils.h \
abg-fwd.h \
abg-hash.h \
abg-config.h \

View File

@ -37,13 +37,13 @@ namespace abigail
class corpus
{
public:
struct impl;
typedef std::string string;
typedef shared_ptr<translation_unit> translation_unit_sptr;
typedef std::vector<translation_unit_sptr> translation_units;
private:
string m_name;
translation_units m_members;
shared_ptr<impl> m_priv;
corpus();
@ -57,8 +57,24 @@ public:
const translation_units&
get_translation_units() const;
void
drop_translation_units();
string&
get_file_path() const;
void
set_file_path(const string&);
bool
is_empty() const;
bool
write() const;
int
read();
};
}//end namespace abigail
#endif //__ABG_CORPUS_H__

View File

@ -54,6 +54,7 @@ struct charDeleter
typedef shared_ptr<xmlChar> xml_char_sptr;
reader_sptr new_reader_from_file(const std::string& path);
reader_sptr new_reader_from_buffer(const std::string& buffer);
xml_char_sptr build_xml_char_sptr(xmlChar*);
template<class T>

View File

@ -0,0 +1,72 @@
// -*- mode: C++ -*-
//
// Copyright (C) 2013 Red Hat, Inc.
//
// This file is part of the GNU Application Binary Interface Generic
// Analysis and Instrumentation Library (libabigail). This library is
// free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 3, or (at your option) any
// later version.
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this program; see the file COPYING-LGPLV3. If
// not, see <http://www.gnu.org/licenses/>.
/// @file
#include <tr1/memory>
#include <zip.h>
namespace abigail
{
namespace zip_utils
{
using std::tr1::shared_ptr;
using std::string;
/// @brief Functor passed to shared_ptr constructor during
/// instantiation with zip*
///
/// Its aim is to delete zip* managed by shared_ptr.
struct archive_deleter
{
void
operator()(zip* archive)
{
/// ??? Maybe check the return code of close and throw if the
/// close fails? But then callers must be prepared to handle
/// this.
zip_close(archive);
}
};//end archive_deleter
/// @brief Functor passed to shared_ptr<zip_file>'s constructor.
///
/// Its aim is to close (actually delete) the zip_file* managed by the
/// shared_ptr.
struct zip_file_deleter
{
void
operator()(zip_file*f)
{
zip_fclose(f);
}
};
typedef shared_ptr<zip> zip_sptr;
zip_sptr open_archive(const string& path, int flags, int *errorp);
typedef shared_ptr<zip_file> zip_file_sptr;
zip_file_sptr open_file_in_archive(zip_sptr archive,
int file_index);
}// end namespace zip
}// end namespace abigail

View File

@ -4,8 +4,10 @@ libabigaildir=$(libdir)/
libabigail_la_SOURCES = $(headers) \
$(h)/abg-ir.cc \
$(h)/abg-corpus.cc \
$(h)/abg-reader.cc \
$(h)/abg-libxml-utils.cc \
$(h)/abg-libzip-utils.cc \
$(h)/abg-hash.cc \
$(h)/abg-writer.cc \
$(h)/abg-config.cc \
@ -13,7 +15,7 @@ $(h)/abg-viz-common.cc \
$(h)/abg-viz-dot.cc \
$(h)/abg-viz-svg.cc
libabigail_la_LDFLAGS=@XML_LIBS@ -Wl,--as-needed
libabigail_la_LDFLAGS=@DEPS_LIBS@ -Wl,--as-needed
AM_CPPFLAGS=@XML_CFLAGS@ -Wall -I$(top_srcdir)/include -I$(top_builddir)/include
AM_CPPFLAGS=@DEPS_CPPFLAGS@ -Wall -I$(abs_top_srcdir)/include -I$(abs_top_builddir)/include -I$(abs_top_builddir)
AM_CXXFLAGS="-std=gnu++11"

320
src/abg-corpus.cc Normal file
View File

@ -0,0 +1,320 @@
// -*- mode: C++ -*-
//
// Copyright (C) 2013 Red Hat, Inc.
//
// This file is part of the GNU Application Binary Interface Generic
// Analysis and Instrumentation Library (libabigail). This library is
// free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 3, or (at your option) any
// later version.
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this program; see the file COPYING-LGPLV3. If
// not, see <http://www.gnu.org/licenses/>.
/// @file
#include <cstdio>
#include <cstring>
#include <ext/stdio_filebuf.h>
#include <sstream>
#include <stdexcept>
#include "abg-ir.h"
#include "abg-corpus.h"
#include "abg-libzip-utils.h"
namespace abigail
{
using std::ostringstream;
using std::list;
using std::vector;
using zip_utils::zip_sptr;
using zip_utils::zip_file_sptr;
using zip_utils::open_archive;
using zip_utils::open_file_in_archive;
template<typename T>
struct array_deleter
{
void
operator()(T* a)
{
delete [] a;
}
};//end array_deleter
struct corpus::impl
{
string path;
translation_units members;
vector<string> serialized_tus;
mutable zip_sptr archive;
impl(const string &p)
: path(p)
{}
zip_sptr
get_archive() const
{
int error_code = 0;
if (!archive)
{
// Open the zip archive. If no archive at that path existed,
// create a new archive.
archive = open_archive(path, ZIP_CREATE|ZIP_CHECKCONS, &error_code);
if (error_code)
{
std::ostringstream o;
o << "zip_create returned error code '" << error_code << "'";
if (archive)
o << " and returned a non-null archive";
throw std::runtime_error(o.str());
}
}
return archive;
}
/// Closes the zip archive associated to the current corpus, if any.
///
/// Note that closing the archive writes the content (that was added
/// to it) to the disk and frees the memory associated to the
/// ressources held by the archive.
void
close_archive()
{
if (archive)
archive.reset();
}
/// @brief Write a translation unit to the current zip archive
/// associated to to the current corpus.
///
/// If the translation unit was already present (and loaded) in the
/// current archive, this function replaces the version in the
/// archive with the translation unit given in parameter, otherwise,
/// the translation unit given in parameter is just added to the
/// current archive. Note that the updated archive is not saved to
/// disk until the fonction close_archive() is invoked.
bool
write_tu_to_archive(const translation_unit& tu)
{
ostringstream os;
zip_sptr ar = get_archive();
if (!archive)
return false;
if (!tu.write(os))
return false;
serialized_tus.push_back(os.str());
zip_source *source;
if ((source = zip_source_buffer(ar.get(),
serialized_tus.back().c_str(),
serialized_tus.back().size(),
false)) == 0)
return false;
int index = zip_name_locate(ar.get(), tu.get_path().c_str(), 0);
if ( index == -1)
{
if (zip_add(ar.get(), tu.get_path().c_str(), source) < 0)
{
zip_source_free(source);
return false;
}
}
else
{
if (zip_replace(ar.get(), index, source) != 0)
{
zip_source_free(source);
return false;
}
}
return true;
}
/// Read a file that is at a particular index in the archive, into a
/// translation_unit.
///
/// @param tu the translation unit to read the content of the file
/// into.
///
/// @param file_index the index of the file to read in.
///
/// @return true upon successful completion, false otherwise.
bool
read_to_translation_unit(translation_unit& tu,
int file_index)
{
zip_sptr ar = get_archive();
if (!ar)
return false;
zip_file_sptr f = open_file_in_archive(ar, file_index);
if (!f)
return false;
string input;
{
// Allocate a 64K byte buffer to read the archive.
int buf_size = 64 * 1024;
shared_ptr<char> buf(new char[buf_size + 1], array_deleter<char>());
memset(buf.get(), 0, buf_size + 1);
input.reserve(buf_size);
while (zip_fread(f.get(), buf.get(), buf_size))
{
input.append(buf.get());
memset(buf.get(), 0, buf_size + 1);
}
}
if (!tu.read(input))
return false;
return true;
}
private:
impl();
};
/// @param path the path to the file containing the ABI corpus.
corpus::corpus(const string& path)
{
m_priv.reset(new impl(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
void
corpus::add(const translation_unit_sptr tu)
{
m_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 corpus::translation_units&
corpus::get_translation_units() const
{
return m_priv->members;
}
/// Erase the translation units contained in this in-memory object.
///
/// Note that the on-disk archive file that contains the serialized
/// representation of this object is not modified.
void
corpus::drop_translation_units()
{
m_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.
/// @return the file path associated to the current corpus.
string&
corpus::get_file_path() const
{
return m_priv->path;
}
/// Set 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.
/// @param the new file path to assciate to the current corpus.
void
corpus::set_file_path(const string& path)
{
m_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();
}
/// Serialize the current corpus to disk in a file which path is given
/// by corpus::get_file_path.
///
/// @return true upon successful completion, false otherwise.
bool
corpus::write() const
{
for (translation_units::const_iterator i = get_translation_units().begin();
i != get_translation_units().end();
++i)
{
if (! m_priv->write_tu_to_archive(**i))
return false;
}
// TODO: ensure abi-info descriptor is added to the archive.
m_priv->close_archive();
return true;
}
/// Open the archive which path is given by corpus::get_file_path and
/// de-serialize each of the translation units it contains.
///
/// @return the number of entries read and properly de-serialized,
/// zero if none was red.
int
corpus::read()
{
zip_sptr ar = m_priv->get_archive();
if (!ar)
return false;
int nb_of_tu_read = 0;
int nb_entries = zip_get_num_entries(ar.get(), 0);
if (nb_entries < 0)
return 0;
// TODO: ensure abi-info descriptor is present in the archive. Read
// it and ensure that version numbers match.
for (int i = 0; i < nb_entries; ++i)
{
shared_ptr<translation_unit>
tu(new translation_unit(zip_get_name(ar.get(), i, 0)));
if (m_priv->read_to_translation_unit(*tu, i))
{
add(tu);
++nb_of_tu_read;
}
}
return nb_of_tu_read;
}
}// end namespace abigail

View File

@ -152,6 +152,16 @@ translation_unit::get_path() const
return m_path;
}
/// Set the path associated to the current instance of
/// translation_unit.
///
/// @param a_path the new path to set.
void
translation_unit::set_path(const string& a_path)
{
m_path = a_path;
}
/// Getter of the location manager for the current translation unit.
///
/// @return a reference to the location manager for the current

View File

@ -41,6 +41,21 @@ new_reader_from_file(const std::string& path)
return p;
}
/// Instanciate an xmlTextReader that parses the content of an
/// in-memory buffer, wrap it into a smart pointer and return it.
///
/// @param buffer the in-memory buffer to be parsed by the returned
/// instance of xmlTextReader.
reader_sptr
new_reader_from_buffer(const std::string& buffer)
{
reader_sptr p =
build_sptr(xmlReaderForMemory(buffer.c_str(),
buffer.length(),
"", 0, 0));
return p;
}
/// Build and return a shared_ptr for a pointer to xmlTextReader
template<>
shared_ptr<xmlTextReader>

73
src/abg-libzip-utils.cc Normal file
View File

@ -0,0 +1,73 @@
// -*- mode: C++ -*-
//
// Copyright (C) 2013 Red Hat, Inc.
//
// This file is part of the GNU Application Binary Interface Generic
// Analysis and Instrumentation Library (libabigail). This library is
// free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 3, or (at your option) any
// later version.
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this program; see the file COPYING-LGPLV3. If
// not, see <http://www.gnu.org/licenses/>.
/// @file
#include <string>
#include "abg-libzip-utils.h"
namespace abigail
{
namespace zip_utils
{
/// This is a wrapper of the zip_open function, from libzip. Its
/// purpose is to return a zip pointer wrapped in an appropriate
/// shared_ptr and thus to free the caller from having to deal with
/// calling zip_close on it.
///
/// The arguments this wrapper have the same meaning as in zip_open.
///
/// @param path the path to the zip archive to open.
///
/// @return a non-null zip pointer if the function succeeds.
zip_sptr
open_archive(const string& path, int flags, int *errorp)
{
zip* z = zip_open(path.c_str(), flags, errorp);
if (!z)
return zip_sptr();
return zip_sptr(z, archive_deleter());
}
/// @brief Open a file from a zip archive.
///
/// Open the file that is at a given \a index in the \a archive.
///
/// @param archive the zip archive to consider
///
/// @param file_index the index of the file to open from the zip
/// \a archive.
///
/// @return a non-null zip_file* upon successful completion, a null
/// pointer otherwise.
zip_file_sptr
open_file_in_archive(zip_sptr archive,
int file_index)
{
zip_file * f = zip_fopen_index(archive.get(), file_index, 0);
if (!f)
return zip_file_sptr();
return zip_file_sptr(f, zip_file_deleter());
}
}// end namespace zip
}// end namespace abigail

View File

@ -2618,4 +2618,17 @@ translation_unit::read()
reader::read_context read_ctxt(xml::new_reader_from_file(this->get_path()));
return reader::read_input(read_ctxt, *this);
}
/// Deserialize the contents of an in-memory buffer into this
/// translation_unit object.
///
///@param buffer the in-memory buffer to de-serialize from.
///
/// @return true upon successful de-serialization, false otherwise.
bool
translation_unit::read(const string& buffer)
{
reader::read_context read_ctxt(xml::new_reader_from_buffer(buffer));
return reader::read_input(read_ctxt, *this);
}
}//end namespace abigail

View File

@ -20,27 +20,29 @@
/// @file
#include <ostream>
#include <iostream>
#include <fstream>
#include <sstream>
#include <tr1/memory>
#include <tr1/unordered_map>
#include "abg-config.h"
#include "abg-ir.h"
namespace abigail
{
/// Internal namespace for writer.
namespace writer
{
using std::cerr;
using std::tr1::shared_ptr;
using std::tr1::dynamic_pointer_cast;
using std::tr1::static_pointer_cast;
using std::ofstream;
using std::ostream;
using std::ostringstream;
using std::list;
using std::tr1::unordered_map;
/// Internal namespace for writer.
namespace writer
{
class id_manager
{
unsigned long long m_cur_id;
@ -1487,11 +1489,48 @@ write_class_tdecl(const shared_ptr<class_tdecl> decl,
} //end namespace writer
/// Serialize the contents of this translation unit object into an
/// output stream.
///
/// @param out the output stream.
bool
translation_unit::write(std::ostream &out)
translation_unit::write(std::ostream &out) const
{
writer::write_context ctxt(out);
return writer::write_translation_unit(*this, ctxt, /*indent=*/0);
}
/// Serialize the contents of this translation unit object into an
/// external file.
///
/// @param out the path to the external file.
bool
translation_unit::write(const string& path) const
{
bool result = true;
try
{
ofstream of(path, std::ios_base::trunc);
if (!of.is_open())
{
cerr << "failed to access " << path << "\n";
return false;
}
if (!write(of))
{
cerr << "failed to access " << path << "\n";
result = false;
}
of.close();
}
catch(...)
{
result = false;
}
return result;
}
} //end namespace abigail

View File

@ -2,6 +2,7 @@ h=$(abs_srcdir)
TESTS= \
runtestreadwrite \
runtestwritereadarchive \
runtestsvg \
runtestdot
@ -9,8 +10,8 @@ noinst_PROGRAMS= $(TESTS) testwalker
noinst_LTLIBRARIES = libtestutils.la
libtestutils_la_SOURCES=\
test-utils.h \
libtestutils_la_SOURCES= \
test-utils.h \
test-utils.cc
libtestutils_la_CXXFLAGS= \
@ -20,6 +21,9 @@ libtestutils_la_CXXFLAGS= \
runtestreadwrite_SOURCES=$(h)/test-read-write.cc
runtestreadwrite_LDADD=libtestutils.la $(top_builddir)/src/libabigail.la
runtestwritereadarchive_SOURCES=$(h)/test-write-read-archive.cc
runtestwritereadarchive_LDADD=libtestutils.la $(top_builddir)/src/libabigail.la
runtestsvg_SOURCES=$(h)/test-svg.cc
runtestsvg_LDADD=$(top_builddir)/src/libabigail.la
@ -49,7 +53,13 @@ data/test-read-write/test12.xml \
data/test-read-write/test13.xml \
data/test-read-write/test14.xml \
data/test-read-write/test15.xml \
data/test-read-write/test16.xml
data/test-read-write/test16.xml \
\
data/test-write-read-archive/test0.xml \
data/test-write-read-archive/test1.xml \
data/test-write-read-archive/test2.xml \
data/test-write-read-archive/test3.xml \
data/test-write-read-archive/test4.xml
clean-local: clean-local-check
.PHONY: clean-local-check

View File

@ -0,0 +1,3 @@
<abi-instr version='1.0'>
<type-decl name='int' size-in-bits='32' alignment-in-bits='32' id='type-id-1'/>
</abi-instr>

View File

@ -0,0 +1,5 @@
<abi-instr version='1.0'>
<namespace-decl name='foo'>
<type-decl name='int' size-in-bits='32' alignment-in-bits='32' id='type-id-1'/>
</namespace-decl>
</abi-instr>

View File

@ -0,0 +1,14 @@
<abi-instr version='1.0'>
<namespace-decl name='foo'>
<type-decl name='int' size-in-bits='32' alignment-in-bits='32' id='type-id-1'/>
</namespace-decl>
<namespace-decl name='bar'>
<type-decl name='int32' size-in-bits='32' alignment-in-bits='32' id='type-id-2'/>
</namespace-decl>
<namespace-decl name='baz'>
<namespace-decl name='barbaz'>
<type-decl name='int64' size-in-bits='64' alignment-in-bits='64' id='type-id-3'/>
</namespace-decl>
</namespace-decl>
<type-decl name='char' size-in-bits='8' alignment-in-bits='8' id='type-id-4'/>
</abi-instr>

View File

@ -0,0 +1,6 @@
<abi-instr version='1.0'>
<type-decl name='int' size-in-bits='32' alignment-in-bits='32' id='type-id-1'/>
<qualified-type-def type-id='type-id-1' const='yes' id='type-id-2'/>
<qualified-type-def type-id='type-id-1' volatile='yes' id='type-id-3'/>
<qualified-type-def type-id='type-id-1' const='yes' volatile='yes' id='type-id-4'/>
</abi-instr>

View File

@ -0,0 +1,4 @@
<abi-instr version='1.0'>
<type-decl name='int' size-in-bits='32' alignment-in-bits='32' id='type-id-1'/>
<pointer-type-def type-id='type-id-1' size-in-bits='64' alignment-in-bits='64' id='type-id-2'/>
</abi-instr>

View File

@ -0,0 +1,176 @@
// -*- Mode: C++ -*-
//
// Copyright (C) 2013 Red Hat, Inc.
//
// This file is part of the GNU Application Binary Interface Generic
// Analysis and Instrumentation Library (libabigail). This library is
// free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 3, or (at your option) any
// later version.
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this program; see the file COPYING-LGPLV3. If
// not, see <http://www.gnu.org/licenses/>.
#include <cstdlib>
#include <iostream>
#include <fstream>
#include "test-utils.h"
#include "abg-ir.h"
#include "abg-corpus.h"
struct InOutSpec
{
const char* in_path;
const char* out_path;
};// end struct InOutSpec
/// This is an aggregate that specifies where the test gets the
/// elements that it reads to build an archive. It also specifies
/// where to write the output result of the element that is written
/// back to disk, for diffing purposes.
InOutSpec archive_elements[] =
{
{
"data/test-write-read-archive/test0.xml",
"output/test-write-read-archive/test0.xml",
},
{
"data/test-write-read-archive/test1.xml",
"output/test-write-read-archive/test2.xml",
},
{
"data/test-write-read-archive/test2.xml",
"output/test-write-read-archive/test2.xml",
},
{
"data/test-write-read-archive/test3.xml",
"output/test-write-read-archive/test3.xml",
},
{
"data/test-write-read-archive/test4.xml",
"output/test-write-read-archive/test4.xml",
},
// This should be the last entry.
{NULL, NULL}
};
#define NUM_ARCHIVES_ELEMENTS \
((sizeof(archive_elements) / sizeof(InOutSpec)) -1)
/// Where to write the archive, and where to read it from to get the
/// base for the diffing.
const InOutSpec archive_spec =
{
"data/test-write-read-archive/archive.abi",
"output/test-write-read-archive/archive.abi"
};
using std::string;
using std::cerr;
using std::ofstream;
using std::tr1::shared_ptr;
using abigail::corpus;
using abigail::translation_unit;
int
main()
{
// Read the elements into abigail::translation_unit and stick them
// into an abigail::corpus.
string in_path, out_path;
bool is_ok = true;
out_path =
abigail::tests::get_build_dir() + "/tests/" + archive_spec.out_path;
if (!abigail::tests::ensure_parent_dir_created(out_path))
{
cerr << "Could not create parent director for " << out_path;
return 1;
}
corpus abi_corpus(out_path);
for (InOutSpec *s = archive_elements; s->in_path; ++s)
{
in_path = abigail::tests::get_src_dir() + "/tests/" + s->in_path;
shared_ptr<abigail::translation_unit> tu(new translation_unit(in_path));
if (!tu->read())
{
cerr << "failed to read " << in_path << "\n";
is_ok = false;
continue;
}
abi_corpus.add(tu);
}
if (!abi_corpus.write())
{
cerr << "failed to write archive file: " << abi_corpus.get_file_path();
return 1;
}
// Diff the archive members.
//
// Basically, re-read the corpus from disk, walk the loaded
// translation units, write them back and diff them against their
// reference.
abi_corpus.drop_translation_units();
if (abi_corpus.get_translation_units().size())
{
cerr << "In-memory object of abi corpus at '"
<< abi_corpus.get_file_path()
<< "' still has translation units after call to "
"corpus::drop_translation_units!";
return false;
}
if (!abi_corpus.read())
{
cerr << "Failed to load the abi corpus from path '"
<< abi_corpus.get_file_path()
<< "'";
return 1;
}
if (abi_corpus.get_translation_units().size() != NUM_ARCHIVES_ELEMENTS)
{
cerr << "Read " << abi_corpus.get_translation_units().size()
<< " elements from the abi corpus at "
<< abi_corpus.get_file_path()
<< " instead of "
<< NUM_ARCHIVES_ELEMENTS;
return 1;
}
for (unsigned i = 0; i < NUM_ARCHIVES_ELEMENTS; ++i)
{
InOutSpec& spec = archive_elements[i];
out_path =
abigail::tests::get_build_dir() + "/tests/" + spec.out_path;
if (!abi_corpus.get_translation_units()[i]->write(out_path))
{
cerr << "Failed to serialize translation_unit to '"
<< out_path
<< "'\n";
is_ok = false;
}
string ref =
abigail::tests::get_src_dir() + "/tests/" + spec.in_path;
string cmd = "diff -u " + ref + " " + out_path;
if (system(cmd.c_str()))
is_ok = false;
}
return !is_ok;
}