#include #include #include #include #include #include #include #include #include #include #include "JSystem/JSystemToolkit.hh" #include "JLang/JLangToolkit.hh" #include "Jeep/JParser.hh" #include "Jeep/JMessage.hh" namespace { static const char WILDCARD = '%'; //!< file name wild card static const char* const INCLUDE_DIRECTIVE = "#include"; //!< C++ include directive static const char* const NAMESPACE_DIRECTIVE = "namespace"; //!< C++ namespace directive static const char* const INCLUDE_EXTENSION = ".hh"; //!< Jpp include file extension static const char* const SOFTWARE_DIRECTORY = "software"; //!< Jpp software directory static const std::set rm = //!< discard sub-directories { "JMarkov" }; /** * Auxiliary class to list dependencies of Jpp include files. */ struct JDirectory : public std::map > { static const char JPP_PREFIX = 'J'; //!< Jpp file name prefix static const char DIRECTORY_SEPARATOR = '/'; //!< sub-directory /** * Auxiliary data structure for directory path. */ struct path_type : public std::vector { /** * Check validity of path. * * \param key key * \return true if valid; else false */ bool is_valid(const key_type& key) const { for (const_iterator p = this->begin(); p != this->end(); ++p) { if (key == *p) { return false; } for (const_iterator q = this->begin(); q != p; ++q) { if (*p == *q) { return false; } } } return true; } }; /** * Default constructor. * * \param dir top-level directory */ JDirectory(const std::string& dir = "") { using namespace std; using namespace JPP; const string JPP_DIR = (dir != "" ? dir : getenv("JPP_DIR")); const string TOP = JPP_DIR + DIRECTORY_SEPARATOR + SOFTWARE_DIRECTORY; for (const auto& top : ls(TOP)) { if (top[0] == JPP_PREFIX && rm.count(top) == 0) { const string SUB = TOP + DIRECTORY_SEPARATOR + top; for (const auto& file : ls(SUB)) { if (file.find(INCLUDE_EXTENSION) != string::npos) { const string file_name = SUB + DIRECTORY_SEPARATOR + file; ifstream in(file_name.c_str()); for (string buffer; getline(in, buffer) && buffer.find(NAMESPACE_DIRECTIVE) == string::npos; ) { if (buffer.find(INCLUDE_DIRECTIVE) != string::npos) { istringstream is(buffer); string key, include_file; is >> key >> include_file; if (is_double_quoted(include_file) && include_file[1] == JPP_PREFIX) { const size_t pos = include_file.find(DIRECTORY_SEPARATOR); if (pos != string::npos) { const string dir = include_file.substr(1, pos - 1); if (dir != top) { (*this)[top].insert(dir); } } } } } in.close(); } } } } } /** * Copy constructor. * * \param input directory * \param key key */ JDirectory(const JDirectory& input, const key_type& key) { this->copy(input, key); } /** * Get mapped data for given key. * * \param key key * \return mapped data */ const mapped_type& get(const key_type& key) const { static const mapped_type buffer; const_iterator p = this->find(key); if (p != this->end()) return p->second; else return buffer; } /** * Get circular dependency for given key. * * \param key key * \return path */ path_type getPath(const key_type& key) const { path.clear(); evaluate(key, get(key)); return path; } /** * Check recursively mapped data for given key. * * \param key key * \return true if valid; else false */ bool is_valid(const key_type& key) const { path.clear(); return evaluate(key, get(key)); } /** * Print data conform tikz format. * * \param out output stream */ inline void print(std::ostream& out) const { using namespace std; for (const_iterator i = this->begin(); i != this->end(); ++i) { out << i->first << " -> {"; for (const auto& dir : i->second) { out << " " << dir << ","; } out << "}," << endl; } } /** * Print data conform tikz format. * * \param out output stream * \param key key */ inline void print(std::ostream& out, const JDirectory::key_type& key) const { JDirectory(*this, key).print(out); } private: /** * Recursive copy of input data. * * \param input directory * \param key key */ void copy(const JDirectory& input, const key_type& key) { (*this)[key] = input.get(key); for (const auto& dir : input.get(key)) { if (this->find(dir) == this->end()) { copy(input, dir); } } } /** * Check recursively mapped data for given key. * * \param key key * \param target target data * \return true if valid; else false */ bool evaluate(const key_type& key, const mapped_type& target) const { if (!path.is_valid(key)) { // premature end return false; } for (mapped_type::const_iterator i = target.begin(); i != target.end(); ++i) { path.push_back(*i); // grow if (!evaluate(key, this->get(*i))) { return false; } path.pop_back(); // shrink } return true; // normal end } mutable path_type path; // internal buffer }; } /** * \file * Example program to test dependencies of include files. * \author mdejong */ int main(int argc, char **argv) { using namespace std; using namespace JPP; string outputFile; string jpp; string dir; int debug; try { JParser<> zap("Example program to test dependencies of include files"); zap['o'] = make_field(outputFile) = ""; zap['J'] = make_field(jpp) = ""; zap['D'] = make_field(dir) = ""; zap['d'] = make_field(debug) = 3; zap(argc, argv); } catch(const exception &error) { FATAL(error.what() << endl); } const JDirectory directory(jpp); if (outputFile != "") { const size_t pos = outputFile.find(WILDCARD); if (pos != string::npos) { outputFile.replace(pos, 1, (dir != "" ? dir.c_str() : "Jpp")); } ofstream out(outputFile.c_str()); out << "\\documentclass[border=5mm]{standalone}" << endl; out << "" << endl; out << "\\usepackage{tikz}" << endl; out << "\\usetikzlibrary{graphs, graphdrawing}" << endl; out << "\\usegdlibrary{layered}" << endl; out << "\\begin{document}" << endl; out << "" << endl; out << "\\tikz \\graph [layered layout]" << endl; out << "{" << endl; if (dir == "") directory.print(out); else directory.print(out, dir); out << "};" << endl; out << "" << endl; out << "\\end{document}" << endl; out.close(); } for (JDirectory::const_iterator i = directory.begin(); i != directory.end(); ++i) { DEBUG(i->first << ": " << flush); if (!directory.is_valid(i->first)) { const JDirectory::path_type path = directory.getPath(i->first); for (JDirectory::path_type::const_iterator dir = path.begin(); dir != path.end(); ++dir) { DEBUG(' ' << *dir); } } DEBUG(endl); } for (JDirectory::const_iterator i = directory.begin(); i != directory.end(); ++i) { ASSERT(directory.is_valid(i->first), "check validity " << i->first); } return 0; }