pepper-0.3.2/0000755000175000001440000000000011762206662010014 500000000000000pepper-0.3.2/src/0000755000175000001440000000000011762206662010603 500000000000000pepper-0.3.2/src/main.h0000644000175000001440000000236611721276751011630 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: main.h * Global header file */ #ifndef MAIN_H_ #define MAIN_H_ #include "config.h" // Operating system detection (mostly from Qt, qglobal.h) #if defined(__APPLE__) && (defined(__GNUC__) || defined(__xlC__) || defined(__xlc__)) #define POS_DARWIN #elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) #define POS_BSD #elif defined(__linux__) || defined(__linux) #define POS_LINUX #elif defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) #define POS_WIN #endif // Private variables can be declared like this to enable easy unit testing #ifdef PEPPER_UNIT_TESTS #define PEPPER_PVARS public #define PEPPER_PROTVARS public #else #define PEPPER_PVARS private #define PEPPER_PROTVARS protected #endif // Standard integer types #include // Custom exception class #include "pex.h" #endif // MAIN_H_ pepper-0.3.2/src/abstractcache.h0000644000175000001440000000453411721276751013472 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: abstractcache.h * Abstract base class for revision caches (interface) */ #ifndef ABSTRACTCACHE_H_ #define ABSTRACTCACHE_H_ #include "backend.h" class Revision; // This cache should be transparent and inherits the wrapped class class AbstractCache : public Backend { public: AbstractCache(Backend *backend, const Options &options); virtual ~AbstractCache(); void init() { } void open() { m_backend->open(); } void close() { flush(); m_backend->close(); } std::string name() const { return m_backend->name(); } std::string uuid() { if (m_uuid.empty()) m_uuid = m_backend->uuid(); return m_uuid; } std::string head(const std::string &branch = std::string()) { return m_backend->head(branch); } std::string mainBranch() { return m_backend->mainBranch(); } std::vector branches() { return m_backend->branches(); } std::vector tags() { return m_backend->tags(); } Diffstat diffstat(const std::string &id); void filterDiffstat(Diffstat *stat) { m_backend->filterDiffstat(stat); } std::vector tree(const std::string &id = std::string()) { return m_backend->tree(id); } std::string cat(const std::string &path, const std::string &id = std::string()) { return m_backend->cat(path, id); }; LogIterator *iterator(const std::string &branch = std::string(), int64_t start = -1, int64_t end = -1) { return m_backend->iterator(branch, start, end); } void prefetch(const std::vector &ids); Revision *revision(const std::string &id); void finalize() { m_backend->finalize(); } static std::string cacheFile(Backend *backend, const std::string &name); virtual void flush() = 0; virtual void check(bool force = false) = 0; protected: std::string cacheDir(); virtual bool lookup(const std::string &id) = 0; virtual void put(const std::string &id, const Revision &rev) = 0; virtual Revision *get(const std::string &id) = 0; static void checkDir(const std::string &path, bool *created = NULL); protected: Backend *m_backend; std::string m_uuid; // Cached backend UUID }; #endif // ABSTRACTCACHE_H_ pepper-0.3.2/src/revisioniterator.h0000644000175000001440000000247411721276751014314 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: revisioniterator.h * Base class for revision iterators (interface) */ #ifndef REVISIONITERATOR_H_ #define REVISIONITERATOR_H_ #include #include #include "backend.h" #include "lunar/lunar.h" class RevisionIterator { public: enum Flags { PrefetchRevisions = 0x01 }; public: RevisionIterator(Backend *backend, const std::string &branch = std::string(), int64_t start = -1, int64_t end = -1, Flags flags = PrefetchRevisions); ~RevisionIterator(); bool atEnd(); std::string next(); int progress() const; private: void fetchLogs(); protected: Backend *m_backend; Backend::LogIterator *m_logIterator; std::queue m_queue; std::queue::size_type m_total, m_consumed; bool m_atEnd; Flags m_flags; // Lua binding public: RevisionIterator(lua_State *L); int next(lua_State *L); int revisions(lua_State *L); int map(lua_State *L); static const char className[]; static Lunar::RegType methods[]; }; #endif // REVISIONITERATOR_H_ pepper-0.3.2/src/gnuplot.cpp0000644000175000001440000000314611755247753012733 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: gnuplot.cpp * Bidirectional Gnuplot pipe */ #include "gnuplot.h" #include "syslib/fs.h" #include "syslib/parallel.h" using sys::io::PopenStreambuf; // Internal reader thread class StreambufReader : public sys::parallel::Thread { public: StreambufReader(PopenStreambuf *buf, std::ostream &out) : m_buf(buf), m_out(out) { } protected: void run() { char buffer[1024]; std::istream in(m_buf); while (in.good()) { in.read(buffer, sizeof(buffer)); m_out.write(buffer, in.gcount()); } } private: PopenStreambuf *m_buf; std::ostream &m_out; }; // Standard output terminal std::string Gnuplot::s_stdTerminal; // Constructor Gnuplot::Gnuplot(const char * const *args, std::ostream &out) { std::string path = sys::fs::which("gnuplot"); m_buf = new PopenStreambuf(path.c_str(), args, std::ios::in | std::ios::out); m_reader = new StreambufReader(m_buf, out); m_reader->start(); m_pipe = new std::ostream(m_buf); } // Destructor Gnuplot::~Gnuplot() { delete m_pipe; m_buf->closeWrite(); m_reader->wait(); delete m_buf; delete m_reader; } // Writes a command to the Gnuplot pipe void Gnuplot::cmd(const std::string &str) { *m_pipe << str << "\n" << std::flush; } // Writes a command to the Gnuplot pipe void Gnuplot::cmd(const char *str, size_t len) { m_pipe->write(str, len); *m_pipe << std::flush; } pepper-0.3.2/src/ldbcache.cpp0000644000175000001440000001244511721276751012763 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: ldbcache.cpp * Revision cache using Leveldb */ #include "main.h" #include #include "bstream.h" #include "cache.h" #include "logger.h" #include "revision.h" #include "strlib.h" #include "utils.h" #include "syslib/fs.h" #include "ldbcache.h" // Constructor LdbCache::LdbCache(Backend *backend, const Options &options) : AbstractCache(backend, options), m_db(NULL) { } // Destructor LdbCache::~LdbCache() { closedb(); } // Flushes the cache to disk void LdbCache::flush() { } // Checks cache consistency void LdbCache::check(bool force) { try { if (!m_db) opendb(); } catch (const std::exception &ex) { PDEBUG << "Exception while opening database: " << ex.what() << endl; Logger::info() << "LdbCache: Database can't be opened, trying to repair it" << endl; } if (force || !m_db) { std::string path = cacheDir() + "/ldb"; leveldb::Status status = leveldb::RepairDB(path, leveldb::Options()); if (!status.ok()) { Logger::err() << "Error repairing database: " << status.ToString() << endl; } else { Logger::info() << "LdbCache: Database repaired" << endl; } closedb(); opendb(); } // Simply try to read all revisions Logger::info() << "LdbCache: Checking revisions..." << endl; std::vector corrupted; size_t n = 0; leveldb::Iterator* it = m_db->NewIterator(leveldb::ReadOptions()); for (it->SeekToFirst(); it->Valid(); it->Next()) { Revision rev(it->key().ToString()); std::string value = it->value().ToString(); MIStream rin(value.c_str(), value.length()); if (!rev.load(rin)) { PDEBUG << "Revision " << it->key().ToString() << " corrupted!" << endl; corrupted.push_back(it->key().ToString()); } ++n; } if (!it->status().ok()) { Logger::err() << "Error iterating over cached revisions: " << it->status().ToString() << endl; Logger::err() << "Please re-run with --force to repair the database (might cause data loss)" << endl; return; } Logger::info() << "LdbCache: Checked " << n << " revisions, found " << corrupted.size() << " to be corrupted" << endl; for (size_t i = 0; i < corrupted.size(); i++) { Logger::err() << "LdbCache: Revision " << corrupted[i] << " is corrupted, removing from index file" << endl; leveldb::Status status = m_db->Delete(leveldb::WriteOptions(), corrupted[i]); if (!status.ok()) { Logger::err() << "Error: Can't remove from revision " << corrupted[i] << " from database: " << status.ToString() << endl; return; } } } // Checks if the diffstat of the given revision is already cached bool LdbCache::lookup(const std::string &id) { if (!m_db) opendb(); std::string value; leveldb::Status s = m_db->Get(leveldb::ReadOptions(), id, &value); if (s.IsNotFound()) { return false; } if (!s.ok()) { throw PEX(str::printf("Error reading from cache: %s", s.ToString().c_str())); } return true; } // Adds the revision to the cache void LdbCache::put(const std::string &id, const Revision &rev) { if (!m_db) opendb(); MOStream rout; rev.write(rout); std::vector data(rout.data()); leveldb::Status s = m_db->Put(leveldb::WriteOptions(), id, std::string(data.begin(), data.end())); if (!s.ok()) { throw PEX(str::printf("Error writing to cache: %s", s.ToString().c_str())); } } Revision *LdbCache::get(const std::string &id) { if (!m_db) opendb(); std::string value; leveldb::Status s = m_db->Get(leveldb::ReadOptions(), id, &value); if (!s.ok()) { throw PEX(str::printf("Error reading from cache: %s", s.ToString().c_str())); } Revision *rev = new Revision(id); MIStream rin(value.c_str(), value.length()); if (!rev->load(rin)) { throw PEX(str::printf("Unable to read from cache: Data corrupted")); } return rev; } // Opens the database connection void LdbCache::opendb() { if (m_db) return; std::string path = cacheDir() + "/ldb"; PDEBUG << "Using cache dir: " << path << endl; if (!sys::fs::dirExists(path)) { sys::fs::mkpath(path); } leveldb::Options options; options.create_if_missing = false; leveldb::Status s = leveldb::DB::Open(options, path, &m_db); if (!s.ok()) { // New cache: Import revisions from old cache options.create_if_missing = true; leveldb::Status s = leveldb::DB::Open(options, path, &m_db); if (!s.ok()) { throw PEX(str::printf("Unable to open database %s: %s", path.c_str(), s.ToString().c_str())); } Cache c(m_backend, m_opts); import(&c); } } // Closes the database connection void LdbCache::closedb() { delete m_db; m_db = NULL; } // Imports all revisions from the given cache void LdbCache::import(Cache *cache) { try { cache->load(); } catch (const std::exception &ex) { PDEBUG << "Error loading old cache for import: " << ex.what() << endl; return; } Logger::info() << "LdbCache: Found old cache, importing revisions..." << endl; std::map >::const_iterator it; size_t n = 0; for (it = cache->m_index.begin(); it != cache->m_index.end(); ++it) { Revision *rev = cache->get(it->first); put(it->first, *rev); delete rev; ++n; } Logger::info() << "LdbCache: Imported " << n << " revisions" << endl; } pepper-0.3.2/src/jobqueue.h0000644000175000001440000000573211721276751012523 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: jobqueue.h * Simple thread-safe job queue */ #ifndef JOBQUEUE_H_ #define JOBQUEUE_H_ #include #include #include #include "logger.h" #include "syslib/parallel.h" template class JobQueue { public: JobQueue(size_t max = 512) : m_max(max), m_end(false) { } void put(const std::vector &args) { m_mutex.lock(); for (unsigned int i = 0; i < args.size(); i++) { m_queue.push(args[i]); m_status[args[i]] = -1; } m_mutex.unlock(); m_argWait.wakeAll(); } void stop() { m_mutex.lock(); m_end = true; m_mutex.unlock(); m_argWait.wakeAll(); m_resultWait.wakeAll(); } bool getArg(Arg *arg) { m_mutex.lock(); while (!m_end && (m_queue.empty() || m_results.size() > m_max)) { m_argWait.wait(&m_mutex); } if (m_end) { m_mutex.unlock(); return false; } *arg = m_queue.front(); m_queue.pop(); m_mutex.unlock(); return true; } bool getArgs(std::vector *args, size_t max) { m_mutex.lock(); while (!m_end && (m_queue.empty() || m_results.size() > m_max)) { m_argWait.wait(&m_mutex); } if (m_end) { m_mutex.unlock(); return false; } args->clear(); while (args->size() < max && !m_queue.empty()) { args->push_back(m_queue.front()); m_queue.pop(); } m_mutex.unlock(); return true; } bool hasArg(const Arg &arg) { m_mutex.lock(); bool has = (m_status.find(arg) != m_status.end()); m_mutex.unlock(); return has; } bool getResult(const Arg &arg, Result *res) { m_mutex.lock(); if (m_status.find(arg) == m_status.end()) { m_mutex.unlock(); return false; } while (!m_end && m_status[arg] < 0) { m_resultWait.wait(&m_mutex); } if (m_end || !m_status[arg]) { m_mutex.unlock(); return false; } *res = m_results[arg]; m_results.erase(arg); m_status.erase(arg); m_mutex.unlock(); m_argWait.wake(); return true; } void done(const Arg &arg, const Result &result) { m_mutex.lock(); m_results[arg] = result; m_status[arg] = 1; #ifdef DEBUG PTRACE << arg << " ok, " << m_results.size() << " results in queue" << endl; #endif m_mutex.unlock(); m_resultWait.wakeAll(); } void failed(const Arg &arg) { m_mutex.lock(); m_status[arg] = 0; #ifdef DEBUG PTRACE << arg << " FAILED, " << m_results.size() << " results in queue" << endl; #endif m_mutex.unlock(); m_resultWait.wakeAll(); } private: sys::parallel::Mutex m_mutex; sys::parallel::WaitCondition m_argWait, m_resultWait; std::queue m_queue; std::map m_results; std::map m_status; size_t m_max; bool m_end; }; #endif // JOBQUEUE_H_ pepper-0.3.2/src/Makefile.in0000644000175000001440000011566111762206057012600 00000000000000# Makefile.in generated by automake 1.12 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software # Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # # pepper - SCM statistics report generator # Copyright (C) 2010-2012 Jonas Gehring # # Released under the GNU General Public License, version 3. # Please see the COPYING file in the source distribution for license # terms and conditions, or see http://www.gnu.org/licenses/. # VPATH = @srcdir@ am__make_dryrun = \ { \ am__dry=no; \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \ | grep '^AM OK$$' >/dev/null || am__dry=yes;; \ *) \ for am__flg in $$MAKEFLAGS; do \ case $$am__flg in \ *=*|--*) ;; \ *n*) am__dry=yes; break;; \ esac; \ done;; \ esac; \ test $$am__dry = yes; \ } pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = pepper$(EXEEXT) # Debugging? @DEBUG_TRUE@am__append_1 = \ @DEBUG_TRUE@ -rdynamic @DEBUG_TRUE@am__append_2 = \ @DEBUG_TRUE@ -rdynamic # Optional features @GIT_BACKEND_TRUE@am__append_3 = \ @GIT_BACKEND_TRUE@ backends/git.h backends/git.cpp @GIT_BACKEND_TRUE@am__append_4 = \ @GIT_BACKEND_TRUE@ -DUSE_GIT @MERCURIAL_BACKEND_TRUE@am__append_5 = \ @MERCURIAL_BACKEND_TRUE@ backends/mercurial.h backends/mercurial.cpp @MERCURIAL_BACKEND_TRUE@am__append_6 = \ @MERCURIAL_BACKEND_TRUE@ -DUSE_MERCURIAL @MERCURIAL_BACKEND_TRUE@am__append_7 = \ @MERCURIAL_BACKEND_TRUE@ $(PYTHON_CPPFLAGS) @MERCURIAL_BACKEND_TRUE@am__append_8 = \ @MERCURIAL_BACKEND_TRUE@ $(PYTHON_LDFLAGS) @SVN_BACKEND_TRUE@am__append_9 = \ @SVN_BACKEND_TRUE@ backends/subversion.h backends/subversion.cpp \ @SVN_BACKEND_TRUE@ backends/subversion_p.h \ @SVN_BACKEND_TRUE@ backends/subversion_delta.cpp @SVN_BACKEND_TRUE@am__append_10 = \ @SVN_BACKEND_TRUE@ -DUSE_SUBVERSION $(APR_CPPFLAGS) $(APR_INCLUDES) @SVN_BACKEND_TRUE@am__append_11 = \ @SVN_BACKEND_TRUE@ -Wno-deprecated-declarations \ @SVN_BACKEND_TRUE@ $(APR_CFLAGS) \ @SVN_BACKEND_TRUE@ $(SVN_CFLAGS) @SVN_BACKEND_TRUE@am__append_12 = \ @SVN_BACKEND_TRUE@ $(SVN_LDFLAGS) \ @SVN_BACKEND_TRUE@ $(SVN_LIBS) \ @SVN_BACKEND_TRUE@ $(APR_LIBS) @GNUPLOT_TRUE@am__append_13 = \ @GNUPLOT_TRUE@ plot.h plot.cpp \ @GNUPLOT_TRUE@ gnuplot.h gnuplot.cpp @GNUPLOT_TRUE@am__append_14 = \ @GNUPLOT_TRUE@ -DUSE_GNUPLOT @LEVELDB_TRUE@am__append_15 = \ @LEVELDB_TRUE@ ldbcache.h ldbcache.cpp @LEVELDB_TRUE@am__append_16 = \ @LEVELDB_TRUE@ $(LEVELDB_CPPFLAGS) @LEVELDB_TRUE@am__append_17 = \ @LEVELDB_TRUE@ $(LEVELDB_LIBS) @LEVELDB_TRUE@am__append_18 = \ @LEVELDB_TRUE@ -DUSE_LDBCACHE subdir = src DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ $(top_srcdir)/depcomp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_zlib.m4 \ $(top_srcdir)/m4/ax_lua.m4 $(top_srcdir)/m4/ax_pthread.m4 \ $(top_srcdir)/m4/configure_backends.m4 \ $(top_srcdir)/m4/find_apr.m4 $(top_srcdir)/m4/find_svn.m4 \ $(top_srcdir)/m4/ax_python_devel.m4 \ $(top_srcdir)/m4/configure_features.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) AR = ar ARFLAGS = cru libpepper_a_AR = $(AR) $(ARFLAGS) libpepper_a_LIBADD = am__libpepper_a_SOURCES_DIST = abstractcache.h abstractcache.cpp \ backend.h backend.cpp bstream.h bstream.cpp cache.h cache.cpp \ diffstat.h diffstat.cpp jobqueue.h logger.h logger.cpp \ luahelpers.h luamodules.h luamodules.cpp main.h options.h \ options.cpp pex.h pex.cpp report.h report.cpp repository.h \ repository.cpp revision.h revision.cpp revisioniterator.h \ revisioniterator.cpp strlib.h strlib.cpp tag.h tag.cpp utils.h \ utils.cpp syslib/fs.h syslib/fs.cpp syslib/io.h syslib/io.cpp \ syslib/parallel.h syslib/parallel.cpp syslib/sigblock.h \ syslib/sigblock.cpp syslib/datetime.h syslib/datetime.cpp \ 3rdparty/lunar/lunar.h backends/git.h backends/git.cpp \ backends/mercurial.h backends/mercurial.cpp \ backends/subversion.h backends/subversion.cpp \ backends/subversion_p.h backends/subversion_delta.cpp plot.h \ plot.cpp gnuplot.h gnuplot.cpp ldbcache.h ldbcache.cpp @GIT_BACKEND_TRUE@am__objects_1 = git.$(OBJEXT) @MERCURIAL_BACKEND_TRUE@am__objects_2 = mercurial.$(OBJEXT) @SVN_BACKEND_TRUE@am__objects_3 = subversion.$(OBJEXT) \ @SVN_BACKEND_TRUE@ subversion_delta.$(OBJEXT) @GNUPLOT_TRUE@am__objects_4 = plot.$(OBJEXT) gnuplot.$(OBJEXT) @LEVELDB_TRUE@am__objects_5 = ldbcache.$(OBJEXT) am_libpepper_a_OBJECTS = abstractcache.$(OBJEXT) backend.$(OBJEXT) \ bstream.$(OBJEXT) cache.$(OBJEXT) diffstat.$(OBJEXT) \ logger.$(OBJEXT) luamodules.$(OBJEXT) options.$(OBJEXT) \ pex.$(OBJEXT) report.$(OBJEXT) repository.$(OBJEXT) \ revision.$(OBJEXT) revisioniterator.$(OBJEXT) strlib.$(OBJEXT) \ tag.$(OBJEXT) utils.$(OBJEXT) fs.$(OBJEXT) io.$(OBJEXT) \ parallel.$(OBJEXT) sigblock.$(OBJEXT) datetime.$(OBJEXT) \ $(am__objects_1) $(am__objects_2) $(am__objects_3) \ $(am__objects_4) $(am__objects_5) libpepper_a_OBJECTS = $(am_libpepper_a_OBJECTS) am__installdirs = "$(DESTDIR)$(bindir)" PROGRAMS = $(bin_PROGRAMS) am_pepper_OBJECTS = main.$(OBJEXT) pepper_OBJECTS = $(am_pepper_OBJECTS) pepper_DEPENDENCIES = libpepper.a DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) CXXLD = $(CXX) CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ -o $@ COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ SOURCES = $(libpepper_a_SOURCES) $(pepper_SOURCES) DIST_SOURCES = $(am__libpepper_a_SOURCES_DIST) $(pepper_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ ALLOCA = @ALLOCA@ AMTAR = @AMTAR@ APR_CFLAGS = @APR_CFLAGS@ APR_CPPFLAGS = @APR_CPPFLAGS@ APR_INCLUDES = @APR_INCLUDES@ APR_LIBS = @APR_LIBS@ ASCIIDOC = @ASCIIDOC@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FRAMEWORKS = @FRAMEWORKS@ GIT = @GIT@ GNUPLOT = @GNUPLOT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDFLAGS = @LDFLAGS@ LEVELDB_CPPFLAGS = @LEVELDB_CPPFLAGS@ LEVELDB_LIBS = @LEVELDB_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ $(am__append_17) LTLIBOBJS = @LTLIBOBJS@ LUA = @LUA@ LUA_INCLUDE = @LUA_INCLUDE@ LUA_LIB = @LUA_LIB@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PTHREAD_CC = @PTHREAD_CC@ PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ PTHREAD_LIBS = @PTHREAD_LIBS@ PYTHON = @PYTHON@ PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@ PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@ PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@ PYTHON_LDFLAGS = @PYTHON_LDFLAGS@ PYTHON_SITE_PKG = @PYTHON_SITE_PKG@ PYTHON_VERSION = @PYTHON_VERSION@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ SVN_CFLAGS = @SVN_CFLAGS@ SVN_LDFLAGS = @SVN_LDFLAGS@ SVN_LIBS = @SVN_LIBS@ VERSION = @VERSION@ XMLTO = @XMLTO@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ ax_pthread_config = @ax_pthread_config@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ # Pack all code into a static library, so the unit test programs # can use it easily. noinst_LIBRARIES = libpepper.a libpepper_a_SOURCES = abstractcache.h abstractcache.cpp backend.h \ backend.cpp bstream.h bstream.cpp cache.h cache.cpp diffstat.h \ diffstat.cpp jobqueue.h logger.h logger.cpp luahelpers.h \ luamodules.h luamodules.cpp main.h options.h options.cpp pex.h \ pex.cpp report.h report.cpp repository.h repository.cpp \ revision.h revision.cpp revisioniterator.h \ revisioniterator.cpp strlib.h strlib.cpp tag.h tag.cpp utils.h \ utils.cpp syslib/fs.h syslib/fs.cpp syslib/io.h syslib/io.cpp \ syslib/parallel.h syslib/parallel.cpp syslib/sigblock.h \ syslib/sigblock.cpp syslib/datetime.h syslib/datetime.cpp \ 3rdparty/lunar/lunar.h $(am__append_3) $(am__append_5) \ $(am__append_9) $(am__append_13) $(am__append_15) # This is the main program pepper_SOURCES = \ main.cpp pepper_LDADD = \ libpepper.a AM_CXXFLAGS = -Wall -W -pipe $(PTHREAD_CFLAGS) $(am__append_1) \ $(am__append_7) $(am__append_11) $(am__append_16) INCLUDES = \ -I@top_srcdir@/src/3rdparty AM_CPPFLAGS = $(LUA_INCLUDE) -DDATADIR=\"$(pkgdatadir)\" \ $(am__append_4) $(am__append_6) $(am__append_10) \ $(am__append_14) $(am__append_18) AM_LDFLAGS = $(PTHREAD_LIBS) $(LUA_LIB) $(FRAMEWORKS) $(am__append_2) \ $(am__append_8) $(am__append_12) # Last but not least, the CFLAGS AM_CFLAGS = $(AM_CXXFLAGS) all: all-am .SUFFIXES: .SUFFIXES: .cpp .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu src/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libpepper.a: $(libpepper_a_OBJECTS) $(libpepper_a_DEPENDENCIES) $(EXTRA_libpepper_a_DEPENDENCIES) -rm -f libpepper.a $(libpepper_a_AR) libpepper.a $(libpepper_a_OBJECTS) $(libpepper_a_LIBADD) $(RANLIB) libpepper.a install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p; \ then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) pepper$(EXEEXT): $(pepper_OBJECTS) $(pepper_DEPENDENCIES) $(EXTRA_pepper_DEPENDENCIES) @rm -f pepper$(EXEEXT) $(CXXLINK) $(pepper_OBJECTS) $(pepper_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/abstractcache.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bstream.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cache.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/datetime.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/diffstat.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fs.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/git.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnuplot.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/io.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldbcache.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logger.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/luamodules.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mercurial.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parallel.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pex.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plot.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/report.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/repository.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/revision.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/revisioniterator.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sigblock.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strlib.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subversion.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subversion_delta.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tag.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` fs.o: syslib/fs.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT fs.o -MD -MP -MF $(DEPDIR)/fs.Tpo -c -o fs.o `test -f 'syslib/fs.cpp' || echo '$(srcdir)/'`syslib/fs.cpp @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/fs.Tpo $(DEPDIR)/fs.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='syslib/fs.cpp' object='fs.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o fs.o `test -f 'syslib/fs.cpp' || echo '$(srcdir)/'`syslib/fs.cpp fs.obj: syslib/fs.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT fs.obj -MD -MP -MF $(DEPDIR)/fs.Tpo -c -o fs.obj `if test -f 'syslib/fs.cpp'; then $(CYGPATH_W) 'syslib/fs.cpp'; else $(CYGPATH_W) '$(srcdir)/syslib/fs.cpp'; fi` @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/fs.Tpo $(DEPDIR)/fs.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='syslib/fs.cpp' object='fs.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o fs.obj `if test -f 'syslib/fs.cpp'; then $(CYGPATH_W) 'syslib/fs.cpp'; else $(CYGPATH_W) '$(srcdir)/syslib/fs.cpp'; fi` io.o: syslib/io.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT io.o -MD -MP -MF $(DEPDIR)/io.Tpo -c -o io.o `test -f 'syslib/io.cpp' || echo '$(srcdir)/'`syslib/io.cpp @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/io.Tpo $(DEPDIR)/io.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='syslib/io.cpp' object='io.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o io.o `test -f 'syslib/io.cpp' || echo '$(srcdir)/'`syslib/io.cpp io.obj: syslib/io.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT io.obj -MD -MP -MF $(DEPDIR)/io.Tpo -c -o io.obj `if test -f 'syslib/io.cpp'; then $(CYGPATH_W) 'syslib/io.cpp'; else $(CYGPATH_W) '$(srcdir)/syslib/io.cpp'; fi` @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/io.Tpo $(DEPDIR)/io.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='syslib/io.cpp' object='io.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o io.obj `if test -f 'syslib/io.cpp'; then $(CYGPATH_W) 'syslib/io.cpp'; else $(CYGPATH_W) '$(srcdir)/syslib/io.cpp'; fi` parallel.o: syslib/parallel.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT parallel.o -MD -MP -MF $(DEPDIR)/parallel.Tpo -c -o parallel.o `test -f 'syslib/parallel.cpp' || echo '$(srcdir)/'`syslib/parallel.cpp @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/parallel.Tpo $(DEPDIR)/parallel.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='syslib/parallel.cpp' object='parallel.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o parallel.o `test -f 'syslib/parallel.cpp' || echo '$(srcdir)/'`syslib/parallel.cpp parallel.obj: syslib/parallel.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT parallel.obj -MD -MP -MF $(DEPDIR)/parallel.Tpo -c -o parallel.obj `if test -f 'syslib/parallel.cpp'; then $(CYGPATH_W) 'syslib/parallel.cpp'; else $(CYGPATH_W) '$(srcdir)/syslib/parallel.cpp'; fi` @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/parallel.Tpo $(DEPDIR)/parallel.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='syslib/parallel.cpp' object='parallel.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o parallel.obj `if test -f 'syslib/parallel.cpp'; then $(CYGPATH_W) 'syslib/parallel.cpp'; else $(CYGPATH_W) '$(srcdir)/syslib/parallel.cpp'; fi` sigblock.o: syslib/sigblock.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT sigblock.o -MD -MP -MF $(DEPDIR)/sigblock.Tpo -c -o sigblock.o `test -f 'syslib/sigblock.cpp' || echo '$(srcdir)/'`syslib/sigblock.cpp @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/sigblock.Tpo $(DEPDIR)/sigblock.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='syslib/sigblock.cpp' object='sigblock.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o sigblock.o `test -f 'syslib/sigblock.cpp' || echo '$(srcdir)/'`syslib/sigblock.cpp sigblock.obj: syslib/sigblock.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT sigblock.obj -MD -MP -MF $(DEPDIR)/sigblock.Tpo -c -o sigblock.obj `if test -f 'syslib/sigblock.cpp'; then $(CYGPATH_W) 'syslib/sigblock.cpp'; else $(CYGPATH_W) '$(srcdir)/syslib/sigblock.cpp'; fi` @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/sigblock.Tpo $(DEPDIR)/sigblock.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='syslib/sigblock.cpp' object='sigblock.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o sigblock.obj `if test -f 'syslib/sigblock.cpp'; then $(CYGPATH_W) 'syslib/sigblock.cpp'; else $(CYGPATH_W) '$(srcdir)/syslib/sigblock.cpp'; fi` datetime.o: syslib/datetime.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT datetime.o -MD -MP -MF $(DEPDIR)/datetime.Tpo -c -o datetime.o `test -f 'syslib/datetime.cpp' || echo '$(srcdir)/'`syslib/datetime.cpp @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/datetime.Tpo $(DEPDIR)/datetime.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='syslib/datetime.cpp' object='datetime.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o datetime.o `test -f 'syslib/datetime.cpp' || echo '$(srcdir)/'`syslib/datetime.cpp datetime.obj: syslib/datetime.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT datetime.obj -MD -MP -MF $(DEPDIR)/datetime.Tpo -c -o datetime.obj `if test -f 'syslib/datetime.cpp'; then $(CYGPATH_W) 'syslib/datetime.cpp'; else $(CYGPATH_W) '$(srcdir)/syslib/datetime.cpp'; fi` @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/datetime.Tpo $(DEPDIR)/datetime.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='syslib/datetime.cpp' object='datetime.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o datetime.obj `if test -f 'syslib/datetime.cpp'; then $(CYGPATH_W) 'syslib/datetime.cpp'; else $(CYGPATH_W) '$(srcdir)/syslib/datetime.cpp'; fi` git.o: backends/git.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT git.o -MD -MP -MF $(DEPDIR)/git.Tpo -c -o git.o `test -f 'backends/git.cpp' || echo '$(srcdir)/'`backends/git.cpp @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/git.Tpo $(DEPDIR)/git.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='backends/git.cpp' object='git.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o git.o `test -f 'backends/git.cpp' || echo '$(srcdir)/'`backends/git.cpp git.obj: backends/git.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT git.obj -MD -MP -MF $(DEPDIR)/git.Tpo -c -o git.obj `if test -f 'backends/git.cpp'; then $(CYGPATH_W) 'backends/git.cpp'; else $(CYGPATH_W) '$(srcdir)/backends/git.cpp'; fi` @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/git.Tpo $(DEPDIR)/git.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='backends/git.cpp' object='git.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o git.obj `if test -f 'backends/git.cpp'; then $(CYGPATH_W) 'backends/git.cpp'; else $(CYGPATH_W) '$(srcdir)/backends/git.cpp'; fi` mercurial.o: backends/mercurial.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT mercurial.o -MD -MP -MF $(DEPDIR)/mercurial.Tpo -c -o mercurial.o `test -f 'backends/mercurial.cpp' || echo '$(srcdir)/'`backends/mercurial.cpp @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/mercurial.Tpo $(DEPDIR)/mercurial.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='backends/mercurial.cpp' object='mercurial.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o mercurial.o `test -f 'backends/mercurial.cpp' || echo '$(srcdir)/'`backends/mercurial.cpp mercurial.obj: backends/mercurial.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT mercurial.obj -MD -MP -MF $(DEPDIR)/mercurial.Tpo -c -o mercurial.obj `if test -f 'backends/mercurial.cpp'; then $(CYGPATH_W) 'backends/mercurial.cpp'; else $(CYGPATH_W) '$(srcdir)/backends/mercurial.cpp'; fi` @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/mercurial.Tpo $(DEPDIR)/mercurial.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='backends/mercurial.cpp' object='mercurial.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o mercurial.obj `if test -f 'backends/mercurial.cpp'; then $(CYGPATH_W) 'backends/mercurial.cpp'; else $(CYGPATH_W) '$(srcdir)/backends/mercurial.cpp'; fi` subversion.o: backends/subversion.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT subversion.o -MD -MP -MF $(DEPDIR)/subversion.Tpo -c -o subversion.o `test -f 'backends/subversion.cpp' || echo '$(srcdir)/'`backends/subversion.cpp @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/subversion.Tpo $(DEPDIR)/subversion.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='backends/subversion.cpp' object='subversion.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o subversion.o `test -f 'backends/subversion.cpp' || echo '$(srcdir)/'`backends/subversion.cpp subversion.obj: backends/subversion.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT subversion.obj -MD -MP -MF $(DEPDIR)/subversion.Tpo -c -o subversion.obj `if test -f 'backends/subversion.cpp'; then $(CYGPATH_W) 'backends/subversion.cpp'; else $(CYGPATH_W) '$(srcdir)/backends/subversion.cpp'; fi` @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/subversion.Tpo $(DEPDIR)/subversion.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='backends/subversion.cpp' object='subversion.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o subversion.obj `if test -f 'backends/subversion.cpp'; then $(CYGPATH_W) 'backends/subversion.cpp'; else $(CYGPATH_W) '$(srcdir)/backends/subversion.cpp'; fi` subversion_delta.o: backends/subversion_delta.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT subversion_delta.o -MD -MP -MF $(DEPDIR)/subversion_delta.Tpo -c -o subversion_delta.o `test -f 'backends/subversion_delta.cpp' || echo '$(srcdir)/'`backends/subversion_delta.cpp @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/subversion_delta.Tpo $(DEPDIR)/subversion_delta.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='backends/subversion_delta.cpp' object='subversion_delta.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o subversion_delta.o `test -f 'backends/subversion_delta.cpp' || echo '$(srcdir)/'`backends/subversion_delta.cpp subversion_delta.obj: backends/subversion_delta.cpp @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT subversion_delta.obj -MD -MP -MF $(DEPDIR)/subversion_delta.Tpo -c -o subversion_delta.obj `if test -f 'backends/subversion_delta.cpp'; then $(CYGPATH_W) 'backends/subversion_delta.cpp'; else $(CYGPATH_W) '$(srcdir)/backends/subversion_delta.cpp'; fi` @am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/subversion_delta.Tpo $(DEPDIR)/subversion_delta.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='backends/subversion_delta.cpp' object='subversion_delta.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o subversion_delta.obj `if test -f 'backends/subversion_delta.cpp'; then $(CYGPATH_W) 'backends/subversion_delta.cpp'; else $(CYGPATH_W) '$(srcdir)/backends/subversion_delta.cpp'; fi` ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: CTAGS CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: $(HEADERS) $(SOURCES) $(LISP) list='$(SOURCES) $(HEADERS) $(LISP)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LIBRARIES) $(PROGRAMS) installdirs: for dir in "$(DESTDIR)$(bindir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-binPROGRAMS clean-generic clean-noinstLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ clean-generic clean-noinstLIBRARIES cscopelist ctags distclean \ distclean-compile distclean-generic distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-binPROGRAMS install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \ tags uninstall uninstall-am uninstall-binPROGRAMS # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: pepper-0.3.2/src/main.cpp0000644000175000001440000001615011755247753012166 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: main.cpp * Program entry point */ #include "main.h" #include #include #include #if defined(POS_LINUX) && defined(DEBUG) #include #endif #include "backend.h" #include "abstractcache.h" #include "logger.h" #include "options.h" #include "report.h" #ifdef USE_LDBCACHE #include "ldbcache.h" #else #include "cache.h" #endif #include "syslib/sigblock.h" // Signal handler struct SignalHandler : public sys::sigblock::Handler { SignalHandler(AbstractCache *cache = NULL) : cache(cache) { } void operator()(int signum) { // This is very unclean, but functions called from here should not be // blocked by logging functions. And this is more useful than turning // logging off. Logger::unlock(); if (cache) { Logger::status() << "Catched signal " << signum << ", flushing cache" << endl; cache->flush(); } } AbstractCache *cache; }; // Prints a short footer for help screens and listings static void printFooter() { std::cout << std::endl; std::cout << "Report bugs to " << "<" << PACKAGE_BUGREPORT ">" << std::endl; } // Prints program usage information static void printHelp(const Options &opts) { std::cout << "USAGE: " << PACKAGE_NAME << " [options] [report options] [repository]" << std::endl << std::endl; std::cout << "Main options:" << std::endl; Options::printHelp(); if (!opts.repository().empty() || !opts.forcedBackend().empty()) { std::cout << std::endl; try { Backend *backend = Backend::backendFor(opts); if (backend == NULL) { throw PEX("No backend found"); } std::cout << "Options for the " << backend->name() << " backend:" << std::endl; backend->printHelp(); delete backend; } catch (const std::exception &ex) { std::cout << "Sorry, unable to find a backend for '" << opts.repository() << "'" << std::endl; } } if (!opts.report().empty()) { std::cout << std::endl; try { Report r(opts.report()); r.printHelp(); } catch (const PepperException &ex) { std::cerr << ex.where() << ": " << ex.what() << std::endl; return; } } printFooter(); } // Prints the program version static void printVersion() { std::cout << PACKAGE_NAME << " " << PACKAGE_VERSION << std::endl; std::cout << "Copyright (C) 2010-2012 " << "Jonas Gehring <" << PACKAGE_BUGREPORT << ">" << std::endl; std::cout << "Released under the GNU General Public License." << std::endl; } // Configures the global logging streams for the given options static void setupLogger(std::vector *streams, const Options &) { // Note that the log level has already been set by Options::parse() #ifdef DEBUG // In debug mode, write log data to files if the log level is not high enough std::string files[Logger::NumLevels] = {"", "error.out", "warn.out", "status.out", "info.out", "debug.out", "trace.out"}; for (int i = Logger::level()+1; i < Logger::NumLevels; i++) { if (!files[i].empty()) { std::ofstream *out = new std::ofstream(); out->open(files[i].c_str(), std::ios::out); assert(out->good()); streams->push_back(out); Logger::setOutput(*out, i); } } // Turn log level to maximum Logger::setLevel(Logger::NumLevels); #else (void)streams; // No compiler warnings, please #endif } // Runs the program according to the given actions int start(const Options &opts) { // Print requested help screens or listings if (opts.helpRequested()) { printHelp(opts); return EXIT_SUCCESS; } else if (opts.versionRequested()) { printVersion(); return EXIT_SUCCESS; } else if (opts.backendListRequested()) { Backend::listBackends(); printFooter(); return EXIT_SUCCESS; } else if (opts.reportListRequested()) { Report::printReportListing(); printFooter(); return EXIT_SUCCESS; } else if (opts.repository().empty() || opts.report().empty()) { printHelp(opts); return EXIT_FAILURE; } // Setup backend Backend *backend; try { backend = Backend::backendFor(opts); if (backend == NULL) { std::cerr << "Error: No backend found for url: " << opts.repository() << std::endl; return EXIT_FAILURE; } } catch (const PepperException &ex) { std::cerr << "Error detecting repository type: " << ex.where() << ": " << ex.what() << std::endl; Logger::flush(); return EXIT_FAILURE; } SignalHandler sighandler; AbstractCache *cache = NULL; try { if (opts.useCache()) { backend->init(); #ifdef USE_LDBCACHE cache = new LdbCache(backend, opts); #else cache = new Cache(backend, opts); #endif sighandler.cache = cache; cache->init(); } else { backend->init(); } } catch (const PepperException &ex) { std::cerr << "Error initializing backend: " << ex.where() << ": " << ex.what() << std::endl; Logger::flush(); return EXIT_FAILURE; } int signums[] = {SIGINT, SIGTERM}; sys::sigblock::block(2, signums, &sighandler); sys::sigblock::ignore(SIGPIPE); int ret; try { Report r(opts.report(), (cache ? cache : backend)); ret = r.run(); } catch (const PepperException &ex) { std::cerr << "Recieved exception while running report:" << std::endl; std::cerr << " what(): " << ex.what() << std::endl; std::cerr << " where(): " << ex.where() << std::endl; std::cerr << " trace(): " << ex.trace() << std::endl; ret = EXIT_FAILURE; } catch (const std::exception &ex) { std::cerr << "Recieved exception while running report:" << std::endl; std::cerr << " what(): " << ex.what() << std::endl; ret = EXIT_FAILURE; } delete cache; // This will also flush the cache delete backend; return ret; } // Program entry point int main(int argc, char **argv) { #ifdef WIN32 // On windows, we need to change the mode for stdout to binary to avoid // newlines being automatically translated to CRLF. _setmode(_fileno(stdout), _O_BINARY); #endif // WIN32 #if defined(POS_LINUX) && defined(DEBUG) // Enable core dumps { struct rlimit rl; rl.rlim_cur = RLIM_INFINITY; rl.rlim_max = RLIM_INFINITY; setrlimit(RLIMIT_CORE, &rl); } #endif Options opts; try { opts.parse(argc, argv); } catch (const std::exception &ex) { std::cerr << "Error parsing arguments: " << ex.what() << std::endl; std::cerr << "Run with --help for usage information" << std::endl; return EXIT_FAILURE; } std::vector streams; setupLogger(&streams, opts); int ret; try { ret = start(opts); } catch (const PepperException &ex) { std::cerr << "Recieved unhandled exception:" << std::endl; std::cerr << " what(): " << ex.what() << std::endl; std::cerr << " where(): " << ex.where() << std::endl; std::cerr << " trace(): " << ex.trace() << std::endl; ret = EXIT_FAILURE; } catch (const std::exception &ex) { std::cerr << "Recieved unhandled exception:" << std::endl; std::cerr << " what(): " << ex.what() << std::endl; ret = EXIT_FAILURE; } Logger::flush(); // Close log files for (unsigned int i = 0; i < streams.size(); i++) { streams[i]->close(); delete streams[i]; } return ret; } pepper-0.3.2/src/luamodules.cpp0000644000175000001440000001415211721276751013405 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: luamodules.h * Extra C modules for the Lua API (implementation) */ #include "main.h" #include #include "cache.h" #include "luahelpers.h" #include "report.h" #include "repository.h" #include "strlib.h" #include "syslib/datetime.h" #include "syslib/fs.h" namespace LuaModules { // "Main" module namespace pepper { // Returns the current report context int current_report(lua_State *L) { if (Report::current() == NULL) { return LuaHelpers::pushNil(L); } return LuaHelpers::push(L, Report::current()); } // Runs another report int run(lua_State *L) { Report r(L); return r.run(L); } // Returns a list of all reachable reports int list_reports(lua_State *L) { // Return only the report paths std::vector > reports = Report::listReports(); std::vector paths(reports.size()); for (size_t i = 0; i < reports.size(); i++) { paths[i] = reports[i].first; } return LuaHelpers::push(L, paths); } // Returns the program version string int version(lua_State *L) { return LuaHelpers::push(L, PACKAGE_VERSION); } // Function table of main functions const struct luaL_reg table[] = { {"current_report", current_report}, {"run", run}, {"list_reports", list_reports}, {"version", version}, {NULL, NULL} }; } // namespace pepper // Utility functions namespace utils { // Custom fclose() handler for lua file handles int fclose(lua_State *L) { FILE **p = (FILE **)lua_touserdata(L, 1); int rc = fclose(*p); if (rc == 0) *p = NULL; return 1; } // Generates a temporary file, and returns a file handle as well as the file name int mkstemp(lua_State *L) { std::string templ; if (lua_gettop(L) > 0) { templ = LuaHelpers::pops(L); } FILE **pf = (FILE **)lua_newuserdata(L, sizeof *pf); *pf = 0; luaL_getmetatable(L, LUA_FILEHANDLE); lua_setmetatable(L, -2); // Register custom __close() function // (From lua posix module by Luiz Henrique de Figueiredo) lua_getfield(L, LUA_REGISTRYINDEX, "PEPPER_UTILS_FILE"); if (lua_isnil(L, -1)) { lua_pop(L, 1); lua_newtable(L); lua_pushvalue(L, -1); lua_pushcfunction(L, fclose); lua_setfield(L, -2, "__close"); lua_setfield(L, LUA_REGISTRYINDEX, "PEPPER_UTILS_FILE"); } lua_setfenv(L, -2); // Gemerate the file std::string filename; try { *pf = sys::fs::mkstemp(&filename, templ); } catch (const PepperException &ex) { return LuaHelpers::pushError(L, ex.what(), ex.where()); } LuaHelpers::push(L, filename); return 2; } // Removes a file int unlink(lua_State *L) { bool recurse = false; if (lua_gettop(L) > 1) { recurse = LuaHelpers::popb(L); } try { if (recurse) { sys::fs::unlink(LuaHelpers::tops(L).c_str()); } else { sys::fs::unlinkr(LuaHelpers::tops(L).c_str()); } } catch (const std::exception &ex) { return LuaHelpers::pushError(L, ex.what()); } return 0; } // Splits a string int split(lua_State *L) { std::string pattern = LuaHelpers::pops(L); std::string string = LuaHelpers::pops(L); return LuaHelpers::push(L, str::split(string, pattern)); } // Wrapper for strptime int strptime(lua_State *L) { std::string format = LuaHelpers::pops(L); std::string str = LuaHelpers::pops(L); int64_t time; try { time = sys::datetime::ptime(str, format); } catch (const std::exception &ex) { return LuaHelpers::pushError(L, ex.what()); } return LuaHelpers::push(L, time); } // Wrapper for dirname() int dirname(lua_State *L) { return LuaHelpers::push(L, sys::fs::dirname(LuaHelpers::pops(L))); } // Wrapper for basename() int basename(lua_State *L) { return LuaHelpers::push(L, sys::fs::basename(LuaHelpers::pops(L))); } // Function table of the utils library const struct luaL_reg table[] = { {"mkstemp", mkstemp}, {"unlink", unlink}, {"split", split}, {"strptime", strptime}, {"dirname", dirname}, {"basename", basename}, {NULL, NULL} }; } // namespace utils // Internal, undocumented functions namespace internal { // Runs a cache check for the given repository int check_cache(lua_State *L) { bool force = false; if (lua_gettop(L) != 1 && lua_gettop(L) != 2) { return LuaHelpers::pushError(L, "Invalid number of arguments (1 or 2 expected)"); } if (lua_gettop(L) > 1) { force = lua_type(L, 0) != LUA_TNIL; lua_pop(L, 1); } Repository *repo = LuaHelpers::popl(L); AbstractCache *cache = dynamic_cast(repo->backend()); if (cache == NULL) { return LuaHelpers::pushError(L, "No active cache found"); } try { cache->check(force); } catch (const PepperException &ex) { return LuaHelpers::pushError(L, str::printf("Error checking cache: %s: %s", ex.where(), ex.what())); } catch (const std::exception &ex) { return LuaHelpers::pushError(L, str::printf("Error checking cache: %s", ex.what())); } return LuaHelpers::pushNil(L); } // Lua wrapper for sys::datetime::Watch class Watch : public sys::datetime::Watch { public: Watch(lua_State *) : sys::datetime::Watch() { } int start(lua_State *) { sys::datetime::Watch::start(); return 0; } int elapsed(lua_State *L) { return LuaHelpers::push(L, sys::datetime::Watch::elapsed()); } int elapsedMSecs(lua_State *L) { return LuaHelpers::push(L, sys::datetime::Watch::elapsedMSecs()); } static const char className[]; static Lunar::RegType methods[]; }; const char Watch::className[] = "watch"; Lunar::RegType Watch::methods[] = { LUNAR_DECLARE_METHOD(Watch, start), LUNAR_DECLARE_METHOD(Watch, elapsed), LUNAR_DECLARE_METHOD(Watch, elapsedMSecs), {0,0} }; // Function table of internal functions const struct luaL_reg table[] = { {"check_cache", check_cache}, {NULL, NULL} }; } // namespace internal // Registers all modules in the given Lua context void registerModules(lua_State *L) { luaL_register(L, "pepper", pepper::table); luaL_register(L, "pepper.utils", utils::table); luaL_register(L, "pepper.internal", internal::table); Lunar::Register(L, "pepper"); } } // namespace LuaModules pepper-0.3.2/src/report.cpp0000644000175000001440000003716211724532647012556 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: report.cpp * Report script context */ #include "main.h" #include #include #include #include #include "backend.h" #include "diffstat.h" #include "logger.h" #include "luahelpers.h" #include "luamodules.h" #include "options.h" #include "repository.h" #include "revision.h" #include "revisioniterator.h" #include "strlib.h" #include "tag.h" #ifdef USE_GNUPLOT #include "plot.h" #endif #include "syslib/fs.h" #include "report.h" #define PEPPER_MAX_STACK_SIZE 64 /* * Stack helper functions */ namespace { // Report entry function names const char *funcs[] = {"run", "main", NULL}; // Checks if the given report is "executable", i.e. contains a main() function bool isExecutable(lua_State *L, std::string *entryPoint = NULL) { int i = 0; while (funcs[i] != NULL && !LuaHelpers::hasFunction(L, funcs[i])) { ++i; } if (funcs[i] != NULL && entryPoint != NULL) { *entryPoint = funcs[i]; } return funcs[i] != NULL; } // Prints a backtrace if Lua is panicking int atpanic(lua_State *L) { std::cerr << "Lua PANIC: " << LuaHelpers::tops(L) << std::endl; #ifdef DEBUG std::cerr << PepperException::stackTrace(); #endif return 0; } // Returns all paths that may contains reports std::vector reportDirs() { std::vector dirs; // Read the PEPPER_REPORTS environment variable char *env = getenv("PEPPER_REPORTS"); if (env != NULL) { #ifdef POS_WIN std::vector parts = str::split(env, ";"); #else std::vector parts = str::split(env, ":"); #endif for (size_t i = 0; i < parts.size(); i++) { dirs.push_back(parts[i]); } } #ifdef DATADIR dirs.push_back(DATADIR); #endif return dirs; } // Returns the full path to the given script std::string findScript(const std::string &script) { if (sys::fs::fileExists(script)) { return script; } if (sys::fs::fileExists(script + ".lua")) { return script + ".lua"; } std::vector dirs = reportDirs(); for (size_t i = 0; i < dirs.size(); i++) { std::string path = dirs[i] + "/" + script; if (sys::fs::fileExists(path)) { return path; } path += ".lua"; if (sys::fs::fileExists(path)) { return path; } } return script; } // Sets up the lua context lua_State *setupLua() { // Setup lua context lua_State *L = lua_open(); luaL_openlibs(L); lua_atpanic(L, atpanic); // Register extra modules functions LuaModules::registerModules(L); // Register binding classes Lunar::Register(L, "pepper"); Lunar::Register(L, "pepper"); Lunar::Register(L, "pepper"); Lunar::Register(L, "pepper"); Lunar::Register(L, "pepper"); Lunar::Register(L, "pepper"); #ifdef USE_GNUPLOT Lunar::Register(L, "pepper"); #endif // Setup package path to include built-in modules lua_getglobal(L, "package"); lua_getfield(L, -1, "path"); std::string path = LuaHelpers::pops(L); std::vector dirs = reportDirs(); for (size_t i = 0; i < dirs.size(); i++) { path += ";"; path += dirs[i] + "/?.lua"; } LuaHelpers::push(L, path); lua_setfield(L, -2, "path"); lua_pop(L, 1); PTRACE << "Lua package path has been set to " << path << endl; // Setup (deprecated) meta table luaL_newmetatable(L, "meta"); lua_setglobal(L, "meta"); return L; } // Opens a lua script and returns its entry point std::string loadReport(lua_State *L, const std::string &path) { // Check script syntax by loading the file if (luaL_loadfile(L, path.c_str()) != 0) { throw PEX(lua_tostring(L, -1)); } // Execute the main chunk if (lua_pcall(L, 0, LUA_MULTRET, 0) != 0) { throw PEX(lua_tostring(L, -1)); } std::string main; if (!isExecutable(L, &main)) { throw PEX("Not executable (no run function)"); } return main; } // Wrapper for print(), mostly from VIM - Vi IMproved by Bram Moolenaar int printWrapper(lua_State *L) { std::string str; int n = lua_gettop(L); lua_getglobal(L, "tostring"); for (int i = 1; i <= n; i++) { lua_pushvalue(L, -1); /* tostring */ lua_pushvalue(L, i); /* arg */ lua_call(L, 1, 1); size_t l; const char *s = lua_tolstring(L, -1, &l); if (s == NULL) { return luaL_error(L, "cannot convert to string"); } if (i > 1) { str += '\t'; } str += std::string(s, l); lua_pop(L, 1); } Report *c = Report::current(); (c == NULL ? std::cout : c->out()) << str << std::endl; return 0; } } // anonymous namespace /* * Report class implementation */ // Static report stack std::stack Report::s_stack; // Constructor Report::Report(const std::string &script, Backend *backend) : m_repo(NULL), m_script(script), m_out(&std::cout), m_metaDataRead(false) { if (backend) { m_repo = new Repository(backend); m_options = backend->options().reportOptions(); } } // Constructor Report::Report(const std::string &script, const std::map &options, Backend *backend) : m_repo(NULL), m_script(script), m_options(options), m_out(&std::cout), m_metaDataRead(false) { if (backend) { m_repo = new Repository(backend); } } // Destructor Report::~Report() { delete m_repo; } // Runs the report, printing error messages to the given stream int Report::run(std::ostream &out, std::ostream &err) { if (s_stack.size() == PEPPER_MAX_STACK_SIZE) { err << "Error running report: maximum stack size ("; err << PEPPER_MAX_STACK_SIZE << ") exceeded" << std::endl; return EXIT_FAILURE; } // Push this context on top of the stack std::string path = findScript(m_script); PDEBUG << "Report path resolved: " << m_script << " -> " << path << endl; PTRACE << "Pushing report context for " << path << endl; s_stack.push(this); PDEBUG << "Stack size is " << s_stack.size() << endl; std::ostream *prevout = m_out; m_out = &out; // Ensure the backend is ready m_repo->backend()->open(); lua_State *L = setupLua(); // Wrap print() function to use custom output stream lua_pushcfunction(L, printWrapper); lua_setglobal(L, "print"); // Run the script std::string main; int ret = EXIT_SUCCESS; try { main = loadReport(L, path); } catch (const std::exception &ex) { err << "Error opening report: " << ex.what() << std::endl; goto error; } // Call the report function, with the current report context as an argument lua_getglobal(L, main.c_str()); LuaHelpers::push(L, this); if (lua_pcall(L, 1, 1, 0) != 0) { err << "Error running report: " << lua_tostring(L, -1) << std::endl; goto error; } goto cleanup; error: ret = EXIT_FAILURE; cleanup: lua_gc(L, LUA_GCCOLLECT, 0); lua_close(L); // Inform backend that the report is done m_repo->backend()->close(); PTRACE << "Popping report context for " << path << endl; s_stack.pop(); m_out = prevout; return ret; } // Pretty-prints report options void Report::printHelp() { if (!m_metaDataRead) { readMetaData(); } std::cout << "Options for report '" << m_metaData.name << "':" << std::endl; for (size_t i = 0; i < m_metaData.options.size(); i++) { Options::print(m_metaData.options[i].synopsis, m_metaData.options[i].description); } } // Returns the report's meta data Report::MetaData Report::metaData() { if (!m_metaDataRead) { readMetaData(); } return m_metaData; } // Checks if the report is valid (e.g., executable) bool Report::valid() { std::string path = findScript(m_script); lua_State *L = setupLua(); bool valid = true; try { loadReport(L, path); } catch (const std::exception &ex) { PDEBUG << "Invalid report: " << ex.what() << endl; valid = false; } // Clean up lua_gc(L, LUA_GCCOLLECT, 0); lua_close(L); return valid; } // Returns the report output stream std::ostream &Report::out() const { return *m_out; } // Returns whether the standard output is redirected bool Report::outputRedirected() const { return (m_out != &std::cout); } // Lists all report scripts and their descriptions std::vector > Report::listReports() { std::vector > reports; std::vector dirs = reportDirs(); for (size_t j = 0; j < dirs.size(); j++) { std::string builtin = dirs[j]; if (!sys::fs::dirExists(builtin)) { continue; } std::vector contents = sys::fs::ls(builtin); std::sort(contents.begin(), contents.end()); for (size_t i = 0; i < contents.size(); i++) { if (contents[i].empty() || contents[i][contents[i].length()-1] == '~' || !sys::fs::fileExists(builtin + "/" + contents[i])) { continue; } std::string path = builtin + "/" + contents[i]; Report report(path); if (report.valid()) { reports.push_back(std::pair(path, report.metaData().description)); } } } return reports; } // Prints a listing of all report scripts to stdout void Report::printReportListing(std::ostream &out) { std::vector > reports = listReports(); std::string dirname; for (size_t i = 0; i < reports.size(); i++) { std::string nextdir = sys::fs::dirname(reports[i].first); if (dirname != nextdir) { dirname = nextdir; if (i < reports.size()-1) { out << std::endl; } out << "Available reports in " << dirname << ":" << std::endl; } std::string name = sys::fs::basename(reports[i].first); if (name.length() > 4 && name.compare(name.length()-4, 4, ".lua") == 0) { name = name.substr(0, name.length()-4); } Options::print(name, reports[i].second, out); } #ifndef USE_GNUPLOT out << std::endl << "NOTE: Built without Gnuplot support. "; out << "Graphical reports are not shown." << std::endl; #endif } // Returns the backend wrapper Repository *Report::repository() const { return m_repo; } // Returns the report options std::map Report::options() const { return m_options; } // Returns the current context Report *Report::current() { return (s_stack.empty() ? NULL : s_stack.top()); } // Reads the report script's meta data void Report::readMetaData() { lua_State *L = setupLua(); // Open the script std::string path = findScript(m_script); try { loadReport(L, path); } catch (const std::exception &ex) { throw PEX(str::printf("Error opening report: %s", ex.what())); } // Try to run describe() lua_getglobal(L, "describe"); if (lua_type(L, -1) == LUA_TFUNCTION) { LuaHelpers::push(L, this); if (lua_pcall(L, 1, 1, 0) != 0) { throw PEX(str::printf("Error opening report: %s", lua_tostring(L, -1))); } if (lua_type(L, -1) != LUA_TTABLE) { throw PEX("Error opening report: Expected table from describe()"); } } else { lua_pop(L, 1); // Else, use the meta table lua_getglobal(L, "meta"); if (lua_type(L, -1) != LUA_TTABLE) { throw PEX("Error opening report: Neither describe() nor meta-table found"); } } // Read the report name m_metaData.name = sys::fs::basename(m_script); lua_getfield(L, -1, "title"); if (lua_type(L, -1) == LUA_TSTRING) { m_metaData.name = LuaHelpers::tops(L); } else { lua_pop(L, 1); // "meta.name" has been used in the first version of the scripting tutorial lua_getfield(L, -1, "name"); if (lua_type(L, -1) == LUA_TSTRING) { m_metaData.name = LuaHelpers::tops(L); } } lua_pop(L, 1); // Read the report description lua_getfield(L, -1, "description"); if (lua_type(L, -1) == LUA_TSTRING) { m_metaData.description = LuaHelpers::tops(L); } lua_pop(L, 1); // Check for possible options lua_getfield(L, -1, "options"); if (lua_type(L, -1) == LUA_TTABLE) { lua_pushnil(L); while (lua_next(L, -2) != 0) { if (lua_type(L, -1) != LUA_TTABLE) { lua_pop(L, 1); continue; } int i = 0; std::string arg, text; lua_pushnil(L); while (lua_next(L, -2) != 0) { if (lua_type(L, -1) == LUA_TSTRING) { if (i == 0) { arg = luaL_checkstring(L, -1); } else if (i == 1) { text = luaL_checkstring(L, -1); } } ++i; lua_pop(L, 1); } lua_pop(L, 1); if (i >= 2) { m_metaData.options.push_back(MetaData::Option(arg, text)); } } } lua_pop(L, 1); // Clean up lua_gc(L, LUA_GCCOLLECT, 0); lua_close(L); m_metaDataRead = true; } /* * Lua binding */ const char Report::className[] = "report"; Lunar::RegType Report::methods[] = { LUNAR_DECLARE_METHOD(Report, getopt), LUNAR_DECLARE_METHOD(Report, repository), LUNAR_DECLARE_METHOD(Report, run), LUNAR_DECLARE_METHOD(Report, name), LUNAR_DECLARE_METHOD(Report, description), LUNAR_DECLARE_METHOD(Report, options), {0,0} }; Report::Report(lua_State *L) : m_repo(NULL), m_out(&std::cout), m_metaDataRead(false) { if (lua_gettop(L) != 1 && lua_gettop(L) != 2) { LuaHelpers::pushError(L, "Invalid number of arguments (1 or 2 expected)"); return; } if (!Report::current()) { LuaHelpers::pushError(L, "No current report"); return; } m_options = Report::current()->options(); if (lua_gettop(L) == 2) { m_options = LuaHelpers::popms(L); } m_script = LuaHelpers::pops(L); m_repo = new Repository(Report::current()->repository()->backend()); } int Report::repository(lua_State *L) { return LuaHelpers::push(L, m_repo); } int Report::getopt(lua_State *L) { if (lua_gettop(L) != 1 && lua_gettop(L) != 2) { return luaL_error(L, "Invalid number of arguments (1 or 2 expected)"); } bool defaultProvided = (lua_gettop(L) == 2); std::string defaultValue = (lua_gettop(L) == 2 ? LuaHelpers::pops(L) : std::string()); std::vector keys = str::split(LuaHelpers::pops(L), ",", true); std::string value; bool valueFound = false; for (unsigned int i = 0; i < keys.size(); i++) { if (m_options.find(keys[i]) != m_options.end()) { value = m_options.find(keys[i])->second; valueFound = true; break; } } if (valueFound) { return LuaHelpers::push(L, value); } else if (defaultProvided) { return LuaHelpers::push(L, defaultValue); } return LuaHelpers::pushNil(L); } int Report::run(lua_State *L) { try { std::stringstream out, err; if (run(out, err) != 0) { return LuaHelpers::pushError(L, str::trim(err.str())); } return LuaHelpers::push(L, out.str()); } catch (const PepperException &ex) { return LuaHelpers::pushError(L, ex.what(), ex.where()); } catch (const std::exception &ex) { return LuaHelpers::pushError(L, ex.what()); } return LuaHelpers::pushNil(L); } int Report::name(lua_State *L) { try { if (!m_metaDataRead) { readMetaData(); } return LuaHelpers::push(L, m_metaData.name); } catch (const PepperException &ex) { return LuaHelpers::pushError(L, ex.what(), ex.where()); } catch (const std::exception &ex) { return LuaHelpers::pushError(L, ex.what()); } return LuaHelpers::pushNil(L); } int Report::description(lua_State *L) { try { if (!m_metaDataRead) { readMetaData(); } return LuaHelpers::push(L, m_metaData.description); } catch (const PepperException &ex) { return LuaHelpers::pushError(L, ex.what(), ex.where()); } catch (const std::exception &ex) { return LuaHelpers::pushError(L, ex.what()); } return LuaHelpers::pushNil(L); } int Report::options(lua_State *L) { try { if (!m_metaDataRead) { readMetaData(); } // Convert each option into a 2-dimensional vector std::vector > opvec; for (size_t i = 0; i < m_metaData.options.size(); i++) { std::vector v; v.push_back(m_metaData.options[i].synopsis); v.push_back(m_metaData.options[i].description); opvec.push_back(v); } return LuaHelpers::push(L, opvec); } catch (const PepperException &ex) { return LuaHelpers::pushError(L, ex.what(), ex.where()); } catch (const std::exception &ex) { return LuaHelpers::pushError(L, ex.what()); } return LuaHelpers::pushNil(L); } pepper-0.3.2/src/luamodules.h0000644000175000001440000000100711721276751013045 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: luamodules.h * Extra C modules for the Lua API (interface) */ #ifndef LUAMODULES_H_ #define LUAMODULES_H_ struct lua_State *L; namespace LuaModules { void registerModules(lua_State *L); }; #endif // LUAMODULES_H_ pepper-0.3.2/src/options.h0000644000175000001440000000277611721276751012404 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: options.h * Command-line option parsing (interface) */ #ifndef OPTIONS_H_ #define OPTIONS_H_ #include "main.h" #include #include #include #include class Options { public: Options(); void parse(int argc, char **argv); bool helpRequested() const; bool versionRequested() const; bool backendListRequested() const; bool reportListRequested() const; bool useCache() const; std::string cacheDir() const; std::string forcedBackend() const; std::string repository() const; std::string value(const std::string &key, const std::string &defvalue = std::string()) const; std::map options() const; std::string report() const; std::map reportOptions() const; static void print(const std::string &option, const std::string &text, std::ostream &out = std::cout); static void printHelp(std::ostream &out = std::cout); private: void reset(); void parse(const std::vector &args); bool parseOpt(const std::string &arg, std::string *key, std::string *value); PEPPER_PVARS: std::map m_options; std::map m_reportOptions; }; #endif // OPTIONS_H_ pepper-0.3.2/src/repository.h0000644000175000001440000000212311721276751013112 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: repository.h * Repository interface (interface) */ #ifndef REPOSITORY_H_ #define REPOSITORY_H_ #include "lunar/lunar.h" class Backend; class RevisionIterator; class Repository { public: Repository(Backend *backend); ~Repository(); Backend *backend() const; private: Backend *m_backend; // Lua binding public: Repository(lua_State *L); int url(lua_State *L); int type(lua_State *L); int head(lua_State *L); int default_branch(lua_State *L); int branches(lua_State *L); int tags(lua_State *L); int tree(lua_State *L); int revision(lua_State *L); int iterator(lua_State *L); int cat(lua_State *L); // Compability methods int main_branch(lua_State *L); static const char className[]; static Lunar::RegType methods[]; }; #endif // REPOSITORY_H_ pepper-0.3.2/src/revision.h0000644000175000001440000000267211721276751012542 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: revision.h * Revision data class (interface) */ #ifndef REVISION_H_ #define REVISION_H_ #include #include "main.h" #include "diffstat.h" #include "lunar/lunar.h" class BIStream; class BOStream; class Revision { friend class Repository; friend class RevisionIterator; public: Revision(const std::string &id); Revision(const std::string &id, int64_t date, const std::string &author, const std::string &message, const Diffstat &diffstat); ~Revision(); std::string id() const; Diffstat diffstat() const; void write(BOStream &out) const; bool load(BIStream &in); void write03(BOStream &out) const; // for pepper <= 0.3 bool load03(BIStream &in); // for pepper <= 0.3 PEPPER_PVARS: std::string m_id; int64_t m_date; std::string m_author; std::string m_message; Diffstat m_diffstat; // Lua binding public: Revision(lua_State *L); int id(lua_State *L); int parent_id(lua_State *L); int date(lua_State *L); int author(lua_State *L); int message(lua_State *L); int diffstat(lua_State *L); static const char className[]; static Lunar::RegType methods[]; }; #endif // REVISION_H_ pepper-0.3.2/src/pex.h0000644000175000001440000000270011721276751011470 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: pex.h * Custom exception class and macros (interface) */ #ifndef PEX_H_ #define PEX_H_ #include #include #include class PepperException : public std::exception { public: PepperException(const std::string &str, const char *file = NULL, int line = 0, const std::string &trace = std::string()) throw(); PepperException(int code, const char *file = NULL, int line = 0, const std::string &trace = std::string()) throw(); virtual ~PepperException() throw(); virtual const char *what() const throw(); const char *where() const throw(); const char *trace() const throw(); static std::string stackTrace(); static std::string strerror(int code); private: std::string m_str; std::string m_trace; char m_where[512]; }; // Generates an exception with where() information #ifdef DEBUG #define PEX(str) PepperException(str, __FILE__, __LINE__, PepperException::stackTrace()) #define PEX_ERRNO() PepperException(errno, __FILE__, __LINE__, PepperException::stackTrace()) #else #define PEX(str) PepperException(str, __FILE__, __LINE__) #define PEX_ERRNO() PepperException(errno, __FILE__, __LINE__) #endif #endif // PEX_H_ pepper-0.3.2/src/revisioniterator.cpp0000644000175000001440000001277511721276751014654 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: revisioniterator.cpp * Base class for revision iterators * * The RevisionIterator is being used for walking through version history. * The main design idea is to make iteration as fast as possible, resulting * in two properties: * * * The revsions that are included in the iteration are determined in a * separate thread, concurrent to the backend data fetching operations. * * Backends can pre-fetch revision data, probably using multiple threads * * The backend provides a LogIterator thread which offers methods for retrieving * a bunch of revisions from the repository log at once. Once these revisions * are known, the backend can pre-fetch them if it needs to. Note that the * RevisionIterator calls the corresponding backend methods for pre-fetching. * This design isn't exceptionally beautiful but functional. */ #include "main.h" #include "logger.h" #include "luahelpers.h" #include "revision.h" #include "revisioniterator.h" // Constructor RevisionIterator::RevisionIterator(Backend *backend, const std::string &branch, int64_t start, int64_t end, Flags flags) : m_backend(backend), m_total(0), m_consumed(0), m_atEnd(false), m_flags(flags) { m_logIterator = backend->iterator(branch, start, end); m_logIterator->start(); } // Destructor RevisionIterator::~RevisionIterator() { m_logIterator->wait(); delete m_logIterator; } // Returns whether the iterator is at its end bool RevisionIterator::atEnd() { if (m_total == 0) { // Iteration has just started, and we don't know whether there are // any revisions at all fetchLogs(); } return m_queue.empty(); } // Returns the next revision std::string RevisionIterator::next() { // Call the function in order to retrieve the first revisions if necessary if (atEnd()) { return std::string(); } if (m_queue.size() <= 1) { fetchLogs(); if (m_queue.empty()) { return std::string(); } } ++m_consumed; std::string id = m_queue.front(); m_queue.pop(); return id; } // Returns a progress estimate (percejjjge) int RevisionIterator::progress() const { if (m_logIterator->running()) { return 0; } return int((100.0f * m_consumed) / m_total); } // Fetches new logs void RevisionIterator::fetchLogs() { // We need to ask the backend to prefetch the next revision, so // a temporary queue is used for fetching the next IDs std::queue tq; m_logIterator->nextIds(&tq); m_total += tq.size(); std::vector ids; while (!tq.empty()) { ids.push_back(tq.front()); m_queue.push(tq.front()); tq.pop(); } if (m_flags & PrefetchRevisions) { m_backend->prefetch(ids); } } /* * Lua binding */ const char RevisionIterator::className[] = "iterator"; Lunar::RegType RevisionIterator::methods[] = { LUNAR_DECLARE_METHOD(RevisionIterator, next), LUNAR_DECLARE_METHOD(RevisionIterator, revisions), LUNAR_DECLARE_METHOD(RevisionIterator, map), {0,0} }; RevisionIterator::RevisionIterator(lua_State *) { m_backend = NULL; m_logIterator = NULL; } int RevisionIterator::next(lua_State *L) { if (atEnd()) { return LuaHelpers::pushNil(L); } Revision *revision = NULL; try { revision = m_backend->revision(next()); m_backend->filterDiffstat(&(revision->m_diffstat)); } catch (const PepperException &ex) { return LuaHelpers::pushError(L, ex.what(), ex.where()); } PTRACE << "Fetched revision " << revision->id() << endl; return LuaHelpers::push(L, revision); } // Static function that will call the next() function from above. It is being // used as an iterator in RevisionIterator::revisions() static int callnext(lua_State *L) { RevisionIterator *it = LuaHelpers::topl(L, -2); return it->next(L); } int RevisionIterator::revisions(lua_State *L) { LuaHelpers::push(L, callnext); LuaHelpers::push(L, this); LuaHelpers::pushNil(L); return 3; } int RevisionIterator::map(lua_State *L) { if (m_backend == NULL) return LuaHelpers::pushNil(L); if (lua_gettop(L) != 1) { return luaL_error(L, "Invalid number of arguments (1 expected)"); } luaL_checktype(L, -1, LUA_TFUNCTION); int callback = luaL_ref(L, LUA_REGISTRYINDEX); lua_pop(L, 1); int progress = 0; if (Logger::level() < Logger::Info) { Logger::status() << "Fetching revisions... " << flush; } while (!atEnd()) { Revision *revision = NULL; try { revision = m_backend->revision(next()); m_backend->filterDiffstat(&(revision->m_diffstat)); } catch (const PepperException &ex) { return LuaHelpers::pushError(L, ex.what(), ex.where()); } PTRACE << "Fetched revision " << revision->id() << endl; lua_rawgeti(L, LUA_REGISTRYINDEX, callback); LuaHelpers::push(L, revision); lua_call(L, 1, 1); lua_pop(L, 1); if (Logger::level() > Logger::Info) { Logger::info() << "\r\033[0K"; Logger::info() << "Fetching revisions... " << revision->id() << flush; } else { if (progress != this->progress()) { progress = this->progress(); Logger::status() << "\r\033[0K"; Logger::status() << "Fetching revisions... " << progress << "%" << flush; } } delete revision; } Logger::status() << "\r\033[0K"; Logger::status() << "Fetching revisions... done" << endl; try { m_backend->finalize(); } catch (const PepperException &ex) { return LuaHelpers::pushError(L, ex.what(), ex.where()); } return 0; } pepper-0.3.2/src/gnuplot.h0000644000175000001440000000165111755247753012377 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: gnuplot.h * Bidirectional Gnuplot pipe (interface) */ #ifndef _GNUPLOT_H #define _GNUPLOT_H #include #include #include #include "syslib/io.h" class StreambufReader; class Gnuplot { public: Gnuplot(const char * const *args, std::ostream &out = std::cout); ~Gnuplot(); void cmd(const std::string &str); void cmd(const char *ptr, size_t len); inline Gnuplot& operator<<(const std::string &str) { cmd(str); return (*this); } private: sys::io::PopenStreambuf *m_buf; StreambufReader *m_reader; std::ostream *m_pipe; static std::string s_stdTerminal; }; #endif // _GNUPLOT_H pepper-0.3.2/src/Makefile.am0000644000175000001440000000476211721276751012571 00000000000000# # pepper - SCM statistics report generator # Copyright (C) 2010-2012 Jonas Gehring # # Released under the GNU General Public License, version 3. # Please see the COPYING file in the source distribution for license # terms and conditions, or see http://www.gnu.org/licenses/. # # Pack all code into a static library, so the unit test programs # can use it easily. noinst_LIBRARIES = libpepper.a bin_PROGRAMS = pepper libpepper_a_SOURCES = \ abstractcache.h abstractcache.cpp \ backend.h backend.cpp \ bstream.h bstream.cpp \ cache.h cache.cpp \ diffstat.h diffstat.cpp \ jobqueue.h \ logger.h logger.cpp \ luahelpers.h \ luamodules.h luamodules.cpp \ main.h \ options.h options.cpp \ pex.h pex.cpp \ report.h report.cpp \ repository.h repository.cpp \ revision.h revision.cpp \ revisioniterator.h revisioniterator.cpp \ strlib.h strlib.cpp \ tag.h tag.cpp \ utils.h utils.cpp \ \ syslib/fs.h syslib/fs.cpp \ syslib/io.h syslib/io.cpp \ syslib/parallel.h syslib/parallel.cpp \ syslib/sigblock.h syslib/sigblock.cpp \ syslib/datetime.h syslib/datetime.cpp \ \ 3rdparty/lunar/lunar.h # This is the main program pepper_SOURCES = \ main.cpp pepper_LDADD = \ libpepper.a AM_CXXFLAGS = \ -Wall -W -pipe \ $(PTHREAD_CFLAGS) INCLUDES = \ -I@top_srcdir@/src/3rdparty AM_CPPFLAGS = \ $(LUA_INCLUDE) \ -DDATADIR=\"$(pkgdatadir)\" AM_LDFLAGS = \ $(PTHREAD_LIBS) \ $(LUA_LIB) \ $(FRAMEWORKS) # Debugging? if DEBUG AM_CXXFLAGS += \ -rdynamic AM_LDFLAGS += \ -rdynamic endif # Optional features if GIT_BACKEND libpepper_a_SOURCES += \ backends/git.h backends/git.cpp AM_CPPFLAGS += \ -DUSE_GIT endif if MERCURIAL_BACKEND libpepper_a_SOURCES += \ backends/mercurial.h backends/mercurial.cpp AM_CPPFLAGS += \ -DUSE_MERCURIAL AM_CXXFLAGS += \ $(PYTHON_CPPFLAGS) AM_LDFLAGS += \ $(PYTHON_LDFLAGS) endif if SVN_BACKEND libpepper_a_SOURCES += \ backends/subversion.h backends/subversion.cpp \ backends/subversion_p.h \ backends/subversion_delta.cpp AM_CPPFLAGS += \ -DUSE_SUBVERSION $(APR_CPPFLAGS) $(APR_INCLUDES) AM_CXXFLAGS += \ -Wno-deprecated-declarations \ $(APR_CFLAGS) \ $(SVN_CFLAGS) AM_LDFLAGS += \ $(SVN_LDFLAGS) \ $(SVN_LIBS) \ $(APR_LIBS) endif if GNUPLOT libpepper_a_SOURCES += \ plot.h plot.cpp \ gnuplot.h gnuplot.cpp AM_CPPFLAGS += \ -DUSE_GNUPLOT endif if LEVELDB libpepper_a_SOURCES += \ ldbcache.h ldbcache.cpp AM_CXXFLAGS += \ $(LEVELDB_CPPFLAGS) LIBS += \ $(LEVELDB_LIBS) AM_CPPFLAGS += \ -DUSE_LDBCACHE endif # Last but not least, the CFLAGS AM_CFLAGS = $(AM_CXXFLAGS) pepper-0.3.2/src/ldbcache.h0000644000175000001440000000163311721276751012425 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: ldbcache.h * Revision cache using Leveldb (interface) */ #ifndef LDBCACHE_H_ #define LDBCACHE_H_ #include "abstractcache.h" class Cache; namespace leveldb { class DB; } class LdbCache : public AbstractCache { public: LdbCache(Backend *backend, const Options &options); ~LdbCache(); void flush(); void check(bool force = false); protected: bool lookup(const std::string &id); void put(const std::string &id, const Revision &rev); Revision *get(const std::string &id); private: void opendb(); void closedb(); void import(Cache *cache); private: leveldb::DB *m_db; }; #endif // CACHE_H_ pepper-0.3.2/src/diffstat.h0000644000175000001440000000316111721276751012502 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: diffstat.h * Diffstat object and parser (interface) */ #ifndef DIFFSTAT_H_ #define DIFFSTAT_H_ #include #include #include #include "main.h" #include "lunar/lunar.h" #include "syslib/parallel.h" class BIStream; class BOStream; class Diffstat { friend class DiffParser; public: struct Stat { uint64_t cadd, ladd; uint64_t cdel, ldel; Stat() : cadd(0), ladd(0), cdel(0), ldel(0) { } inline bool empty() const { return (cadd == 0 && ladd == 0 && cdel == 0 && ldel == 0); } }; public: Diffstat(); ~Diffstat(); std::map stats() const; void filter(const std::string &prefix); void write(BOStream &out) const; bool load(BIStream &in); PEPPER_PVARS: std::map m_stats; // Lua binding public: Diffstat(lua_State *L); int files(lua_State *L); int lines_added(lua_State *L); int bytes_added(lua_State *L); int lines_removed(lua_State *L); int bytes_removed(lua_State *L); static const char className[]; static Lunar::RegType methods[]; }; class DiffParser : public sys::parallel::Thread { public: DiffParser(std::istream &in); Diffstat stat() const; static Diffstat parse(std::istream &in); protected: void run(); private: std::istream &m_in; Diffstat m_stat; }; #endif // DIFFSTAT_H_ pepper-0.3.2/src/bstream.h0000644000175000001440000001374011721276751012337 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: bstream.h * Binary input and output streams (interfaces) */ #ifndef BSTREAM_H_ #define BSTREAM_H_ #include #include #include #include #include "main.h" // read()/write() macros with assertions #if defined(NDEBUG) || 1 // Don't use assertions, since realiable EOF testing requires // stream operators to fail silently. #define ASSERT_READ(p, n) do { read(p, n); } while (0) #define ASSERT_WRITE(p, n) do { write(p, n); } while (0) #else #define ASSERT_READ(p, n) do { assert(read(p, n) == (ssize_t)n); } while (0) #define ASSERT_WRITE(p, n) do { assert(write(p, n) == (ssize_t)n); } while (0) #endif // Base class for all streams class BStream { public: // Abstract base class for raw streams class RawStream { public: RawStream() { } virtual ~RawStream() { } virtual bool ok() const = 0; virtual bool eof() const = 0; virtual size_t tell() const = 0; virtual bool seek(size_t offset) = 0; virtual ssize_t read(void *ptr, size_t n) = 0; virtual ssize_t write(const void *ptr, size_t n) = 0; }; BStream(RawStream *stream) : m_stream(stream) { } virtual ~BStream() { delete m_stream; } inline bool ok() const { return m_stream != NULL && m_stream->ok(); } inline bool eof() const { return m_stream == NULL || m_stream->eof(); } inline size_t tell() const { return (m_stream ? m_stream->tell() : 0); } inline bool seek(size_t offset) { return (m_stream ? m_stream->seek(offset) : false); } inline ssize_t read(void *ptr, size_t n) { return (m_stream ? m_stream->read(ptr, n) : 0); } inline ssize_t write(const void *ptr, size_t n) { return (m_stream ? m_stream->write(ptr, n) : 0); } // Byte swapping (from Qt) static inline uint32_t bswap(uint32_t source) { return 0 | ((source & 0x000000ff) << 24) | ((source & 0x0000ff00) << 8) | ((source & 0x00ff0000) >> 8) | ((source & 0xff000000) >> 24); } static inline uint64_t bswap(uint64_t source) { char *t = (char *)&source; std::swap(t[0], t[7]), std::swap(t[1], t[6]); std::swap(t[2], t[5]), std::swap(t[3], t[4]); return source; } protected: RawStream *m_stream; }; // Input stream class BIStream : public BStream { public: BIStream(const std::string &path); BIStream(FILE *f); BIStream &operator>>(char &c); BIStream &operator>>(uint32_t &i); BIStream &operator>>(uint64_t &i); inline BIStream &operator>>(int64_t &i) { return (*this >> reinterpret_cast(i)); } BIStream &operator>>(std::string &s); BIStream &operator>>(std::vector &v); template BIStream &operator>>(std::vector &v) { uint32_t size; (*this) >> size; v.resize(size); for (uint32_t i = 0; i < size && !eof(); i++) { (*this) >> v[i]; } return *this; } protected: BIStream(RawStream *stream); }; // Output stream class BOStream : public BStream { public: BOStream(const std::string &path, bool append = false); BOStream(FILE *f); BOStream &operator<<(char c); BOStream &operator<<(uint32_t i); BOStream &operator<<(uint64_t i); inline BOStream &operator<<(int64_t i) { return (*this << static_cast(i)); } BOStream &operator<<(const std::string &s); BOStream &operator<<(const std::vector &v); template BOStream &operator<<(const std::vector &v) { (*this) << (uint32_t)v.size(); for (uint32_t i = 0; i < v.size(); i++) { (*this) << v[i]; } return *this; } protected: BOStream(RawStream *stream); }; // Memory input stream class MIStream : public BIStream { public: MIStream(const char *data, size_t n); MIStream(const std::vector &data); }; // Memory output stream class MOStream : public BOStream { public: MOStream(); std::vector data() const; }; // Compressed input stream class GZIStream : public BIStream { public: GZIStream(const std::string &path); }; // Compressed output stream class GZOStream : public BOStream { public: GZOStream(const std::string &path, bool append = false); }; // Inlined functions inline BOStream &BOStream::operator<<(char c) { ASSERT_WRITE((char *)&c, 1); return *this; } inline BOStream &BOStream::operator<<(uint32_t i) { #ifndef WORDS_BIGENDIAN i = bswap(i); #endif ASSERT_WRITE((char *)&i, 4); return *this; } inline BOStream &BOStream::operator<<(uint64_t i) { #ifndef WORDS_BIGENDIAN i = bswap(i); #endif ASSERT_WRITE((char *)&i, 8); return *this; } inline BOStream &BOStream::operator<<(const std::string &s) { ASSERT_WRITE(s.data(), s.length()); return (*this << '\0'); } inline BOStream &BOStream::operator<<(const std::vector &v) { (*this) << (uint32_t)v.size(); ASSERT_WRITE(&(v[0]), v.size()); return *this; } inline BIStream &BIStream::operator>>(char &c) { ASSERT_READ((char *)&c, 1); return *this; } inline BIStream &BIStream::operator>>(uint32_t &i) { ASSERT_READ((char *)&i, 4); #ifndef WORDS_BIGENDIAN i = bswap(i); #endif return *this; } inline BIStream &BIStream::operator>>(uint64_t &i) { ASSERT_READ((char *)&i, 8); #ifndef WORDS_BIGENDIAN i = bswap(i); #endif return *this; } inline BIStream &BIStream::operator>>(std::string &s) { char buffer[120], *bptr = buffer; char c; s.clear(); do { if (eof()) { s.clear(); return *this; } (*this) >> c; switch (c) { case 0: break; default: { *bptr = c; if (bptr-buffer == sizeof(buffer)-1) { s.append(buffer, sizeof(buffer)); bptr = buffer; } else { ++bptr; } continue; } } break; } while (true); if (bptr != buffer) { s.append(buffer, bptr-buffer); } return *this; } inline BIStream &BIStream::operator>>(std::vector &v) { uint32_t size; *this >> size; v.resize(size); ASSERT_READ(&v[0], size); return *this; } #endif // BSTREAM_H_ pepper-0.3.2/src/bstream.cpp0000644000175000001440000001177511721276751012700 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: bstream.cpp * Binary input and output streams (implementations) */ #include "main.h" #include #ifdef HAVE_LIBZ #include #endif #include "bstream.h" // RawStream implementation for cstdio class FileStream : public BStream::RawStream { public: FileStream(FILE *f) : f(f) { } ~FileStream() { if (f) fclose(f); } bool ok() const { return !(f == NULL || ferror(f) != 0); } bool eof() const { return feof(f); } size_t tell() const { return ftell(f); } bool seek(size_t offset) { return (fseek(f, offset, SEEK_SET) >= 0); } ssize_t read(void *ptr, size_t n) { if (feof(f)) { return 0; } return fread(ptr, 1, n, f); } ssize_t write(const void *ptr, size_t n) { return fwrite(ptr, 1, n, f); } FILE *f; }; // RawStream implementation for memory buffers class MemoryStream : public BStream::RawStream { public: MemoryStream() : p(0), size(0), asize(512) { m_buffer = new char[asize]; } MemoryStream(const char *data, size_t n) : p(0), size(n) { m_buffer = new char[n]; memcpy(m_buffer, data, n); } ~MemoryStream() { delete[] m_buffer;} bool ok() const { return true; } bool eof() const { return !(p < size); } size_t tell() const { return p; } bool seek(size_t offset) { if (offset > size) { return false; } p = offset; return true; } ssize_t read(void *ptr, size_t n) { if (p == size) { return 0; } size_t nr = std::min(size - p, n); memcpy(ptr, m_buffer + p, nr); p += nr; return nr; } ssize_t write(const void *ptr, size_t n) { if (p + n >= asize) { size_t oldsize = size; do { asize *= 2; } while (p + n >= asize); char *newbuf = new char[asize]; memcpy(newbuf, m_buffer, oldsize); delete[] m_buffer; m_buffer = newbuf; } memcpy(m_buffer + p, ptr, n); p += n; size += n; return n; } char *m_buffer; size_t p, size, asize; }; #ifdef HAVE_LIBZ // RawStream implementation for zlib class GzStream : public BStream::RawStream { public: GzStream(gzFile f) : f(f) { } ~GzStream() { if (f) gzclose(f); } bool ok() const { if (f == NULL) { return false; } int err; gzerror(f, &err); return (err == 0); } bool eof() const { return gzeof(f); } size_t tell() const { return gztell(f); } bool seek(size_t offset) { return (gzseek(f, offset, SEEK_SET) >= 0); } ssize_t read(void *ptr, size_t n) { return gzread(f, ptr, n); } ssize_t write(const void *ptr, size_t n) { return gzwrite(f, ptr, n); } gzFile f; }; #endif // HAVE_LIBZ // Constructors BIStream::BIStream(const std::string &path) #if (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3) : BStream(new FileStream(fopen(path.c_str(), "rbm"))) #else : BStream(new FileStream(fopen(path.c_str(), "rb"))) #endif { } BIStream::BIStream(FILE *f) : BStream(new FileStream(f)) { } BIStream::BIStream(RawStream *stream) : BStream(stream) { } BOStream::BOStream(const std::string &path, bool append) : BStream(new FileStream(fopen(path.c_str(), (append ? "ab" : "wb")))) { } BOStream::BOStream(RawStream *stream) : BStream(stream) { } MIStream::MIStream(const char *data, size_t n) : BIStream(new MemoryStream(data, n)) { } MIStream::MIStream(const std::vector &data) : BIStream(new MemoryStream(&data[0], data.size())) { } MOStream::MOStream() : BOStream(new MemoryStream()) { } // Returns the stream's internal buffer std::vector MOStream::data() const { MemoryStream *ms = (MemoryStream *)m_stream; return std::vector(ms->m_buffer, ms->m_buffer + ms->size); } // Constructors GZIStream::GZIStream(const std::string &path) #ifdef HAVE_LIBZ : BIStream(new GzStream(gzopen(path.c_str(), "rb"))) #else : BIStream(path) #endif { } #ifdef HAVE_LIBZ // Single-linked buffer struct LBuffer { char *data; int size; LBuffer *next; LBuffer() : data(NULL), size(0), next(NULL) { } ~LBuffer() { delete[] data; } }; #endif // HAVE_LIBZ // Constructor GZOStream::GZOStream(const std::string &path, bool append) #ifdef HAVE_LIBZ : BOStream(append ? NULL : new GzStream(gzopen(path.c_str(), "wb"))) #else : BOStream(path, append) #endif { #ifdef HAVE_LIBZ // Appending to gzipped files does not work. Instead, read the whole // file and re-write it. if (append) { LBuffer *buf = NULL, *first = NULL; gzFile in = gzopen(path.c_str(), "rb"); if (in) { while (!gzeof(in)) { if (buf) { buf->next = new LBuffer(); buf = buf->next; } else { buf = new LBuffer(); first = buf; } buf->data = new char[4096]; buf->size = gzread(in, buf->data, 4096); } gzclose(in); } gzFile gz = gzopen(path.c_str(), "wb"); buf = first; while (buf != NULL) { gzwrite(gz, buf->data, buf->size); LBuffer *tmp = buf; buf = buf->next; delete tmp; } m_stream = new GzStream(gz); } #endif // HAVE_LIBZ } pepper-0.3.2/src/luahelpers.h0000644000175000001440000001636511721276751013054 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: luahelpersq.h * Helper functions for interacting with the Lua API */ #ifndef LUAHELPERS_H_ #define LUAHELPERS_H_ #include "main.h" #include #include #include #include #include "strlib.h" #include "lunar/lunar.h" namespace LuaHelpers { inline void stackdump(lua_State *L, std::ostream &out = std::cout) { int top = lua_gettop(L); out << "Stack size: " << top << std::endl; for (int i = top; i >= 1; i--) { out << " -" << (top-i+1) << " "; int t = lua_type(L, i); switch (t) { case LUA_TSTRING: out << "string: '" << lua_tostring(L, i) << "'"; break; case LUA_TBOOLEAN: out << "boolean: " << (lua_toboolean(L, i) ? "true" : "false"); break; case LUA_TNUMBER: out << "number: " << lua_tonumber(L, i); break; default: out << lua_typename(L, t); break; } out << std::endl; } } inline int push(lua_State *L, int i) { lua_pushinteger(L, i); return 1; } inline int push(lua_State *L, int64_t i) { lua_pushinteger(L, i); return 1; } inline int push(lua_State *L, uint64_t i) { lua_pushnumber(L, i); return 1; } inline int push(lua_State *L, double i) { lua_pushnumber(L, i); return 1; } inline int push(lua_State *L, bool i) { lua_pushboolean(L, (int)i); return 1; } inline int push(lua_State *L, const char *s) { lua_pushstring(L, s); return 1; } inline int push(lua_State *L, const std::string &s) { lua_pushlstring(L, s.c_str(), s.length()); return 1; } inline int push(lua_State *L, int (*f)(lua_State *L)) { lua_pushcfunction(L, f); return 1; } template inline int push(lua_State *L, const std::vector &v) { lua_createtable(L, v.size(), 0); int table = lua_gettop(L); int i = 1; typename std::vector::const_iterator it = v.begin(); while (it != v.end()) { push(L, *it); lua_rawseti(L, table, i++); ++it; } return 1; } template inline int push(lua_State *L, const std::map &m) { lua_createtable(L, m.size(), 0); int table = lua_gettop(L); typename std::map::const_iterator it = m.begin(); while (it != m.end()) { push(L, it->first); push(L, it->second); lua_settable(L, table); ++it; } return 1; } template inline int push(lua_State *L, T *i, bool gc = false) { Lunar::push(L, i, gc); return 1; } inline int pushNil(lua_State *L) { lua_pushnil(L); return 0; } inline int pushError(lua_State *L, const std::string &e) { // Make sure that the string is passed properly return luaL_error(L, "%s", e.c_str()); } inline int pushError(lua_State *L, const char *what, const char *where) { std::string s(where); s += ": "; s += what; return pushError(L, s); } inline int topi(lua_State *L, int index = -1) { return luaL_checkinteger(L, index); } inline bool topb(lua_State *L, int index = -1) { luaL_checktype(L, index, LUA_TBOOLEAN); return (bool)lua_toboolean(L, index); } inline double topd(lua_State *L, int index = -1) { return luaL_checknumber(L, index); } inline std::string tops(lua_State *L, int index = -1) { return luaL_checkstring(L, index); } template inline T *topl(lua_State *L, int index = -1) { return Lunar::check(L, index); } inline int popi(lua_State *L) { int i = topi(L, -1); lua_pop(L, 1); return i; } inline bool popb(lua_State *L) { bool b = topb(L, -1); lua_pop(L, 1); return b; } inline double popd(lua_State *L) { double d = topd(L, -1); lua_pop(L, 1); return d; } inline std::string pops(lua_State *L) { std::string s = tops(L, -1); lua_pop(L, 1); return s; } template inline T* popl(lua_State *L) { T *t = topl(L); lua_pop(L, 1); return t; } inline std::vector topvd(lua_State *L, int index = -1) { std::vector t; luaL_checktype(L, index, LUA_TTABLE); lua_pushvalue(L, index); lua_pushnil(L); while (lua_next(L, -2) != 0) { t.push_back(popd(L)); } lua_pop(L, 1); return t; } inline std::vector topvs(lua_State *L, int index = -1) { std::vector t; luaL_checktype(L, index, LUA_TTABLE); lua_pushvalue(L, index); lua_pushnil(L); while (lua_next(L, -2) != 0) { t.push_back(pops(L)); } lua_pop(L, 1); return t; } inline std::map topms(lua_State *L, int index = -1) { std::map t; luaL_checktype(L, index, LUA_TTABLE); lua_pushvalue(L, index); lua_pushnil(L); while (lua_next(L, -2) != 0) { t[tops(L, -2)] = tops(L, -1); lua_pop(L, 1); } lua_pop(L, 1); return t; } inline std::vector popvd(lua_State *L) { std::vector t = topvd(L, -1); lua_pop(L, 1); return t; } inline std::vector popvs(lua_State *L) { std::vector t = topvs(L, -1); lua_pop(L, 1); return t; } inline std::map popms(lua_State *L) { std::map t = topms(L, -1); lua_pop(L, 1); return t; } template inline void call(lua_State *L, const T1 &arg1, int nresults) { push(L, arg1); lua_call(L, 1, nresults); } template inline void call(lua_State *L, const T1 &arg1, const T2 &arg2, int nresults) { push(L, arg1); push(L, arg2); lua_call(L, 2, nresults); } template inline void call(lua_State *L, const T1 &arg1, const T2 &arg2, const T3 &arg3, int nresults) { push(L, arg1); push(L, arg2); push(L, arg3); lua_call(L, 3, nresults); } template inline void calls(lua_State *L, const std::string &name, const T1 &arg1, const T2 &arg2) { // Find function std::vector parts = str::split(name, "."); lua_getglobal(L, parts[0].c_str()); if (parts.size() > 1) { for (size_t i = 1; i < parts.size(); i++) { if (lua_type(L, -1) != LUA_TTABLE) { throw PEX("No such module"); } lua_getfield(L, -1, parts[i].c_str()); lua_remove(L, -2); } } if (lua_type(L, -1) != LUA_TFUNCTION) { throw PEX("No such function"); } push(L, arg1); push(L, arg2); lua_call(L, 2, 1); } inline int tablevi(lua_State *L, const std::string &key, int def = -1, int index = -1) { luaL_checktype(L, index, LUA_TTABLE); push(L, key); lua_gettable(L, index-1); if (lua_isnil(L, -1)) { lua_pop(L, 1); return def; } return popi(L); } inline bool tablevb(lua_State *L, const std::string &key, bool def = false, int index = -1) { luaL_checktype(L, index, LUA_TTABLE); push(L, key); lua_gettable(L, index-1); if (lua_isnil(L, -1)) { lua_pop(L, 1); return def; } return popb(L); } inline std::string tablevb(lua_State *L, const std::string &key, const std::string &def = std::string(), int index = -1) { luaL_checktype(L, index, LUA_TTABLE); push(L, key); lua_gettable(L, index-1); if (lua_isnil(L, -1)) { lua_pop(L, 1); return def; } return pops(L); } inline size_t tablesize(lua_State *L, int index = -1) { return lua_objlen(L, index); } inline bool hasFunction(lua_State *L, const std::string &name) { lua_getglobal(L, name.c_str()); bool has = (lua_type(L, -1) == LUA_TFUNCTION); lua_pop(L, 1); return has; } } // namespace LuaHelpers #endif // LUAHELPERS_H_ pepper-0.3.2/src/cache.cpp0000644000175000001440000002516611721276751012305 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: cache.cpp * Revision cache with custom binary format */ #include "main.h" #include #include #include #include "bstream.h" #include "logger.h" #include "options.h" #include "revision.h" #include "strlib.h" #include "utils.h" #include "syslib/datetime.h" #include "syslib/fs.h" #include "syslib/sigblock.h" #include "cache.h" #define CACHE_VERSION (uint32_t)5 #define MAX_CACHEFILE_SIZE 4194304 // Constructor Cache::Cache(Backend *backend, const Options &options) : AbstractCache(backend, options), m_iout(NULL), m_cout(NULL), m_cin(0), m_coindex(0), m_ciindex(0), m_loaded(false), m_lock(-1) { } // Destructor Cache::~Cache() { flush(); unlock(); } // Flushes and closes the cache streams void Cache::flush() { PTRACE << "Flushing cache..." << endl; delete m_iout; m_iout = NULL; delete m_cout; m_cout = NULL; delete m_cin; m_cin = NULL; PTRACE << "Cache flushed" << endl; } // Checks if the diffstat of the given revision is already cached bool Cache::lookup(const std::string &id) { if (!m_loaded) { load(); } return (m_index.find(id) != m_index.end()); } // Adds the revision to the cache void Cache::put(const std::string &id, const Revision &rev) { if (!m_loaded) { load(); } // Defer any signals while writing to the cache SIGBLOCK_DEFER(); // Add revision to cache std::string dir = m_opts.cacheDir() + "/" + uuid(), path; if (m_cout == NULL) { m_coindex = 0; do { path = str::printf("%s/cache.%u", dir.c_str(), m_coindex); if (!sys::fs::fileExists(path) || sys::fs::filesize(path) < MAX_CACHEFILE_SIZE) { break; } ++m_coindex; } while (true); delete m_cout; m_cout = new BOStream(path, true); } else if (m_cout->tell() >= MAX_CACHEFILE_SIZE) { delete m_cout; path = str::printf("%s/cache.%u", dir.c_str(), ++m_coindex); m_cout = new BOStream(path, true); } uint32_t offset = m_cout->tell(); MOStream rout; rev.write03(rout); std::vector compressed = utils::compress(rout.data()); *m_cout << compressed; // Add revision to index if (m_iout == NULL) { if (sys::fs::exists(dir + "/index")) { m_iout = new GZOStream(dir + "/index", true); } else { m_iout = new GZOStream(dir + "/index", false); // Version number *m_iout << CACHE_VERSION; } } *m_iout << id; *m_iout << m_coindex << offset << utils::crc32(compressed); // Update cached index m_index[id] = std::pair(m_coindex, offset); } // Loads a revision from the cache Revision *Cache::get(const std::string &id) { if (!m_loaded) { load(); } std::string dir = cacheDir(); std::pair offset = m_index[id]; std::string path = str::printf("%s/cache.%u", dir.c_str(), offset.first); if (m_cin == NULL || offset.first != m_ciindex) { delete m_cin; m_cin = new BIStream(path); m_ciindex = offset.first; if (!m_cin->ok()) { throw PEX(str::printf("Unable to read from cache file: %s", path.c_str())); } } if (!m_cin->seek(offset.second)) { throw PEX(str::printf("Unable to read from cache file: %s", path.c_str())); } Revision *rev = new Revision(id); std::vector data; *m_cin >> data; data = utils::uncompress(data); if (data.empty()) { throw PEX(str::printf("Unable to read from cache file: %s", path.c_str())); } MIStream rin(data); if (!rev->load03(rin)) { throw PEX(str::printf("Unable to read from cache file: %s", path.c_str())); } return rev; } // Loads the index file void Cache::load() { std::string path = cacheDir(); PDEBUG << "Using cache dir: " << path << endl; m_index.clear(); m_loaded = true; bool created; checkDir(path, &created); lock(); if (created) { return; } // For git repositories, the hardest part is calling uuid() sys::datetime::Watch watch; GZIStream *in = new GZIStream(path+"/index"); if (!in->ok()) { Logger::info() << "Cache: Empty cache for '" << uuid() << '\'' << endl; return; } uint32_t version; *in >> version; switch (checkVersion(version)) { case OutOfDate: delete in; throw PEX(str::printf("Cache is out of date - please run the check_cache report", version)); case UnknownVersion: delete in; throw PEX(str::printf("Unknown cache version number %u - please run the check_cache report", version)); default: break; } Logger::status() << "Loading cache index... " << ::flush; std::string buffer; std::pair pos; uint32_t crc; while (!(*in >> buffer).eof()) { if (buffer.empty()) { break; } *in >> pos.first >> pos.second; *in >> crc; m_index[buffer] = pos; } Logger::status() << "done" << endl; delete in; Logger::info() << "Cache: Loaded " << m_index.size() << " revisions in " << watch.elapsedMSecs() << " ms" << endl; } // Clears all cache files void Cache::clear() { flush(); std::string path = cacheDir(); if (!sys::fs::dirExists(path)) { return; } PDEBUG << "Clearing cache in dir: " << path << endl; std::vector files = sys::fs::ls(path); for (size_t i = 0; i < files.size(); i++) { std::string fullpath = path + "/" + files[i]; PDEBUG << "Unlinking " << fullpath << endl; sys::fs::unlink(fullpath); } } // Locks the cache directory for this process void Cache::lock() { if (m_lock >= 0) { PDEBUG << "Already locked (" << m_lock << ")" << endl; return; } std::string path = cacheDir(); std::string lock = path + "/lock"; m_lock = ::open(lock.c_str(), O_WRONLY | O_CREAT, S_IWUSR); if (m_lock == -1) { throw PEX(str::printf("Unable to lock cache %s: %s", path.c_str(), PepperException::strerror(errno).c_str())); } PTRACE << "Locking file " << lock << endl; struct flock flck; memset(&flck, 0x00, sizeof(struct flock)); flck.l_type = F_WRLCK; if (fcntl(m_lock, F_SETLK, &flck) == -1) { throw PEX(str::printf("Unable to lock cache %s, it may be used by another instance", path.c_str())); } } // Unlocks the cache directory void Cache::unlock() { if (m_lock < 0) { PDEBUG << "Not locked yet (" << m_lock << ")" << endl; return; } std::string path = cacheDir(); PTRACE << "Unlocking file " << path + "/lock" << endl; struct flock flck; memset(&flck, 0x00, sizeof(struct flock)); flck.l_type = F_UNLCK; if (fcntl(m_lock, F_SETLK, &flck) == -1) { throw PEX(str::printf("Unable to unlock cache, please delete %s/lock manually if required", path.c_str())); } if (::close(m_lock) == -1) { throw PEX_ERRNO(); } } // Checks the cache version Cache::VersionCheckResult Cache::checkVersion(int version) { if (version <= 0) { return UnknownVersion; } if (version <= 1) { // The diffstats for Mercurial and Git have been flawed in version 1. // The Subversion backend uses repository-wide diffstats now. return OutOfDate; } if (version <= 2 && m_backend->name() == "subversion") { // Invalid diffstats for deleted files in version 2 (Subversion backend) return OutOfDate; } if (version <= 4 && m_backend->name() == "git") { // Invalid commit times in version 3 (Git backend) return OutOfDate; } if ((uint32_t)version <= CACHE_VERSION) { return Ok; } return UnknownVersion; } // Checks cache entries and removes invalid ones from the index file void Cache::check(bool force) { std::map > index; std::string path = cacheDir(); PDEBUG << "Checking cache in dir: " << path << endl; bool created; checkDir(path, &created); if (created) { Logger::info() << "Cache: Created empty cache for '" << uuid() << '\'' << endl; return; } sys::datetime::Watch watch; GZIStream *in = new GZIStream(path+"/index"); if (!in->ok()) { Logger::info() << "Cache: Empty cache for '" << uuid() << '\'' << endl; delete in; return; } BIStream *cache_in = NULL; uint32_t cache_index = -1; uint32_t version; *in >> version; switch (checkVersion(version)) { case OutOfDate: delete in; Logger::warn() << "Cache: Cache is out of date"; if (!force) { Logger::warn() << " - won't clear it until forced to do so" << endl; } else { Logger::warn() << ", clearing" << endl; clear(); } return; case UnknownVersion: delete in; Logger::warn() << "Cache: Unkown cache version number " << version; if (!force) { Logger::warn() << " - won't clear it until forced to do so" << endl; } else { Logger::warn() << ", clearing" << endl; clear(); } return; default: break; } Logger::status() << "Checking all indexed revisions... " << ::flush; std::string id; std::pair pos; uint32_t crc; std::map crcs; std::vector corrupted; while (!(*in >> id).eof()) { if (!in->ok()) { goto corrupt; } *in >> pos.first >> pos.second; *in >> crc; if (!in->ok()) { goto corrupt; } index[id] = pos; crcs[id] = crc; if (cache_in == NULL || cache_index != pos.first) { delete cache_in; cache_in = new BIStream(str::printf("%s/cache.%u", path.c_str(), pos.first)); cache_index = pos.first; if (!cache_in->ok()) { delete cache_in; cache_in = NULL; goto corrupt; } } if (cache_in != NULL) { if (!cache_in->seek(pos.second)) { goto corrupt; } else { std::vector data; *cache_in >> data; if (utils::crc32(data) != crc) { goto corrupt; } } } PTRACE << "Revision " << id << " ok" << endl; continue; corrupt: PTRACE << "Revision " << id << " corrupted!" << endl; std::cerr << "Cache: Revision " << id << " is corrupted, removing from index file" << std::endl; corrupted.push_back(id); } delete cache_in; delete in; Logger::status() << "done" << endl; Logger::info() << "Cache: Checked " << index.size() << " revisions in " << watch.elapsedMSecs() << " ms" << endl; if (corrupted.empty()) { Logger::info() << "Cache: Everything's alright" << endl; return; } Logger::info() << "Cache: " << corrupted.size() << " corrupted revisions, rewriting index file" << endl; // Remove corrupted revisions from index file for (unsigned int i = 0; i < corrupted.size(); i++) { std::map >::iterator it = index.find(corrupted[i]); if (it != index.end()) { index.erase(it); } } // Defer any signals while writing to the cache { SIGBLOCK_DEFER(); // Rewrite index file GZOStream out(path+"/index"); out << CACHE_VERSION; for (std::map >::iterator it = index.begin(); it != index.end(); ++it) { out << it->first; out << it->second.first << it->second.second; out << crcs[it->first]; } } } pepper-0.3.2/src/backends/0000755000175000001440000000000011762206662012355 500000000000000pepper-0.3.2/src/backends/subversion_delta.cpp0000644000175000001440000004166711721276751016370 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: subversion_delta.cpp * Diff retrieval for the subversion repository backend */ #define __STDC_CONSTANT_MACROS // For INT64_C #include "main.h" #include #include #include #include #include #include "jobqueue.h" #include "logger.h" #include "strlib.h" #include "backends/subversion_p.h" // Statement macro for checking APR calls, similar to SVN_ERR #define APR_ERR(expr) \ do { \ apr_status_t apr_status__temp = (expr); \ if (apr_status__temp) \ return svn_error_wrap_apr(apr_status__temp, NULL); \ } while (0) // streambuf implementation for apr_file_t class AprStreambuf : public std::streambuf { public: explicit AprStreambuf(apr_file_t *file) : std::streambuf(), f(file), m_putback(8), m_buffer(4096 + 8) { char *end = &m_buffer.front() + m_buffer.size(); setg(end, end, end); } private: int_type underflow() { if (gptr() < egptr()) // buffer not exhausted return traits_type::to_int_type(*gptr()); char *base = &m_buffer.front(); char *start = base; if (eback() == base) // true when this isn't the first fill { // Make arrangements for putback characters memmove(base, egptr() - m_putback, m_putback); start += m_putback; } // start is now the start of the buffer, proper. // Read from fptr_ in to the provided buffer size_t n = m_buffer.size() - (start - base); apr_status_t status = apr_file_read(f, start, &n); if (n == 0 || status == APR_EOF) return traits_type::eof(); // Set buffer pointers setg(base, start, start + n); return traits_type::to_int_type(*gptr()); } private: apr_file_t *f; const std::size_t m_putback; std::vector m_buffer; private: // Not allowed AprStreambuf(const AprStreambuf &); AprStreambuf &operator=(const AprStreambuf &); }; // Extra namespace for local structures namespace SvnDelta { // Baton for delta editor struct Baton { const char *target; apr_file_t *out; svn_ra_session_t *ra; svn_revnum_t revision; svn_revnum_t base_revision; svn_revnum_t target_revision; apr_hash_t *deleted_paths; const char *tempdir; const char *empty_file; apr_pool_t *pool; static Baton *make(svn_revnum_t baserev, svn_revnum_t rev, apr_file_t *outfile, apr_pool_t *pool) { Baton *baton = (Baton *)apr_pcalloc(pool, sizeof(Baton)); baton->target = ""; baton->out = outfile; baton->base_revision = baserev; baton->revision = rev; baton->deleted_paths = apr_hash_make(pool); svn_io_temp_dir(&(baton->tempdir), pool); baton->empty_file = NULL; // TODO baton->pool = pool; return baton; } }; struct DirBaton { const char *path; DirBaton *dir_baton; Baton *edit_baton; apr_pool_t *pool; static DirBaton *make(const char *path, DirBaton *parent_baton, Baton *edit_baton, apr_pool_t *pool) { DirBaton *dir_baton = (DirBaton *)apr_pcalloc(pool, sizeof(DirBaton)); dir_baton->dir_baton = parent_baton; dir_baton->edit_baton = edit_baton; dir_baton->pool = pool; dir_baton->path = apr_pstrdup(pool, path); return dir_baton; } }; struct FileBaton { const char *path; const char *path_start_revision; apr_file_t *file_start_revision; apr_hash_t *pristine_props; apr_array_header_t *propchanges; const char *path_end_revision; apr_file_t *file_end_revision; svn_txdelta_window_handler_t apply_handler; void *apply_baton; Baton *edit_baton; apr_pool_t *pool; static FileBaton *make(const char *path, void *edit_baton, apr_pool_t *pool) { FileBaton *file_baton = (FileBaton *)apr_pcalloc(pool, sizeof(FileBaton)); Baton *eb = static_cast(edit_baton); file_baton->edit_baton = eb; file_baton->pool = pool; file_baton->path = apr_pstrdup(pool, path); file_baton->propchanges = apr_array_make(pool, 1, sizeof(svn_prop_t)); return file_baton; } }; // Utility funtions svn_error_t *open_tempfile(apr_file_t **file, const char **path, FileBaton *b) { const char *temp = svn_path_join(b->edit_baton->tempdir, "tempfile", b->pool); return svn_io_open_unique_file2(file, path, temp, ".tmp", svn_io_file_del_on_pool_cleanup, b->pool); } svn_error_t *get_file_from_ra(FileBaton *b, svn_revnum_t revision) { PTRACE << b->path << "@" << revision << endl; svn_stream_t *fstream; apr_file_t *file; SVN_ERR(open_tempfile(&file, &(b->path_start_revision), b)); fstream = svn_stream_from_aprfile2(file, FALSE, b->pool); SVN_ERR(svn_ra_get_file(b->edit_baton->ra, b->path, revision, fstream, NULL, &(b->pristine_props), b->pool)); return svn_stream_close(fstream); } // Delta editor callback functions svn_error_t *set_target_revision(void *edit_baton, svn_revnum_t target_revision, apr_pool_t * /*pool*/) { Baton *eb = static_cast(edit_baton); eb->target_revision = target_revision; return SVN_NO_ERROR; } svn_error_t *open_root(void *edit_baton, svn_revnum_t /*base_revision*/, apr_pool_t *pool, void **root_baton) { PTRACE << endl; Baton *eb = static_cast(edit_baton); DirBaton *b = DirBaton::make("", NULL, eb, pool); *root_baton = b; return SVN_NO_ERROR; } // Prototype svn_error_t *close_file(void *file_baton, const char *text_checksum, apr_pool_t *pool); svn_error_t *delete_entry(const char *path, svn_revnum_t target_revision, void *parent_baton, apr_pool_t *pool) { DirBaton *pb = static_cast(parent_baton); Baton *eb = pb->edit_baton; // File or directory? svn_dirent_t *dirent; SVN_ERR(svn_ra_stat(eb->ra, path, eb->base_revision, &dirent, pool)); if (dirent == NULL) { PDEBUG << "Error: Unable to stat " << path << "@" << eb->base_revision << endl; return SVN_NO_ERROR; } PTRACE << path << "@" << eb->base_revision <<" is a " << (dirent->kind == svn_node_dir ? "directory" : "file") << endl; if (dirent->kind == svn_node_file) { FileBaton *b = FileBaton::make(path, eb, pool); SVN_ERR(get_file_from_ra(b, eb->base_revision)); SVN_ERR(open_tempfile(&(b->file_end_revision), &(b->path_end_revision), b)); SVN_ERR(close_file(b, "", pool)); } else { PTRACE << "Listing " << path << "@" << eb->base_revision << endl; apr_hash_t *dirents; apr_pool_t *iterpool = svn_pool_create(pool); SVN_ERR(svn_ra_get_dir2(eb->ra, &dirents, NULL, NULL, path, eb->base_revision, 0, pool)); // "Delete" directory recursively for (apr_hash_index_t *hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi)) { svn_pool_clear(iterpool); const char *entry; svn_dirent_t *dirent; apr_hash_this(hi, (const void **)(void *)&entry, NULL, (void **)(void *)&dirent); SVN_ERR(delete_entry((const char *)svn_path_join(path, entry, pool), target_revision, parent_baton, iterpool)); } svn_pool_destroy(iterpool); } return SVN_NO_ERROR; } svn_error_t *add_directory(const char *path, void *parent_baton, const char *, svn_revnum_t, apr_pool_t *pool, void **child_baton) { PTRACE << path << endl; DirBaton *pb = static_cast(parent_baton); DirBaton *b = DirBaton::make(path, pb, pb->edit_baton, pool); *child_baton = b; return SVN_NO_ERROR; } svn_error_t *open_directory(const char *path, void *parent_baton, svn_revnum_t /*base_revision*/, apr_pool_t *pool, void **child_baton) { PTRACE << path << endl; DirBaton *pb = static_cast(parent_baton); DirBaton *b = DirBaton::make(path, pb, pb->edit_baton, pool); *child_baton = b; return SVN_NO_ERROR; } svn_error_t *add_file(const char *path, void *parent_baton, const char * /*copyfrom_path*/, svn_revnum_t /*copyfrom_revision*/, apr_pool_t *pool, void **file_baton) { PTRACE << path << endl; DirBaton *db = static_cast(parent_baton); FileBaton *b = FileBaton::make(path, db->edit_baton, pool); *file_baton = b; b->pristine_props = apr_hash_make(pool); return open_tempfile(&(b->file_start_revision), &(b->path_start_revision), b); } svn_error_t *open_file(const char *path, void *parent_baton, svn_revnum_t base_revision, apr_pool_t *pool, void **file_baton) { PTRACE << path << ", base = " << base_revision << endl; DirBaton *db = static_cast(parent_baton); FileBaton *b = FileBaton::make(path, db->edit_baton, pool); *file_baton = b; // TODO: No need to get the whole file if it is binary... return get_file_from_ra(b, base_revision); } svn_error_t *window_handler(svn_txdelta_window_t *window, void *window_baton) { FileBaton *b = static_cast(window_baton); SVN_ERR(b->apply_handler(window, b->apply_baton)); if (!window) { SVN_ERR(svn_io_file_close(b->file_start_revision, b->pool)); SVN_ERR(svn_io_file_close(b->file_end_revision, b->pool)); } return SVN_NO_ERROR; } svn_error_t *apply_textdelta(void *file_baton, const char * /*base_checksum*/, apr_pool_t * /*pool*/, svn_txdelta_window_handler_t *handler, void **handler_baton) { FileBaton *b = static_cast(file_baton); PTRACE << "base is " << b->path_start_revision << endl; SVN_ERR(svn_io_file_open(&(b->file_start_revision), b->path_start_revision, APR_READ, APR_OS_DEFAULT, b->pool)); SVN_ERR(open_tempfile(&(b->file_end_revision), &(b->path_end_revision), b)); svn_txdelta_apply(svn_stream_from_aprfile2(b->file_start_revision, TRUE, b->pool), svn_stream_from_aprfile2(b->file_end_revision, TRUE, b->pool), NULL, b->path, b->pool, &(b->apply_handler), &(b->apply_baton)); *handler = window_handler; *handler_baton = file_baton; return SVN_NO_ERROR; } svn_error_t *close_file(void *file_baton, const char * /*text_checksum*/, apr_pool_t * /*pool*/) { FileBaton *b = static_cast(file_baton); Baton *eb = b->edit_baton; if (b->path_start_revision == NULL || b->path_end_revision == NULL) { PDEBUG << b->path << "@" << eb->target_revision << " Insufficient diff data (nothing has changed)" << endl; return SVN_NO_ERROR; } PTRACE << b->path_start_revision << " -> " << b->path_end_revision << endl; // Skip binary diffs const char *mimetype1 = NULL, *mimetype2 = NULL; if (b->pristine_props) { svn_string_t *pristine_val; pristine_val = (svn_string_t *)apr_hash_get(b->pristine_props, SVN_PROP_MIME_TYPE, strlen(SVN_PROP_MIME_TYPE)); if (pristine_val) { mimetype1 = pristine_val->data; } } if (b->propchanges) { int i; svn_prop_t *propchange; for (i = 0; i < b->propchanges->nelts; i++) { propchange = &APR_ARRAY_IDX(b->propchanges, i, svn_prop_t); if (strcmp(propchange->name, SVN_PROP_MIME_TYPE) == 0) { if (propchange->value) { mimetype2 = propchange->value->data; } break; } } } // TODO: Proper handling of mime-type changes if ((mimetype1 && svn_mime_type_is_binary(mimetype1)) || (mimetype2 && svn_mime_type_is_binary(mimetype2))) { PDEBUG << "Skipping binary files" << endl; return SVN_NO_ERROR; } // Finally, perform the diff static const char equal_string[] = "==================================================================="; svn_diff_t *diff; svn_stream_t *os; os = svn_stream_from_aprfile2(eb->out, TRUE, b->pool); svn_diff_file_options_t *opts = svn_diff_file_options_create(b->pool); SVN_ERR(svn_diff_file_diff_2(&diff, b->path_start_revision, b->path_end_revision, opts, b->pool)); // Print out the diff header SVN_ERR(svn_stream_printf_from_utf8(os, APR_LOCALE_CHARSET, b->pool, "Index: %s" APR_EOL_STR "%s" APR_EOL_STR, b->path, equal_string)); // Output the actual diff SVN_ERR(svn_diff_file_output_unified3(os, diff, b->path_start_revision, b->path_end_revision, b->path, b->path, APR_LOCALE_CHARSET, NULL, FALSE, b->pool)); return SVN_NO_ERROR; } svn_error_t *close_directory(void *, apr_pool_t *) { return SVN_NO_ERROR; } svn_error_t *change_file_prop(void *file_baton, const char *name, const svn_string_t *value, apr_pool_t * /*pool*/) { FileBaton *b = static_cast(file_baton); svn_prop_t *propchange = (svn_prop_t *)apr_array_push(b->propchanges); propchange->name = apr_pstrdup(b->pool, name); propchange->value = value ? svn_string_dup(value, b->pool) : NULL; return SVN_NO_ERROR; } svn_error_t *change_dir_prop(void *, const char *, const svn_string_t *, apr_pool_t *) { return SVN_NO_ERROR; } svn_error_t *absent_directory(const char *path, void * /*parent_baton*/, apr_pool_t * /*pool*/) { PDEBUG << path << endl; return SVN_NO_ERROR; } svn_error_t *absent_file(const char *path, void * /*parent_baton*/, apr_pool_t * /*pool*/) { PDEBUG << path << endl; return SVN_NO_ERROR; } svn_error_t *close_edit(void * /*edit_baton*/, apr_pool_t * /*pool*/) { return SVN_NO_ERROR; } } // namespace SvnDelta // Constructor SvnDiffstatThread::SvnDiffstatThread(SvnConnection *connection, JobQueue *queue) : d(new SvnConnection()), m_queue(queue) { d->open(connection); } // Destructor SvnDiffstatThread::~SvnDiffstatThread() { delete d; } // This function will always perform a diff on the full repository in // order to avoid errors due to non-existent paths and to cache consistency. Diffstat SvnDiffstatThread::diffstat(SvnConnection *c, svn_revnum_t r1, svn_revnum_t r2, apr_pool_t *pool) { if (r2 <= 0) { return Diffstat(); } svn_opt_revision_t rev1, rev2; rev1.kind = rev2.kind = svn_opt_revision_number; rev1.value.number = r1; rev2.value.number = r2; svn_error_t *err; apr_file_t *infile = NULL, *outfile = NULL, *errfile = NULL; if (apr_file_open_stderr(&errfile, pool) != APR_SUCCESS) { throw PEX("Unable to open stderr"); } if (apr_file_pipe_create(&infile, &outfile, pool) != APR_SUCCESS) { throw PEX("Unable to create pipe for reading diff data"); } AprStreambuf buf(infile); std::istream in(&buf); DiffParser parser(in); parser.start(); PTRACE << "Fetching diffstat for revision " << r1 << ":" << r2 << endl; // Setup the diff editor apr_pool_t *subpool = svn_pool_create(pool); svn_delta_editor_t *editor = svn_delta_default_editor(subpool); SvnDelta::Baton *baton = SvnDelta::Baton::make(r1, r2, outfile, subpool); // Open RA session for extra calls during diff err = svn_client_open_ra_session(&baton->ra, c->root, c->ctx, pool); if (err != NULL) { apr_file_close(outfile); apr_file_close(infile); throw PEX(str::printf("Diffstat fetching of revision %ld:%ld failed: %s", r1, r2, SvnConnection::strerr(err).c_str())); } editor->set_target_revision = SvnDelta::set_target_revision; editor->open_root = SvnDelta::open_root; editor->delete_entry = SvnDelta::delete_entry; editor->add_directory = SvnDelta::add_directory; editor->open_directory = SvnDelta::open_directory; editor->add_file = SvnDelta::add_file; editor->open_file = SvnDelta::open_file; editor->apply_textdelta = SvnDelta::apply_textdelta; editor->close_file = SvnDelta::close_file; editor->close_directory = SvnDelta::close_directory; editor->change_file_prop = SvnDelta::change_file_prop; editor->change_dir_prop = SvnDelta::change_dir_prop; editor->close_edit = SvnDelta::close_edit; editor->absent_directory = SvnDelta::absent_directory; editor->absent_file = SvnDelta::absent_file; const svn_ra_reporter3_t *reporter; void *report_baton; err = svn_ra_do_diff3(c->ra, &reporter, &report_baton, rev2.value.number, "", svn_depth_infinity, TRUE, TRUE, c->root, editor, baton, pool); if (err != NULL) { apr_file_close(outfile); apr_file_close(infile); throw PEX(str::printf("Diffstat fetching of revision %ld:%ld failed: %s", r1, r2, SvnConnection::strerr(err).c_str())); } err = reporter->set_path(report_baton, "", rev1.value.number, svn_depth_infinity, FALSE, NULL, pool); if (err != NULL) { apr_file_close(outfile); apr_file_close(infile); throw PEX(str::printf("Diffstat fetching of revision %ld:%ld failed: %s", r1, r2, SvnConnection::strerr(err).c_str())); } err = reporter->finish_report(report_baton, pool); if (err != NULL) { apr_file_close(outfile); apr_file_close(infile); throw PEX(str::printf("Diffstat fetching of revision %ld:%ld failed: %s", r1, r2, SvnConnection::strerr(err).c_str())); } if (apr_file_close(outfile) != APR_SUCCESS) { throw PEX("Unable to close outfile"); } parser.wait(); if (apr_file_close(infile) != APR_SUCCESS) { throw PEX("Unable to close infile"); } return parser.stat(); } // Main Thread function for fetching diffstats from a job queue void SvnDiffstatThread::run() { apr_pool_t *pool = svn_pool_create(d->pool); std::string revision; while (m_queue->getArg(&revision)) { apr_pool_t *subpool = svn_pool_create(pool); std::vector revs = str::split(revision, ":"); svn_revnum_t r1, r2; if (revs.size() > 1) { if (!str::stoi(revs[0], &r1) || !str::stoi(revs[1], &r2)) { goto parse_error; } } else { if (!str::stoi(revs[0], &r2)) { goto parse_error; } r1 = r2 - 1; } try { Diffstat stat = diffstat(d, r1, r2, subpool); m_queue->done(revision, stat); } catch (const PepperException &ex) { Logger::err() << "Error: " << ex.where() << ": " << ex.what() << endl; m_queue->failed(revision); } goto next; parse_error: PEX(std::string("Error parsing revision number ") + revision); m_queue->failed(revision); next: svn_pool_destroy(subpool); } svn_pool_destroy(pool); } pepper-0.3.2/src/backends/git.h0000644000175000001440000000254511721276751013240 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: git.h * Git repository backend (interface) */ #ifndef GIT_BACKEND_H_ #define GIT_BACKEND_H_ #include "backend.h" class GitRevisionPrefetcher; class GitBackend : public Backend { public: GitBackend(const Options &options); ~GitBackend(); void init(); void close(); std::string name() const { return "git"; } static bool handles(const std::string &url); std::string uuid(); std::string head(const std::string &branch = std::string()); std::string mainBranch(); std::vector branches(); std::vector tags(); Diffstat diffstat(const std::string &id); std::vector tree(const std::string &id = std::string()); std::string cat(const std::string &path, const std::string &id = std::string()); LogIterator *iterator(const std::string &branch = std::string(), int64_t start = -1, int64_t end = -1); void prefetch(const std::vector &ids); Revision *revision(const std::string &id); void finalize(); private: std::string m_gitpath; GitRevisionPrefetcher *m_prefetcher; }; #endif // GIT_BACKEND_H_ pepper-0.3.2/src/backends/subversion.h0000644000175000001440000000525611721276751014656 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: subversion.h * Subversion repository backend (interface) */ #ifndef SUBVERSION_BACKEND_H_ #define SUBVERSION_BACKEND_H_ #include "backend.h" class SvnConnection; class SvnDiffstatPrefetcher; class SubversionBackend : public Backend { public: class SvnLogIterator : public LogIterator { struct Interval { Interval() : start(0), end(0) { } Interval(uint64_t start, uint64_t end) : start(start), end(end) { } uint64_t start, end; std::vector revisions; }; public: SvnLogIterator(SubversionBackend *backend, const std::string &prefix, uint64_t startrev, uint64_t endrev); ~SvnLogIterator(); bool nextIds(std::queue *queue); protected: void run(); private: void readIntervalsFromCache(const std::string &file); void writeIntervalsToCache(const std::string &file); void mergeInterval(const Interval &interval); std::vector missingIntervals(uint64_t start, uint64_t end); private: SubversionBackend *m_backend; SvnConnection *d; std::string m_prefix; uint64_t m_startrev, m_endrev; sys::parallel::Mutex m_mutex; sys::parallel::WaitCondition m_cond; std::vector::size_type m_index; std::vector m_cachedIntervals; bool m_finished, m_failed; static sys::parallel::Mutex s_cacheMutex; }; public: SubversionBackend(const Options &options); ~SubversionBackend(); void init(); void close(); std::string name() const { return "subversion"; } static bool handles(const std::string &url); std::string uuid(); std::string head(const std::string &branch = std::string()); std::string mainBranch(); std::vector branches(); std::vector tags(); Diffstat diffstat(const std::string &id); void filterDiffstat(Diffstat *stat); std::vector tree(const std::string &id = std::string()); std::string cat(const std::string &path, const std::string &id = std::string()); LogIterator *iterator(const std::string &branch = std::string(), int64_t start = -1, int64_t end = -1); void prefetch(const std::vector &ids); Revision *revision(const std::string &id); void finalize(); void printHelp() const; private: std::string prefix(const std::string &branch, struct apr_pool_t *pool); private: SvnConnection *d; SvnDiffstatPrefetcher *m_prefetcher; }; #endif // SUBVERSION_BACKEND_H_ pepper-0.3.2/src/backends/mercurial.cpp0000644000175000001440000002206111755247753014775 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: mercurial.cpp * Mercurial repository backend */ #include "Python.h" #include "main.h" // Avoid compilation warnings #include #include #include "logger.h" #include "options.h" #include "revision.h" #include "strlib.h" #include "syslib/fs.h" #include "backends/mercurial.h" // Constructor MercurialBackend::MercurialBackend(const Options &options) : Backend(options) { Py_Initialize(); } // Destructor MercurialBackend::~MercurialBackend() { Py_Finalize(); } // Initializes the backend void MercurialBackend::init() { std::string repo = m_opts.repository(); if (!sys::fs::dirExists(repo + "/.hg")) { throw PEX(str::printf("Not a mercurial repository: %s", repo.c_str())); } int res = simpleString(str::printf("\ import sys \n\ from cStringIO import StringIO\n\ from mercurial import ui,hg,commands \n\ sys.stderr = stderr = StringIO()\n\ myui = ui.ui() \n\ myui.quiet = True \n\ repo = hg.repository(myui, '%s') \n\n", repo.c_str())); if (res != 0) { throw PEX(str::printf("Unable to load or setup Python modules (%d)", res)); } } // Returns true if this backend is able to access the given repository bool MercurialBackend::handles(const std::string &url) { return sys::fs::dirExists(url+"/.hg"); } // Returns a unique identifier for this repository std::string MercurialBackend::uuid() { // Use the ID of the first commit of the master branch as the UUID. std::string out = hgcmd("log", "date=None, user=None, quiet=None, rev=\"0\""); size_t pos = out.find(':'); if (pos != std::string::npos) { out = out.substr(pos+1); } return str::trim(out); } // Returns the HEAD revision for the current branch std::string MercurialBackend::head(const std::string &branch) { std::string out = hgcmd("log", str::printf("date=None, rev=None, user=None, quiet=None, limit=1, branch=[\"%s\"]", branch.c_str())); size_t pos = out.find(':'); if (pos != std::string::npos) { out = out.substr(pos+1); } return str::trim(out); } // Returns the currently checked out branch std::string MercurialBackend::mainBranch() { std::string out = hgcmd("branch"); return str::trim(out); } // Returns a list of available branches std::vector MercurialBackend::branches() { #if 1 std::string out = hgcmd("branches"); #else int ret; std::string out = sys::io::exec(&ret, "hg", "--noninteractive", "--repository", m_opts.repository().c_str(), "branches", "--quiet"); if (ret != 0) { throw PEX(str::printf("Unable to retreive the list of branches (%d)", ret)); } #endif std::vector branches = str::split(out, "\n"); while (!branches.empty() && branches[branches.size()-1].empty()) { branches.pop_back(); } return branches; } // Returns a list of available tags std::vector MercurialBackend::tags() { // TODO: Determine correct keyword for non-quiet output, if any simpleString("myui.quiet = False\n"); std::string out = hgcmd("tags"); simpleString("myui.quiet = True\n"); std::vector lines = str::split(out, "\n"); std::vector tags; for (unsigned int i = 0; i < lines.size(); i++) { size_t pos = lines[i].find(" "); if ((pos = lines[i].find(" ")) == std::string::npos) { continue; } std::string name = lines[i].substr(0, pos); if ((pos = lines[i].find(":", pos)) == std::string::npos) { continue; } std::string id = lines[i].substr(pos+1); tags.push_back(Tag(id, name)); } return tags; } // Returns a diffstat for the specified revision Diffstat MercurialBackend::diffstat(const std::string &id) { std::vector ids = str::split(id, ":"); #if 1 std::string out; if (ids.size() > 1) { out = hgcmd("diff", str::printf("rev=[\"%s:%s\"]", ids[0].c_str(), ids[1].c_str())); } else { out = hgcmd("diff", str::printf("change=\"%s\"", ids[0].c_str())); } #else std::string out = sys::io::exec(hgcmd()+" diff --change "+id); #endif std::istringstream in(out); return DiffParser::parse(in); } // Returns a file listing for the given revision (defaults to HEAD) std::vector MercurialBackend::tree(const std::string &id) { std::string out; if (id.empty()) { out = hgcmd("status", str::printf("change=\"%s\", all=True, no_status=True", id.c_str())); } else { out = hgcmd("status", str::printf("all=True, no_status=True", id.c_str())); } std::vector contents = str::split(out, "\n"); if (!contents.empty() && contents[contents.size()-1].empty()) { contents.pop_back(); } return contents; } // Returns the file contents of the given path at the given revision (defaults to HEAD) std::string MercurialBackend::cat(const std::string &path, const std::string &id) { // stdout redirection to a cStringIO object doesn't work here, // because Mercurial's cat will close the file after writing, // which discards all contents of a cStringIO object. std::string filename; FILE *f = sys::fs::mkstemp(&filename); hgcmd("cat", str::printf("\"%s\", output=\"%s\", rev=\"%s\"", (m_opts.repository() + "/" + path).c_str(), filename.c_str(), id.c_str())); char buf[4096]; size_t size; std::string out; while ((size = fread(buf, 1, sizeof(buf), f)) != 0) { out += std::string(buf, size); } if (ferror(f)) { throw PEX("Error reading stream"); } fclose(f); sys::fs::unlink(filename); return out; } // Returns a revision iterator for the given branch Backend::LogIterator *MercurialBackend::iterator(const std::string &branch, int64_t start, int64_t end) { std::string date = "None"; if (start >= 0) { if (end >= 0) { date = str::printf("\"%lld 0 to %lld 0\"", start, end); } else { date = str::printf("\">%lld 0\"", start); } } else if (end >= 0) { date = str::printf("\"<%lld 0\"", end); } // Request log from HEAD to 0, so follow_first is effective std::string out = hgcmd("log", str::printf("date=%s, user=None, follow_first=True, quiet=None, rev=[\"%s:0\"]", date.c_str(), (head(branch)).c_str())); std::vector revisions = str::split(out, "\n"); if (!revisions.empty()) { revisions.pop_back(); } for (unsigned int i = 0; i < revisions.size(); i++) { size_t pos = revisions[i].find(':'); if (pos != std::string::npos) { revisions[i] = revisions[i].substr(pos+1); } } std::reverse(revisions.begin(), revisions.end()); // Add parent revisions, so diffstat fetching will give correct results for (int i = revisions.size()-1; i > 0; i--) { revisions[i] = revisions[i-1] + ":" + revisions[i]; } return new LogIterator(revisions); } // Returns the revision data for the given ID Revision *MercurialBackend::revision(const std::string &id) { std::vector ids = str::split(id, ":"); #if 1 std::string meta = hgcmd("log", str::printf("rev=[\"%s\"], date=None, user=None, template=\"{date|hgdate}\\n{author|person}\\n{desc}\"", ids.back().c_str())); #else std::string meta = sys::io::exec(hgcmd()+" log -r "+id+" --template=\"{date|hgdate}\n{author|person}\n{desc}\""); #endif std::vector lines = str::split(meta, "\n"); int64_t date = 0; std::string author; if (!lines.empty()) { // Date is given as seconds and timezone offset from UTC std::vector parts = str::split(lines[0], " "); if (parts.size() > 1) { int64_t offset = 0; str::str2int(parts[0], &date); str::str2int(parts[1], &offset); date += offset; } lines.erase(lines.begin()); } if (!lines.empty()) { author = lines[0]; lines.erase(lines.begin()); } std::string msg = str::join(lines, "\n"); return new Revision(id, date, author, msg, diffstat(id)); } // Returns the hg command with the correct --repository command line switch std::string MercurialBackend::hgcmd() const { return std::string("hg --noninteractive --repository ") + m_opts.repository(); } // Returns the Python code for a hg command std::string MercurialBackend::hgcmd(const std::string &cmd, const std::string &args) const { PyObject *pModule = PyImport_AddModule("__main__"); simpleString(str::printf("\ stdout = \"\"\n\ stderr.truncate(0)\n\ res = -127\n\ try:\n\ myui.pushbuffer()\n\ res = commands.%s(myui, repo, %s)\n\ stdout = myui.popbuffer()\n\ except:\n\ res = -127\n\ stderr.write(sys.exc_info()[0])\n\ ", cmd.c_str(), args.c_str())); // Check return value PyObject *object = PyObject_GetAttrString(pModule, "res"); Py_ssize_t res = (object == Py_None ? 0 : PyNumber_AsSsize_t(object, NULL)); if (res != 0) { // Throw exception object = PyObject_GetAttrString(pModule, "stderr"); char *getvalue = strdup("getvalue"); PyObject *output = PyObject_CallMethod(object, getvalue, NULL); free(getvalue); assert(output != NULL); throw PEX(str::trim(PyString_AsString(output))); } // Return stdout object = PyObject_GetAttrString(pModule, "stdout"); assert(object != NULL); return std::string(PyString_AsString(object)); } // Wrapper for PyRun_SimpleString() int MercurialBackend::simpleString(const std::string &str) const { PTRACE << str; return PyRun_SimpleString(str.c_str()); } pepper-0.3.2/src/backends/subversion_p.h0000644000175000001440000000315711721276751015173 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: subversion_p.h * Interfaces of internal classes for the Subversion backend */ #ifndef SUBVERSION_BACKEND_P_H_ #define SUBVERSION_BACKEND_P_H_ #include #include #include #include "diffstat.h" template class JobQueue; // Repository connection class SvnConnection { public: SvnConnection(); ~SvnConnection(); void open(const std::string &url, const std::map &options); void open(SvnConnection *parent); static std::string strerr(svn_error_t *err); private: void init(); template static T hashget(apr_hash_t *hash, const char *key) { return static_cast(apr_hash_get(hash, key, APR_HASH_KEY_STRING)); } public: apr_pool_t *pool; svn_client_ctx_t *ctx; svn_ra_session_t *ra; const char *url, *root, *prefix; }; // The diffstat thread is implemented in subversion_delta.cpp class SvnDiffstatThread : public sys::parallel::Thread { public: SvnDiffstatThread(SvnConnection *connection, JobQueue *queue); ~SvnDiffstatThread(); static Diffstat diffstat(SvnConnection *c, svn_revnum_t r1, svn_revnum_t r2, apr_pool_t *pool); protected: void run(); private: SvnConnection *d; JobQueue *m_queue; }; #endif // SUBVERSION_BACKEND_P_H_ pepper-0.3.2/src/backends/git.cpp0000644000175000001440000005335611762205442013573 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: git.cpp * Git repository backend */ #include "main.h" #include #include #include #include "jobqueue.h" #include "logger.h" #include "options.h" #include "revision.h" #include "strlib.h" #include "utils.h" #include "syslib/datetime.h" #include "syslib/fs.h" #include "syslib/io.h" #include "syslib/parallel.h" #include "backends/git.h" // Diffstat fetching worker thread, using a pipe to write data to "git diff-tree" class GitDiffstatPipe : public sys::parallel::Thread { public: GitDiffstatPipe(const std::string &gitpath, JobQueue *queue) : m_gitpath(gitpath), m_queue(queue) { } static Diffstat diffstat(const std::string &gitpath, const std::string &id, const std::string &parent = std::string()) { if (!parent.empty()) { sys::io::PopenStreambuf buf((gitpath+"/git-diff-tree").c_str(), "-U0", "--no-renames", parent.c_str(), id.c_str()); std::istream in(&buf); Diffstat stat = DiffParser::parse(in); if (buf.close() != 0) { throw PEX("git diff-tree command failed"); } return stat; } else { sys::io::PopenStreambuf buf((gitpath+"/git-diff-tree").c_str(), "-U0", "--no-renames", "--root", id.c_str()); std::istream in(&buf); Diffstat stat = DiffParser::parse(in); if (buf.close() != 0) { throw PEX("git diff-tree command failed"); } return stat; } } protected: void run() { // TODO: Error checking sys::io::PopenStreambuf buf((m_gitpath+"/git-diff-tree").c_str(), "-U0", "--no-renames", "--stdin", "--root", NULL, NULL, NULL, std::ios::in | std::ios::out); std::istream in(&buf); std::ostream out(&buf); std::string revision; while (m_queue->getArg(&revision)) { std::vector revs = str::split(revision, ":"); if (revs.size() < 2) { out << revs[0] << '\n'; } else { out << revs[1] << " " << revs[0] << '\n'; } // We use EOF characters to mark the end of a revision for // the diff parser. git diff-tree won't understand this line // and simply write the EOF. out << (char)EOF << '\n' << std::flush; Diffstat stat = DiffParser::parse(in); m_queue->done(revision, stat); } } private: std::string m_gitpath; JobQueue *m_queue; }; // Meta-data fetching worker thread, passing multiple revisions to git-rev-list at once. // Note that all IDs coming from the JobQueue are expected to contain a single hash, // i.e. no parent:child ID spec. class GitMetaDataThread : public sys::parallel::Thread { public: struct Data { int64_t date; std::string author; std::string message; }; public: GitMetaDataThread(const std::string &gitpath, JobQueue *queue) : m_gitpath(gitpath), m_queue(queue) { } static void parseHeader(const std::vector &header, Data *dest) { // TODO: Proper exception descriptions if (header.size() < 5) { throw PEX(str::printf("Unable to parse meta-data")); } // Here's the raw header format. The 'parent' line is not present for // non-root commits. // $ID_HASH // tree $TREE_HASH // parent $PARENT_HASH // author $AUTHOR_NAME $AUTHOR_EMAIL $DATE $OFFSET // committer $AUTHOR_NAME $AUTHOR_EMAIL $DATE $OFFSET // // $MESSAGE_INDENTED_BY_4_SPACES // Find author line size_t line = 0; while (line < header.size() && header[line].compare(0, 7, "author ")) { ++line; } if (line >= header.size()) { PDEBUG << "Invalid header:" << endl; for (size_t i = 0; i < header.size(); i++) { PDEBUG << header[i] << endl; } throw PEX(str::printf("Unable to parse meta-data")); } // Author information dest->author = header[line].substr(7); // Strip email address and date, assuming a start at the last "<" (not really compliant with RFC2882) size_t pos = dest->author.find_last_of('<'); if (pos != std::string::npos) { dest->author = dest->author.substr(0, pos); } dest->author = str::trim(dest->author); // Commiter date if (header[++line].compare(0, 10, "committer ")) { throw PEX(str::printf("Unable to parse commit date from line: %s", header[line].c_str())); } if ((pos = header[line].find_last_of(' ')) == std::string::npos) { throw PEX(str::printf("Unable to parse commit date from line: %s", header[line].c_str())); } size_t pos2 = header[line].find_last_of(' ', pos - 1); if (pos2 == std::string::npos || !str::str2int(header[line].substr(pos2, pos - pos2), &(dest->date), 10)) { throw PEX(str::printf("Unable to parse commit date from line: %s", header[line].c_str())); } int64_t offset_hr = 0, offset_min = 0; if (!str::str2int(header[line].substr(pos+1, 3), &offset_hr, 10) || !str::str2int(header[line].substr(pos+4, 2), &offset_min, 10)) { throw PEX(str::printf("Unable to parse commit date from line: %s", header[line].c_str())); } dest->date += offset_hr * 60 * 60 + offset_min * 60; // Commit message dest->message.clear(); for (size_t i = line+1; i < header.size(); i++) { if (header[i].length() > 4) { dest->message += header[i].substr(4); } if (!header[i].empty() && header[i][0] != '\0') { dest->message += "\n"; } } } static void metaData(const std::string &gitpath, const std::string &id, Data *dest) { int ret; std::string header = sys::io::exec(&ret, (gitpath+"/git-rev-list").c_str(), "-1", "--header", id.c_str()); if (ret != 0) { throw PEX(str::printf("Unable to retrieve meta-data for revision '%s' (%d, %s)", id.c_str(), ret, header.c_str())); } parseHeader(str::split(header, "\n"), dest); } protected: void run() { Data data; const size_t maxids = 64; const char **args = new const char *[maxids + 4]; std::vector ids; std::string revlist = m_gitpath + "/git-rev-list"; args[0] = "--no-walk"; args[1] = "--header"; // No support for message bodies in old git versions // Try to fetch the revision headers for maxrevs revisions at once while (m_queue->getArgs(&ids, maxids)) { for (size_t i = 0; i < ids.size(); i++) { args[i+2] = ids[i].c_str(); } args[ids.size()+2] = NULL; sys::io::PopenStreambuf buf(revlist.c_str(), args); std::istream in(&buf); // Parse headers std::string str; std::vector header; while (in.good()) { std::getline(in, str); if (!str.empty() && str[0] == '\0') { try { parseHeader(header, &data); m_queue->done(header[0], data); } catch (const std::exception &ex) { PDEBUG << "Error parsing revision header: " << ex.what() << endl; m_queue->failed(header[0]); } header.clear(); header.push_back(str.substr(1)); } else { header.push_back(str); } } int ret = buf.close(); if (ret != 0) { PDEBUG << "Error running rev-list, ret = " << ret << endl; // Try all IDs one by one, so the incorrect ones can be identified for (size_t i = 0; i < ids.size(); i++) { try { metaData(m_gitpath, ids[i], &data); m_queue->done(ids[i], data); } catch (const std::exception &ex) { PDEBUG << "Error parsing revision header: " << ex.what() << endl; m_queue->failed(ids[i]); } } } } delete[] args; } private: std::string m_gitpath; JobQueue *m_queue; }; // Handles the prefetching of revision meta-data and diffstats class GitRevisionPrefetcher { public: GitRevisionPrefetcher(const std::string &git, int n = -1) : m_metaQueue(4096) { if (n < 0) { n = std::max(1, sys::parallel::idealThreadCount() / 2); } for (int i = 0; i < n; i++) { sys::parallel::Thread *thread = new GitDiffstatPipe(git, &m_diffQueue); thread->start(); m_threads.push_back(thread); } // Limit to 4 threads to prevent meta queue congestions n = std::min(n, 4); for (int i = 0; i < n; i++) { sys::parallel::Thread *thread = new GitMetaDataThread(git, &m_metaQueue); thread->start(); m_threads.push_back(thread); } Logger::info() << "GitBackend: Using " << m_threads.size() << " threads for prefetching diffstats (" << m_threads.size()-n << ") / meta-data (" << n << ")" << endl; } ~GitRevisionPrefetcher() { for (unsigned int i = 0; i < m_threads.size(); i++) { delete m_threads[i]; } } void stop() { m_diffQueue.stop(); m_metaQueue.stop(); } void wait() { for (unsigned int i = 0; i < m_threads.size(); i++) { m_threads[i]->wait(); } } void prefetch(const std::vector &revisions) { m_diffQueue.put(revisions); // Put child commits only to the meta queue std::vector children; children.resize(revisions.size()); for (size_t i = 0; i < revisions.size(); i++) { children[i] = utils::childId(revisions[i]); } m_metaQueue.put(children); } bool getDiffstat(const std::string &revision, Diffstat *dest) { return m_diffQueue.getResult(revision, dest); } bool getMeta(const std::string &revision, GitMetaDataThread::Data *dest) { return m_metaQueue.getResult(utils::childId(revision), dest); } bool willFetchDiffstat(const std::string &revision) { return m_diffQueue.hasArg(revision); } bool willFetchMeta(const std::string &revision) { return m_metaQueue.hasArg(utils::childId(revision)); } private: JobQueue m_diffQueue; JobQueue m_metaQueue; std::vector m_threads; }; // Constructor GitBackend::GitBackend(const Options &options) : Backend(options), m_prefetcher(NULL) { } // Destructor GitBackend::~GitBackend() { close(); } // Initializes the backend void GitBackend::init() { std::string repo = m_opts.repository(); if (sys::fs::exists(repo + "/HEAD")) { setenv("GIT_DIR", repo.c_str(), 1); } else if (sys::fs::exists(repo + "/.git/HEAD")) { setenv("GIT_DIR", (repo + "/.git").c_str(), 1); } else if (sys::fs::fileExists(repo + "/.git")) { PDEBUG << "Parsing .git file" << endl; std::ifstream in((repo + "/.git").c_str(), std::ios::in); if (!in.good()) { throw PEX(str::printf("Unable to read from .git file: %s", repo.c_str())); } std::string str; std::getline(in, str); std::vector parts = str::split(str, ":"); if (parts.size() < 2) { throw PEX(str::printf("Unable to parse contents of .git file: %s", str.c_str())); } setenv("GIT_DIR", str::trim(parts[1]).c_str(), 1); } else { throw PEX(str::printf("Not a git repository: %s", repo.c_str())); } // Search for git executable std::string git = sys::fs::which("git");; PDEBUG << "git executable is " << git << endl; int ret; m_gitpath = str::trim(sys::io::exec(&ret, git.c_str(), "--exec-path")); if (ret != 0) { throw PEX("Unable to determine git exec-path"); } PDEBUG << "git exec-path is " << m_gitpath << endl; PDEBUG << "GIT_DIR has been set to " << getenv("GIT_DIR") << endl; } // Called after Report::run() void GitBackend::close() { // Clean up any prefetching threads finalize(); } // Returns true if this backend is able to access the given repository bool GitBackend::handles(const std::string &url) { if (sys::fs::dirExists(url+"/.git")) { return true; } else if (sys::fs::fileExists(url+"/.git")) { PDEBUG << "Detached repository detected" << endl; return true; } else if (sys::fs::dirExists(url) && sys::fs::fileExists(url+"/HEAD") && sys::fs::dirExists(url+"/objects")) { PDEBUG << "Bare repository detected" << endl; return true; } return false; } // Returns a unique identifier for this repository std::string GitBackend::uuid() { // Determine current main branch and the HEAD revision std::string branch = mainBranch(); std::string headrev = head(branch); std::string oldroot, oldhead; int ret; // The $GIT_DIR/pepper.cache file caches branch names and their root // commits. It consists of lines of the form // $BRANCH_NAME $HEAD $ROOT std::string cachefile = std::string(getenv("GIT_DIR")) + "/pepper.cache"; { std::ifstream in((std::string(getenv("GIT_DIR")) + "/pepper.cache").c_str()); while (in.good()) { std::string str; std::getline(in, str); if (str.compare(0, branch.length(), branch)) { continue; } std::vector parts = str::split(str, " "); if (parts.size() == 3) { oldhead = parts[1]; oldroot = parts[2]; if (oldhead == headrev) { PDEBUG << "Found cached root commit" << endl; return oldroot; } } break; } } // Check if the old root commit is still valid by checking if the old head revision // is an ancestory of the current one std::string root; if (!oldroot.empty()) { std::string ref = sys::io::exec(&ret, (m_gitpath+"/git-rev-list").c_str(), "-1", (oldhead + ".." + headrev).c_str()); if (ret == 0 && !ref.empty()) { PDEBUG << "Old head " << oldhead << " is a valid ancestor, updating cached head" << endl; root = oldroot; } } // Get ID of first commit of the selected branch // Unfortunatley, the --max-count=n option results in n revisions counting from the HEAD. // This way, we'll always get the HEAD revision with --max-count=1. if (root.empty()) { sys::datetime::Watch watch; std::string id = sys::io::exec(&ret, (m_gitpath+"/git-rev-list").c_str(), "--reverse", branch.c_str(), "--"); if (ret != 0) { throw PEX(str::printf("Unable to determine the root commit for branch '%s' (%d)", branch.c_str(), ret)); } size_t pos = id.find_first_of('\n'); if (pos == std::string::npos) { throw PEX(str::printf("Unable to determine the root commit for branch '%s' (%d)", branch.c_str(), ret)); } root = id.substr(0, pos); PDEBUG << "Determined root commit in " << watch.elapsedMSecs() << " ms" << endl; } // Update the cache file std::string newfile = cachefile + ".tmp"; FILE *out = fopen(newfile.c_str(), "w"); if (out == NULL) { throw PEX_ERRNO(); } fprintf(out, "%s %s %s\n", branch.c_str(), headrev.c_str(), root.c_str()); { std::ifstream in(cachefile.c_str()); while (in.good()) { std::string str; std::getline(in, str); if (str.empty() || !str.compare(0, branch.length(), branch)) { continue; } fprintf(out, "%s\n", str.c_str()); } fsync(fileno(out)); fclose(out); } sys::fs::rename(newfile, cachefile); return root; } // Returns the HEAD revision for the given branch std::string GitBackend::head(const std::string &branch) { int ret; std::string out = sys::io::exec(&ret, (m_gitpath+"/git-rev-list").c_str(), "-1", (branch.empty() ? "HEAD" : branch).c_str(), "--"); if (ret != 0) { throw PEX(str::printf("Unable to retrieve head commit for branch %s (%d)", branch.c_str(), ret)); } return str::trim(out); } // Returns the currently checked out branch std::string GitBackend::mainBranch() { int ret; std::string out = sys::io::exec(&ret, (m_gitpath+"/git-branch").c_str()); if (ret != 0) { throw PEX(str::printf("Unable to retrieve the list of branches (%d)", ret)); } std::vector branches = str::split(out, "\n"); for (unsigned int i = 0; i < branches.size(); i++) { if (branches[i].empty()) { continue; } if (branches[i][0] == '*') { return branches[i].substr(2); } branches[i] = branches[i].substr(2); } if (std::search_n(branches.begin(), branches.end(), 1, "master") != branches.end()) { return "master"; } else if (std::search_n(branches.begin(), branches.end(), 1, "remotes/origin/master") != branches.end()) { return "remotes/origin/master"; } // Fallback return "master"; } // Returns a list of available local branches std::vector GitBackend::branches() { int ret; std::string out = sys::io::exec(&ret, (m_gitpath+"/git-branch").c_str()); if (ret != 0) { throw PEX(str::printf("Unable to retrieve the list of branches (%d)", ret)); } std::vector branches = str::split(out, "\n"); for (unsigned int i = 0; i < branches.size(); i++) { if (branches[i].empty()) { branches.erase(branches.begin()+i); --i; continue; } branches[i] = branches[i].substr(2); } return branches; } // Returns a list of available tags std::vector GitBackend::tags() { int ret; // Fetch list of tag names std::string out = sys::io::exec(&ret, (m_gitpath+"/git-tag").c_str()); if (ret != 0) { throw PEX(str::printf("Unable to retrieve the list of tags (%d)", ret)); } std::vector names = str::split(out, "\n"); std::vector tags; // Determine corresponding commits for (unsigned int i = 0; i < names.size(); i++) { if (names[i].empty()) { continue; } std::string out = sys::io::exec(&ret, (m_gitpath+"/git-rev-list").c_str(), "-1", names[i].c_str()); if (ret != 0) { throw PEX(str::printf("Unable to retrieve the list of tags (%d)", ret)); } std::string id = str::trim(out); if (!id.empty()) { tags.push_back(Tag(id, names[i])); } } return tags; } // Returns a diffstat for the specified revision Diffstat GitBackend::diffstat(const std::string &id) { // Maybe it's prefetched if (m_prefetcher && m_prefetcher->willFetchDiffstat(id)) { Diffstat stat; if (!m_prefetcher->getDiffstat(id, &stat)) { throw PEX(str::printf("Failed to retrieve diffstat for revision %s", id.c_str())); } return stat; } PDEBUG << "Fetching revision " << id << " manually" << endl; std::vector revs = str::split(id, ":"); if (revs.size() > 1) { return GitDiffstatPipe::diffstat(m_gitpath, revs[1], revs[0]); } return GitDiffstatPipe::diffstat(m_gitpath, revs[0]); } // Returns a file listing for the given revision (defaults to HEAD) std::vector GitBackend::tree(const std::string &id) { int ret; std::string out = sys::io::exec(&ret, (m_gitpath+"/git-ls-tree").c_str(), "-r", "--full-name", "--name-only", (id.empty() ? "HEAD" : id.c_str())); if (ret != 0) { throw PEX(str::printf("Unable to retrieve tree listing for ID '%s' (%d)", id.c_str(), ret)); } std::vector contents = str::split(out, "\n"); while (!contents.empty() && contents[contents.size()-1].empty()) { contents.pop_back(); } return contents; } // Returns the file contents of the given path at the given revision (defaults to HEAD) std::string GitBackend::cat(const std::string &path, const std::string &id) { int ret; std::string out = sys::io::exec(&ret, (m_gitpath+"/git-show").c_str(), ((id.empty() ? std::string("HEAD") : id)+":"+path).c_str()); if (ret != 0) { throw PEX(str::printf("Unable to get file contents of %s@%s (%d)", path.c_str(), id.c_str(), ret)); } return out; } // Returns a revision iterator for the given branch Backend::LogIterator *GitBackend::iterator(const std::string &branch, int64_t start, int64_t end) { int ret; std::string out; if (start >= 0) { std::string maxage = str::printf("--max-age=%lld", start); if (end >= 0) { std::string minage = str::printf("--min-age=%lld", end); out = sys::io::exec(&ret, (m_gitpath+"/git-rev-list").c_str(), "--first-parent", "--reverse", maxage.c_str(), minage.c_str(), branch.c_str(), "--"); } else { out = sys::io::exec(&ret, (m_gitpath+"/git-rev-list").c_str(), "--first-parent", "--reverse", maxage.c_str(), branch.c_str(), "--"); } } else { if (end >= 0) { std::string minage = str::printf("--min-age=%lld", end); out = sys::io::exec(&ret, (m_gitpath+"/git-rev-list").c_str(), "--first-parent", "--reverse", minage.c_str(), branch.c_str(), "--"); } else { out = sys::io::exec(&ret, (m_gitpath+"/git-rev-list").c_str(), "--first-parent", "--reverse", branch.c_str(), "--"); } } if (ret != 0) { throw PEX(str::printf("Unable to retrieve log for branch '%s' (%d)", branch.c_str(), ret)); } std::vector revisions = str::split(out, "\n"); while (!revisions.empty() && revisions[revisions.size()-1].empty()) { revisions.pop_back(); } // Add parent revisions, so diffstat fetching will give correct results for (ssize_t i = revisions.size()-1; i > 0; i--) { revisions[i] = revisions[i-1] + ":" + revisions[i]; } return new LogIterator(revisions); } // Starts prefetching the given revision IDs void GitBackend::prefetch(const std::vector &ids) { if (m_prefetcher == NULL) { m_prefetcher = new GitRevisionPrefetcher(m_gitpath); } m_prefetcher->prefetch(ids); PDEBUG << "Started prefetching " << ids.size() << " revisions" << endl; } // Returns the revision data for the given ID Revision *GitBackend::revision(const std::string &id) { // Unfortunately, older git versions don't have the %B format specifier // for unwrapped subject and body, so the raw commit headers will be parsed instead. #if 0 int ret; std::string meta = sys::io::exec(&ret, m_git.c_str(), "log", "-1", "--pretty=format:%ct\n%aN\n%B", id.c_str()); if (ret != 0) { throw PEX(str::printf("Unable to retrieve meta-data for revision '%s' (%d, %s)", id.c_str(), ret, meta.c_str())); } std::vector lines = str::split(meta, "\n"); int64_t date = 0; std::string author; if (!lines.empty()) { str::str2int(lines[0], &date); lines.erase(lines.begin()); } if (!lines.empty()) { author = lines[0]; lines.erase(lines.begin()); } std::string msg = str::join(lines, "\n"); return new Revision(id, date, author, msg, diffstat(id)); #else // Check for pre-fetched meta data first if (m_prefetcher && m_prefetcher->willFetchMeta(id)) { GitMetaDataThread::Data data; if (!m_prefetcher->getMeta(id, &data)) { throw PEX(str::printf("Failed to retrieve meta-data for revision %s", id.c_str())); } return new Revision(id, data.date, data.author, data.message, diffstat(id)); } GitMetaDataThread::Data data; GitMetaDataThread::metaData(m_gitpath, utils::childId(id), &data); return new Revision(id, data.date, data.author, data.message, diffstat(id)); #endif } // Handle cleanup of diffstat scheduler void GitBackend::finalize() { if (m_prefetcher) { PDEBUG << "Waiting for prefetcher... " << endl; m_prefetcher->stop(); m_prefetcher->wait(); delete m_prefetcher; m_prefetcher = NULL; PDEBUG << "done" << endl; } } pepper-0.3.2/src/backends/mercurial.h0000644000175000001440000000261711721276751014440 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: mercurial.h * Mercurial repository backend (interface) */ #ifndef MERCURIAL_BACKEND_H_ #define MERCURIAL_BACKEND_H_ #include "backend.h" class MercurialBackend : public Backend { public: MercurialBackend(const Options &options); ~MercurialBackend(); void init(); std::string name() const { return "mercurial"; } static bool handles(const std::string &url); std::string uuid(); std::string head(const std::string &branch = std::string()); std::string mainBranch(); std::vector branches(); std::vector tags(); Diffstat diffstat(const std::string &id); std::vector tree(const std::string &id = std::string()); std::string cat(const std::string &path, const std::string &id = std::string()); LogIterator *iterator(const std::string &branch = std::string(), int64_t start = -1, int64_t end = -1); Revision *revision(const std::string &id); private: std::string hgcmd() const; std::string hgcmd(const std::string &cmd, const std::string &args = std::string()) const; int simpleString(const std::string &str) const; }; #endif // MERCURIAL_BACKEND_H_ pepper-0.3.2/src/backends/subversion.cpp0000644000175000001440000010227011755247753015212 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: subversion.cpp * Subversion repository backend */ #define __STDC_CONSTANT_MACROS // For INT64_C #include "main.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "bstream.h" #include "cache.h" #include "jobqueue.h" #include "logger.h" #include "options.h" #include "revision.h" #include "strlib.h" #include "syslib/fs.h" #include "syslib/parallel.h" #include "backends/subversion.h" #include "backends/subversion_p.h" // Constructor SvnConnection::SvnConnection() : pool(NULL), ctx(NULL), ra(NULL), url(NULL), root(NULL), prefix(NULL) { } // Destructor SvnConnection::~SvnConnection() { if (pool) { svn_pool_destroy(pool); } } // Opens the connection to the Subversion repository void SvnConnection::open(const std::string &url, const std::map &options) { PTRACE << "Opening connection to " << url << endl; pool = svn_pool_create(NULL); init(); // Canoicalize the repository URL this->url = svn_path_uri_encode(svn_path_canonicalize(url.c_str(), pool), pool); svn_error_t *err; // Create the client context if ((err = svn_client_create_context(&ctx, pool))) { throw PEX(strerr(err)); } if ((err = svn_config_get_config(&(ctx->config), NULL, pool))) { throw PEX(strerr(err)); } // Setup the authentication data svn_auth_baton_t *auth_baton; svn_config_t *config = hashget(ctx->config, SVN_CONFIG_CATEGORY_CONFIG); const char *user = NULL, *pass = NULL; if (options.find("username") != options.end()) { user = apr_pstrdup(pool, options.find("username")->second.c_str()); } if (options.find("password") != options.end()) { pass = apr_pstrdup(pool, options.find("password")->second.c_str()); } if ((err = svn_cmdline_setup_auth_baton(&auth_baton, (options.find("non-interactive") != options.end()), user, pass, NULL, (options.find("no-auth-cache") != options.end()), config, NULL, NULL, pool))) { throw PEX(strerr(err)); } ctx->auth_baton = auth_baton; // Setup the RA session if ((err = svn_client_open_ra_session(&ra, this->url, ctx, pool))) { throw PEX(strerr(err)); } // Determine the repository root and reparent if ((err = svn_ra_get_repos_root2(ra, &root, pool))) { throw PEX(strerr(err)); } prefix = apr_pstrdup(pool, this->url + strlen(root)); if (prefix && *prefix == '/') { ++prefix; // Original adress is tracked in pool } PDEBUG << "Root is " << root << " -> prefix is " << prefix << endl; PTRACE << "Reparent to " << root << endl; if ((err = svn_ra_reparent(ra, root, pool))) { throw PEX(strerr(err)); } } // Opens the connection to the Subversion repository, using the client context // of the given parent connection void SvnConnection::open(SvnConnection *parent) { if (parent->ctx == NULL) { throw PEX("Parent connection not open yet."); } PTRACE << "Opening child connection to " << parent->root << endl; pool = svn_pool_create(NULL); init(); // Copy connection data from parent ctx = parent->ctx; url = apr_pstrdup(pool, parent->url); root = apr_pstrdup(pool, parent->root); prefix = apr_pstrdup(pool, parent->prefix); // Setup the RA session svn_error_t *err; if ((err = svn_client_open_ra_session(&ra, root, ctx, pool))) { throw PEX(strerr(err)); } } // Similar to svn_handle_error2(), but returns the error description as a std::string std::string SvnConnection::strerr(svn_error_t *err) { apr_pool_t *pool; svn_error_t *tmp_err; apr_array_header_t *empties; apr_pool_create(&pool, NULL); empties = apr_array_make(pool, 0, sizeof(apr_status_t)); std::string str; tmp_err = err; while (tmp_err) { int i; bool printed_already = false; if (!tmp_err->message) { for (i = 0; i < empties->nelts; i++) { if (tmp_err->apr_err == APR_ARRAY_IDX(empties, i, apr_status_t)) { printed_already = true; break; } } } if (!printed_already) { char errbuf[256]; const char *err_string; svn_error_t *tmp_err2 = NULL; if (tmp_err->message) { if (!str.empty()) { str += ", "; } str += std::string(tmp_err->message); } else { if ((tmp_err->apr_err > APR_OS_START_USEERR) && (tmp_err->apr_err <= APR_OS_START_CANONERR)) { err_string = svn_strerror(tmp_err->apr_err, errbuf, sizeof(errbuf)); } else if ((tmp_err2 = svn_utf_cstring_from_utf8(&err_string, apr_strerror(tmp_err->apr_err, errbuf, sizeof(errbuf)), tmp_err->pool))) { svn_error_clear(tmp_err2); err_string = "Can't recode error string from APR"; } if (!str.empty()) { str += ", "; } str += std::string(err_string); APR_ARRAY_PUSH(empties, apr_status_t) = tmp_err->apr_err; } } tmp_err = tmp_err->child; } apr_pool_destroy(pool); return str; } void SvnConnection::init() { svn_error_t *err; if ((err = svn_fs_initialize(pool))) { throw PEX(strerr(err)); } if ((err = svn_ra_initialize(pool))) { throw PEX(strerr(err)); } if ((err = svn_config_ensure(NULL, pool))) { throw PEX(strerr(err)); } } // Handles the prefetching of diffstats class SvnDiffstatPrefetcher { public: SvnDiffstatPrefetcher(SvnConnection *connection, int n = 4) { Logger::info() << "SubversionBackend: Using " << n << " threads for prefetching diffstats" << endl; for (int i = 0; i < n; i++) { SvnDiffstatThread *thread = new SvnDiffstatThread(connection, &m_queue); thread->start(); m_threads.push_back(thread); } } ~SvnDiffstatPrefetcher() { for (unsigned int i = 0; i < m_threads.size(); i++) { delete m_threads[i]; } } void stop() { m_queue.stop(); } void wait() { for (unsigned int i = 0; i < m_threads.size(); i++) { m_threads[i]->wait(); } } void prefetch(const std::vector &revisions) { m_queue.put(revisions); } bool get(const std::string &revision, Diffstat *dest) { return m_queue.getResult(revision, dest); } bool willFetch(const std::string &revision) { return m_queue.hasArg(revision); } private: JobQueue m_queue; std::vector m_threads; }; // Static mutex for reading and writing log interavals sys::parallel::Mutex SubversionBackend::SvnLogIterator::s_cacheMutex; // Constructor SubversionBackend::SvnLogIterator::SvnLogIterator(SubversionBackend *backend, const std::string &prefix, uint64_t startrev, uint64_t endrev) : Backend::LogIterator(), m_backend(backend), d(new SvnConnection()), m_prefix(prefix), m_startrev(startrev), m_endrev(endrev), m_index(0), m_finished(false), m_failed(false) { d->open(m_backend->d); } // Desctructor SubversionBackend::SvnLogIterator::~SvnLogIterator() { delete d; } // Returns the next revision IDs, or an empty vector bool SubversionBackend::SvnLogIterator::nextIds(std::queue *queue) { m_mutex.lock(); while (m_index >= m_ids.size() && !m_finished) { m_cond.wait(&m_mutex); } if (m_failed) { throw PEX("Error fetching server log"); } if (m_index == m_ids.size()) { return false; } while (m_index < m_ids.size()) { queue->push(m_ids[m_index++]); } m_mutex.unlock(); return true; } struct logReceiverBaton { sys::parallel::Mutex *mutex; sys::parallel::WaitCondition *cond; std::vector temp; std::vector *ids; uint64_t latest; }; // Subversion callback for log messages static svn_error_t *logReceiver(void *baton, svn_log_entry_t *entry, apr_pool_t *) { logReceiverBaton *b = static_cast(baton); b->latest = entry->revision; b->temp.push_back(str::itos(b->latest)); if (b->temp.size() > 64) { b->mutex->lock(); for (size_t i = 0; i < b->temp.size(); i++) { b->ids->push_back(b->temp[i]); } b->temp.clear(); b->cond->wakeAll(); b->mutex->unlock(); } return SVN_NO_ERROR; } // Main thread function void SubversionBackend::SvnLogIterator::run() { apr_pool_t *pool = svn_pool_create(d->pool); apr_pool_t *iterpool = svn_pool_create(pool); apr_array_header_t *path = apr_array_make(pool, 1, sizeof (const char *)); std::string sessionPrefix = d->prefix; if (m_prefix.empty()) { APR_ARRAY_PUSH(path, const char *) = svn_path_canonicalize(sessionPrefix.c_str(), pool); } else if (sessionPrefix.empty()) { APR_ARRAY_PUSH(path, const char *) = svn_path_canonicalize(m_prefix.c_str(), pool); } else { APR_ARRAY_PUSH(path, const char *) = svn_path_canonicalize((sessionPrefix+"/"+m_prefix).c_str(), pool); } apr_array_header_t *props = apr_array_make(pool, 1, sizeof (const char *)); // Intentionally empty int windowSize = 1024; if (!strncmp(d->url, "file://", strlen("file://"))) { windowSize = 0; } PDEBUG << "Path is " << APR_ARRAY_IDX(path, 0, const char *) << ", range is [" << m_startrev << ":" << m_endrev << "]" << endl; // This is a vector of intervals that should be fetched. The revisions of // each interval will be appended after fetching, so it can be used to store // revisions that are already cached. std::vector fetch; fetch.push_back(Interval(m_startrev, m_endrev)); std::string cachefile = str::printf("log_%s", APR_ARRAY_IDX(path, 0, const char *)); if (m_backend->options().useCache()) { readIntervalsFromCache(cachefile); // Check if a cached interval intersects with the interval that should be fetched. It's // assumed that m_cachedIntervals doesn't contain intersecting intervals, which is assured // by the recursive merging in mergeInterval(). std::vector fetchnew = missingIntervals(m_startrev, m_endrev); if (!fetchnew.empty()) { fetch = fetchnew; } } logReceiverBaton baton; baton.mutex = &m_mutex; baton.cond = &m_cond; baton.ids = &m_ids; baton.latest = 0; // Fetch all revision intervals that are required for (size_t i = 0; i < fetch.size(); i++) { svn_pool_clear(iterpool); uint64_t wstart = fetch[i].start, lastStart = fetch[i].start; while (wstart <= fetch[i].end) { PDEBUG << "Fetching log from " << wstart << " to " << fetch[i].end << " with window size " << windowSize << endl; svn_error_t *err = svn_ra_get_log2(d->ra, path, wstart, fetch[i].end, windowSize, FALSE, FALSE /* otherwise, copy history will be ignored */, FALSE, props, &logReceiver, &baton, iterpool); if (err != NULL) { Logger::err() << "Error: Unable to fetch server log: " << SvnConnection::strerr(err) << endl; m_mutex.lock(); m_failed = true; m_finished = true; m_cond.wakeAll(); m_mutex.unlock(); svn_pool_destroy(pool); return; } if (baton.latest + 1 > lastStart) { lastStart = baton.latest + 1; } else { lastStart += std::max(windowSize, 1); } wstart = lastStart; } m_mutex.lock(); Logger &l = PTRACE << "Appending " << baton.temp.size() << " fetched revisions: "; for (size_t j = 0; j < baton.temp.size(); j++) { l << baton.temp[j] << " "; m_ids.push_back(baton.temp[j]); } l << endl; baton.temp.clear(); // Append cached revisions if (!fetch[i].revisions.empty()) { l << "Appending " << fetch[i].revisions.size() << " cached revisions: "; for (size_t j = 0; j < fetch[i].revisions.size(); j++) { l << fetch[i].revisions[j] << " "; m_ids.push_back(str::itos(fetch[i].revisions[j])); } l << endl; } m_cond.wakeAll(); m_mutex.unlock(); } m_mutex.lock(); m_finished = true; m_cond.wakeAll(); m_mutex.unlock(); if (m_backend->options().useCache()) { // Add current interval Interval current(m_startrev, m_endrev); for (size_t i = 0; i < m_ids.size(); i++) { uint64_t rev; str::stoi(m_ids[i], &rev); current.revisions.push_back(rev); } mergeInterval(current); writeIntervalsToCache(cachefile); } svn_pool_destroy(pool); } // Reads previous log intervals from the cache void SubversionBackend::SvnLogIterator::readIntervalsFromCache(const std::string &file) { sys::parallel::MutexLocker locker(&s_cacheMutex); std::string cachefile = Cache::cacheFile(m_backend, file); m_cachedIntervals.clear(); if (!sys::fs::fileExists(cachefile)) { return; } GZIStream in(cachefile); uint32_t version; in >> version; if (version != 1) { Logger::warn() << "Unknown version number in cache file " << cachefile << ": " << version << endl; return; } Interval interval; while (!(in >> interval.start).eof()) { uint64_t i = 0, num; in >> interval.end >> num; interval.revisions.resize(num); while (i < num) { in >> interval.revisions[i++]; } if (!in.ok() || interval.revisions.empty() || interval.start >= interval.end) { PTRACE << "Skipping bogus interval: [" << interval.start << ":" << interval.end << "] with " << interval.revisions.size() << " revisions" << endl; continue; } PTRACE << "New revision range: [" << interval.start << ":" << interval.end << "] with " << interval.revisions.size() << " revisions" << endl; m_cachedIntervals.push_back(interval); } if (!in.ok() && !in.eof()) { m_cachedIntervals.clear(); Logger::warn() << "Error reading from cache file " << cachefile << endl; } } // Writes the current intervals to the cache void SubversionBackend::SvnLogIterator::writeIntervalsToCache(const std::string &file) { sys::parallel::MutexLocker locker(&s_cacheMutex); std::string cachefile = Cache::cacheFile(m_backend, file); PDEBUG << "Writing log intervals to cache file " << cachefile << endl; GZOStream *out = new GZOStream(cachefile); *out << (uint32_t)1; // Version number for (size_t i = 0; i < m_cachedIntervals.size() && out->ok(); i++) { const std::vector &revisions = m_cachedIntervals[i].revisions; *out << m_cachedIntervals[i].start << m_cachedIntervals[i].end << (uint64_t)revisions.size(); for (size_t j = 0; j < revisions.size() && out->ok(); j++) { *out << revisions[j]; } } if (!out->ok()) { Logger::warn() << "Error writing to cache file: " << cachefile << endl; delete out; out = NULL; sys::fs::unlink(cachefile); } delete out; } // Merges the given interval into the intervals that have been already known void SubversionBackend::SvnLogIterator::mergeInterval(const Interval &interval) { PDEBUG << "Merging new interval [" << interval.start << ":" << interval.end << "]" << endl; for (size_t i = 0; i < m_cachedIntervals.size(); i++) { const Interval &candidate = m_cachedIntervals[i]; if ((interval.start <= candidate.start && interval.end >= candidate.start) || (interval.start <= candidate.end && interval.end >= candidate.end)) { // Intervals intersect, let's merge them. // NOTE: It's assumed that the repository is immutable, i.e. no // revisions will ever be deleted. PDEBUG << "Intervals [" << interval.start << ":" << interval.end << "] and [" << candidate.start << ":" << candidate.end << "] intersect" << endl; Interval merged(std::min(interval.start, candidate.start), std::max(interval.end, candidate.end)); merged.revisions.insert(merged.revisions.begin(), interval.revisions.begin(), interval.revisions.end()); merged.revisions.insert(merged.revisions.end(), candidate.revisions.begin(), candidate.revisions.end()); std::sort(merged.revisions.begin(), merged.revisions.end()); // Remove duplicates for (size_t j = 0; j < merged.revisions.size()-1; j++) { if (merged.revisions[j] == merged.revisions[j+1]) { merged.revisions.erase(merged.revisions.begin()+j); --j; } } // Merge the merged interval m_cachedIntervals.erase(m_cachedIntervals.begin()+i); mergeInterval(merged); return; } } PDEBUG << "No intersections" << endl; // No merge with previous intervals m_cachedIntervals.push_back(interval); } // Returns missing intervals, including following cached revision numbers std::vector SubversionBackend::SvnLogIterator::missingIntervals(uint64_t start, uint64_t end) { std::vector missing; for (size_t i = 0; i < m_cachedIntervals.size(); i++) { const Interval &candidate = m_cachedIntervals[i]; if (start >= candidate.start && end <= candidate.end ) { // Completely inside candidate interval. No need to fetch any logs from the repository, // but store the relevant revisions in a pseudo interval Interval interval(start+1, start); size_t j = 0, n = candidate.revisions.size(); while (j < n && candidate.revisions[j] < start) ++j; while (j < n && candidate.revisions[j] <= end) { interval.revisions.push_back(candidate.revisions[j]); ++j; } missing.push_back(interval); return missing; } else if (start <= candidate.start && end >= candidate.end) { // Completely including candidate interval. Two intervals need to be fetched from // the server now, but both will be splitted further. std::vector recurse = missingIntervals(start, (candidate.start == 0 ? 0 : candidate.start-1)); recurse.back().revisions.insert(recurse.back().revisions.end(), candidate.revisions.begin(), candidate.revisions.end()); missing.insert(missing.begin(), recurse.begin(), recurse.end()); recurse = missingIntervals(candidate.end+1, end); missing.insert(missing.end(), recurse.begin(), recurse.end()); return missing; } else if (start >= candidate.start && start <= candidate.end) { // Intersecting at start. Add a pseudo interval for the cached revisions // and an interval for the remaining ones after candidate.end Interval interval1(start+1, start); size_t j = 0, n = candidate.revisions.size(); while (j < n && candidate.revisions[j] < start) ++j; interval1.revisions.insert(interval1.revisions.begin(), candidate.revisions.begin() + j, candidate.revisions.end()); missing.push_back(interval1); std::vector recurse = missingIntervals(candidate.end+1, end); missing.insert(missing.end(), recurse.begin(), recurse.end()); return missing; } else if (end >= candidate.start && end <= candidate.end) { // Intersectiong at end. Add a single interval, including the first // revisions in candidate std::vector recurse = missingIntervals(start, (candidate.start == 0 ? 0 : candidate.start-1)); for (size_t j = 0; j < candidate.revisions.size() && candidate.revisions[j] <= end; j++) { recurse.back().revisions.push_back(candidate.revisions[j]); } missing.insert(missing.begin(), recurse.begin(), recurse.end()); return missing; } } // No intersections, so return the complete interval missing.push_back(Interval(start, end)); return missing; } // Constructor SubversionBackend::SubversionBackend(const Options &options) : Backend(options), d(new SvnConnection()), m_prefetcher(NULL) { } // Destructor SubversionBackend::~SubversionBackend() { close(); delete d; } // Opens the connection to the repository void SubversionBackend::init() { // Initialize the Subversion C library #ifndef WIN32 if (svn_cmdline_init(PACKAGE, stderr) != EXIT_SUCCESS) { throw PEX("Failed to initialize Subversion library"); } atexit(apr_terminate); #else // !WIN32 if (svn_cmdline_init(PACKAGE, NULL) != EXIT_SUCCESS) { throw PEX("Failed to initialize Subversion library"); } #endif // !WIN32 PDEBUG << "Subversion library initialized, opening connection" << endl; std::string url = m_opts.repository(); if (!url.compare(0, 1, "/")) { // Detect working copy apr_pool_t *pool = svn_pool_create(NULL); const char *repo; svn_error_t *err = svn_client_url_from_path(&repo, url.c_str(), pool); if (err == NULL) { url = std::string(repo); } else { // Local repository url = std::string("file://") + url; } svn_pool_destroy(pool); } d->open(url, m_opts.options()); } // Called after Report::run() void SubversionBackend::close() { // Clean up any prefetching threads finalize(); } // Returns true if this backend is able to access the given repository bool SubversionBackend::handles(const std::string &url) { const char *schemes[] = {"svn://", "svn+ssh://", "http://", "https://", "file://"}; for (unsigned int i = 0; i < sizeof(schemes)/sizeof(schemes[0]); i++) { if (str::startsWith(url, schemes[i])) { return true; } } // Local repository without the 'file://' prefix if (sys::fs::dirExists(url) && sys::fs::dirExists(url+"/locks") && sys::fs::exists(url+"/db/uuid")) { return true; } // Working copy with .svn directory here or in a parent directory std::string dir = url; while (!dir.empty()) { if (sys::fs::dirExists(dir + "/.svn")) { return true; } std::string parent = sys::fs::dirname(dir); if (parent == dir) { break; } dir = parent; } return false; } // Returns a unique identifier for this repository std::string SubversionBackend::uuid() { apr_pool_t *pool = svn_pool_create(d->pool); const char *id; svn_error_t *err = svn_ra_get_uuid2(d->ra, &id, pool); if (err != NULL) { throw PEX(SvnConnection::strerr(err)); } std::string sid(id); svn_pool_destroy(pool); return sid; } // Returns the HEAD revision for the current branch std::string SubversionBackend::head(const std::string &branch) { apr_pool_t *pool = svn_pool_create(d->pool); std::string prefix = this->prefix(branch, pool); svn_dirent_t *dirent; svn_error_t *err = svn_ra_stat(d->ra, svn_path_join(d->prefix, prefix.c_str(), pool), SVN_INVALID_REVNUM, &dirent, pool); if (err == NULL && dirent == NULL && branch == "trunk") { err = svn_ra_stat(d->ra, "", SVN_INVALID_REVNUM, &dirent, pool); } if (err != NULL) { throw PEX(SvnConnection::strerr(err)); } if (dirent == NULL) { throw PEX(std::string("Unable to determine HEAD revision of ")+d->url+"/"+prefix); } svn_revnum_t rev = dirent->created_rev; PDEBUG << "Head revision for branch " << prefix << " is " << rev << endl; svn_pool_destroy(pool); return str::itos((long int)rev); } // Returns the standard branch (i.e., trunk) std::string SubversionBackend::mainBranch() { return "trunk"; } // Returns a list of available branches std::vector SubversionBackend::branches() { std::string prefix = m_opts.value("branches", "branches"); std::vector branches; apr_pool_t *pool = svn_pool_create(d->pool); char *absPrefix = svn_path_join(d->prefix, prefix.c_str(), pool); // Check if branches directoy is present svn_dirent_t *dirent; svn_error_t *err = svn_ra_stat(d->ra, absPrefix, SVN_INVALID_REVNUM, &dirent, pool); if (err != NULL) { throw PEX(SvnConnection::strerr(err)); } if (dirent == NULL || dirent->kind != svn_node_dir) { // It's not here; let's call the main branch "trunk" branches.push_back("trunk"); return branches; } // Get directory entries apr_hash_t *dirents; err = svn_ra_get_dir2(d->ra, &dirents, NULL, NULL, absPrefix, dirent->created_rev, SVN_DIRENT_KIND, pool); if (err != NULL) { throw PEX(SvnConnection::strerr(err)); } branches.push_back("trunk"); for (apr_hash_index_t *hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi)) { const char *entry; svn_dirent_t *dirent; apr_hash_this(hi, (const void **)(void *)&entry, NULL, (void **)(void *)&dirent); if (dirent->kind == svn_node_dir) { branches.push_back(std::string(entry)); } } // Let's be nice std::sort(branches.begin()+1, branches.end()); svn_pool_destroy(pool); return branches; } // Returns a list of available tags std::vector SubversionBackend::tags() { std::string prefix = m_opts.value("tags", "tags"); std::vector tags; apr_pool_t *pool = svn_pool_create(d->pool); char *absPrefix = svn_path_join(d->prefix, prefix.c_str(), pool); // Check if tags directoy is present svn_dirent_t *dirent; svn_error_t *err = svn_ra_stat(d->ra, absPrefix, SVN_INVALID_REVNUM, &dirent, pool); if (err != NULL) { throw PEX(SvnConnection::strerr(err)); } if (dirent == NULL || dirent->kind != svn_node_dir) { // No tags here return tags; } // Get directory entries apr_hash_t *dirents; err = svn_ra_get_dir2(d->ra, &dirents, NULL, NULL, absPrefix, dirent->created_rev, SVN_DIRENT_KIND | SVN_DIRENT_CREATED_REV, pool); if (err != NULL) { throw PEX(SvnConnection::strerr(err)); } apr_array_header_t *array = svn_sort__hash(dirents, &svn_sort_compare_items_lexically, pool); for (int i = 0; i < array->nelts; i++) { svn_sort__item_t *item = &APR_ARRAY_IDX(array, i, svn_sort__item_t); svn_dirent_t *dirent = (svn_dirent_t *)apr_hash_get(dirents, item->key, item->klen); if (dirent->kind == svn_node_dir) { tags.push_back(Tag(str::itos(dirent->created_rev), (const char *)item->key)); } } svn_pool_destroy(pool); return tags; } // Returns a diffstat for the specified revision Diffstat SubversionBackend::diffstat(const std::string &id) { if (m_prefetcher && m_prefetcher->willFetch(id)) { PTRACE << "Revision " << id << " will be prefetched" << endl; Diffstat stat; if (!m_prefetcher->get(id, &stat)) { throw PEX(std::string("Failed to retrieve diffstat for revision ") + id); } return stat; } std::vector revs = str::split(id, ":"); svn_revnum_t r1, r2; if (revs.size() > 1) { if (!str::stoi(revs[0], &r1) || !str::stoi(revs[1], &r2)) { throw PEX(std::string("Error parsing revision number ") + id); } } else { if (!str::stoi(revs[0], &r2)) { throw PEX(std::string("Error parsing revision number ") + id); } r1 = r2 - 1; } PDEBUG << "Fetching revision " << id << " manually" << endl; apr_pool_t *pool = svn_pool_create(d->pool); Diffstat stat = SvnDiffstatThread::diffstat(d, r1, r2, pool); svn_pool_destroy(pool); return stat; } // Filters a diffstat by prefix void SubversionBackend::filterDiffstat(Diffstat *stat) { // Strip prefix if (d->prefix && strlen(d->prefix)) { stat->filter(d->prefix); } } // Returns a file listing for the given revision (defaults to HEAD) std::vector SubversionBackend::tree(const std::string &id) { svn_revnum_t revision; if (id.empty()) { revision = SVN_INVALID_REVNUM; } else if (!str::stoi(id, &revision)) { throw PEX(std::string("Error parsing revision number ") + id); } apr_pool_t *pool = svn_pool_create(d->pool); apr_pool_t *iterpool = svn_pool_create(pool); std::vector contents; // Pseudo-recursively list directory entries std::stack > stack; stack.push(std::pair(d->prefix, svn_node_dir)); while (!stack.empty()) { svn_pool_clear(iterpool); std::string node = stack.top().first; if (stack.top().second != svn_node_dir) { contents.push_back(node); stack.pop(); continue; } stack.pop(); PDEBUG << "Listing directory contents in " << node << "@" << revision << endl; apr_hash_t *dirents; svn_error_t *err = svn_ra_get_dir2(d->ra, &dirents, NULL, NULL, node.c_str(), revision, SVN_DIRENT_KIND, iterpool); if (err != NULL) { throw PEX(SvnConnection::strerr(err)); } std::string prefix = (node.empty() ? "" : node + "/"); std::stack > next; apr_array_header_t *array = svn_sort__hash(dirents, &svn_sort_compare_items_lexically, iterpool); for (int i = 0; i < array->nelts; i++) { svn_sort__item_t *item = &APR_ARRAY_IDX(array, i, svn_sort__item_t); svn_dirent_t *dirent = (svn_dirent_t *)apr_hash_get(dirents, item->key, item->klen); if (str::endsWith(node, (const char *)item->key) || str::endsWith(d->url, (const char *)item->key)) { continue; } if (dirent->kind == svn_node_file || dirent->kind == svn_node_dir) { next.push(std::pair(prefix + (const char *)item->key, dirent->kind)); } } while (!next.empty()) { stack.push(next.top()); next.pop(); } } svn_pool_destroy(pool); return contents; } // Returns the file contents of the given path at the given revision (defaults to HEAD) std::string SubversionBackend::cat(const std::string &path, const std::string &id) { svn_revnum_t revision; if (id.empty()) { revision = SVN_INVALID_REVNUM; } else if (!str::stoi(id, &revision)) { throw PEX(std::string("Error parsing revision number ") + id); } apr_pool_t *pool = svn_pool_create(d->pool); svn_stringbuf_t *buf = svn_stringbuf_create("", pool); svn_stream_t *stream = svn_stream_from_stringbuf(buf, pool); svn_error_t *err = svn_ra_get_file(d->ra, path.c_str(), revision, stream, NULL, NULL, pool); if (err != NULL) { throw PEX(SvnConnection::strerr(err)); } // Subversion 1.6 offers this in svn_string_from_stream(), but for 1.5 we // must copy the string manually. svn_stringbuf_t *work = svn_stringbuf_create("", pool); char *buffer = (char *)apr_palloc(pool, SVN__STREAM_CHUNK_SIZE); while (1) { apr_size_t len = SVN__STREAM_CHUNK_SIZE; if ((err = svn_stream_read(stream, buffer, &len))) { throw PEX(SvnConnection::strerr(err)); } svn_stringbuf_appendbytes(work, buffer, len); if (len < SVN__STREAM_CHUNK_SIZE) { break; } } if ((err = svn_stream_close(stream))) { throw PEX(SvnConnection::strerr(err)); } std::string content = std::string(work->data, work->len); svn_pool_destroy(pool); return content; } // Returns a log iterator for the given branch Backend::LogIterator *SubversionBackend::iterator(const std::string &branch, int64_t start, int64_t end) { // Check if the branch exists apr_pool_t *pool = svn_pool_create(d->pool); std::string prefix = this->prefix(branch, pool);; svn_dirent_t *dirent; svn_error_t *err = svn_ra_stat(d->ra, svn_path_join(d->prefix, prefix.c_str(), pool), SVN_INVALID_REVNUM, &dirent, pool); if (err != NULL) { throw PEX(SvnConnection::strerr(err)); } if (dirent == NULL) { if (prefix == "trunk") { prefix.clear(); } else { throw PEX(str::printf("No such branch: %s", branch.c_str())); } } svn_revnum_t startrev = 0, endrev; apr_time_t time; if (start >= 0) { apr_time_ansi_put(&time, start); if ((err = svn_ra_get_dated_revision(d->ra, &startrev, time, pool)) != NULL) { throw PEX(SvnConnection::strerr(err)); } // startrev has been set to the HEAD revision at the given start time, but // we're interested in all revisions after this date. ++startrev; } if (end >= 0) { apr_time_ansi_put(&time, end); if ((err = svn_ra_get_dated_revision(d->ra, &endrev, time, pool)) != NULL) { throw PEX(SvnConnection::strerr(err)); } } else { str::stoi(head(branch), &endrev); } PDEBUG << "Revision range: [ " << start << " : " << end << "] -> [" << startrev << " : " << endrev << "]" << endl; svn_pool_destroy(pool); return new SvnLogIterator(this, prefix, startrev, endrev); } // Adds the given revision IDs to the diffstat scheduler void SubversionBackend::prefetch(const std::vector &ids) { if (m_prefetcher == NULL) { std::string numthreads = m_opts.value("threads", "10"); int nthreads = 10; if (!str::stoi(numthreads, &nthreads)) { throw PEX(std::string("Expected number for --threads parameter: ") + numthreads); } if (nthreads == 0) { return; } // Don't use that many threads for local repositories if (!strncmp(d->url, "file://", strlen("file://"))) { nthreads = std::max(1, sys::parallel::idealThreadCount() / 2); } m_prefetcher = new SvnDiffstatPrefetcher(d, nthreads); } m_prefetcher->prefetch(ids); } // Handle cleanup of diffstat scheduler void SubversionBackend::finalize() { if (m_prefetcher) { m_prefetcher->stop(); m_prefetcher->wait(); delete m_prefetcher; m_prefetcher = NULL; } } // Prints a help screen void SubversionBackend::printHelp() const { Options::print("--username=ARG", "Specify a username ARG"); Options::print("--password=ARG", "Specify a password ARG"); Options::print("--no-auth-cache", "Do not cache authentication tokens"); Options::print("--non-interactive", "Do no interactive prompting"); Options::print("--trunk=ARG", "Trunk is at subdirectory ARG"); Options::print("--branches=ARG", "Branches are in subdirectory ARG"); Options::print("--tags=ARG", "Tags are in subdirectory ARG"); Options::print("--threads=ARG", "Use ARG threads for requesting diffstats"); } // Returns the prefix for the given branch std::string SubversionBackend::prefix(const std::string &branch, apr_pool_t *pool) { std::string p; if (branch == "trunk") { p = m_opts.value("trunk", "trunk"); } else if (!branch.empty()) { p = m_opts.value("branches", "branches"); p += "/"; p += branch; } PDEBUG << "Iterator requested for branch " << branch << " -> prefix = " << p << endl; return std::string(svn_path_canonicalize(p.c_str(), pool)); } // Returns the revision data for the given ID Revision *SubversionBackend::revision(const std::string &id) { std::map data; std::string rev = str::split(id, ":").back(); svn_revnum_t revnum; if (!str::stoi(rev, &(revnum))) { throw PEX(std::string("Error parsing revision number ") + id); } apr_pool_t *pool = svn_pool_create(d->pool); apr_hash_t *props; svn_error_t *err = svn_ra_rev_proplist(d->ra, revnum, &props, pool); if (err != NULL) { throw PEX(SvnConnection::strerr(err)); } svn_string_t *value; std::string author, message; int64_t date = 0; if ((value = static_cast(apr_hash_get(props, "svn:author", APR_HASH_KEY_STRING)))) { author = value->data; } if ((value = static_cast(apr_hash_get(props, "svn:date", APR_HASH_KEY_STRING)))) { apr_time_t when; if ((err = svn_time_from_cstring(&when, value->data, pool)) != NULL) { throw PEX(SvnConnection::strerr(err)); } date = apr_time_sec(when); } if ((value = static_cast(apr_hash_get(props, "svn:log", APR_HASH_KEY_STRING)))) { message = value->data; } svn_pool_destroy(pool); return new Revision(id, date, author, message, diffstat(id)); } pepper-0.3.2/src/tag.h0000644000175000001440000000144711721276751011456 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: tag.h * Tag representation (interface) */ #ifndef TAG_H_ #define TAG_H_ #include #include "lunar/lunar.h" class Tag { public: Tag(); Tag(const std::string &id, const std::string &name); std::string id() const; std::string name() const; private: std::string m_id; std::string m_name; // Lua binding public: Tag(lua_State *L); int id(lua_State *L); int name(lua_State *L); static const char className[]; static Lunar::RegType methods[]; }; #endif // TAG_H_ pepper-0.3.2/src/logger.cpp0000644000175000001440000000312511721276751012510 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: logger.cpp * Logging facility class (implementation) */ #include "main.h" #include "logger.h" // Static variables Logger Logger::s_instances[Logger::NumLevels] = { Logger(Logger::None, std::cerr), Logger(Logger::Error, std::cerr), Logger(Logger::Warn, std::cerr), Logger(Logger::Status, std::cerr), Logger(Logger::Info, std::cerr), Logger(Logger::Debug, std::cerr), Logger(Logger::Trace, std::cerr) }; #ifdef DEBUG int Logger::s_level = Logger::Debug; #else int Logger::s_level = Logger::None; #endif sys::parallel::Mutex Logger::s_mutex; // Constructor Logger::Logger(int level, std::ostream &out) : m_level(level), m_out(&out) { } // Sets the output device for the logger instances void Logger::setOutput(std::ostream &out, int level) { if (level < 0) { for (int i = 0; i < Logger::NumLevels; i++) { if (i != Logger::Error && i != Logger::Warn) { s_instances[i].m_out = &out; } } } else if (level != Logger::Error && level != Logger::Warn) { s_instances[level].m_out = &out; } } // Sets the log level void Logger::setLevel(int level) { s_level = std::max((int)Logger::Warn, level); } // Returns the log level int Logger::level() { return s_level; } // Flushes all loggers void Logger::flush() { for (int i = 0; i < Logger::NumLevels; i++) { s_instances[i].m_out->flush(); } } pepper-0.3.2/src/report.h0000644000175000001440000000406611721276751012216 00000000000000/* * pepper - SCM statistics report generator * Copyright (C) 2010-2012 Jonas Gehring * * Released under the GNU General Public License, version 3. * Please see the COPYING file in the source distribution for license * terms and conditions, or see http://www.gnu.org/licenses/. * * file: report.h * Report script context (interface) */ #ifndef REPORT_H_ #define REPORT_H_ #include #include #include #include #include "lunar/lunar.h" class Backend; class Repository; // Report context class Report { public: struct MetaData { struct Option { std::string synopsis; std::string description; Option(const std::string &synopsis, const std::string &description) : synopsis(synopsis), description(description) { } }; std::string name; std::string description; std::vector