mirror of
git://sourceware.org/git/libabigail.git
synced 2025-02-18 23:07:00 +00:00
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:
parent
8884b58638
commit
c57e950ec7
14
configure.ac
14
configure.ac
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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__
|
||||
|
@ -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>
|
||||
|
72
include/abg-libzip-utils.h
Normal file
72
include/abg-libzip-utils.h
Normal 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
|
@ -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
320
src/abg-corpus.cc
Normal 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
|
@ -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
|
||||
|
@ -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
73
src/abg-libzip-utils.cc
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
3
tests/data/test-write-read-archive/test0.xml
Normal file
3
tests/data/test-write-read-archive/test0.xml
Normal 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>
|
5
tests/data/test-write-read-archive/test1.xml
Normal file
5
tests/data/test-write-read-archive/test1.xml
Normal 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>
|
14
tests/data/test-write-read-archive/test2.xml
Normal file
14
tests/data/test-write-read-archive/test2.xml
Normal 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>
|
6
tests/data/test-write-read-archive/test3.xml
Normal file
6
tests/data/test-write-read-archive/test3.xml
Normal 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>
|
4
tests/data/test-write-read-archive/test4.xml
Normal file
4
tests/data/test-write-read-archive/test4.xml
Normal 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>
|
176
tests/test-write-read-archive.cc
Normal file
176
tests/test-write-read-archive.cc
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user