Sprout/tools/files/filegraph.cpp
2014-06-05 16:44:19 +09:00

417 lines
13 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*=============================================================================
Copyright (c) 2011-2014 Bolero MURAKAMI
https://github.com/bolero-MURAKAMI/Sprout
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <utility>
#include <iterator>
#include <algorithm>
#include <boost/wave.hpp>
#include <boost/wave/preprocessing_hooks.hpp>
#include <boost/wave/cpplexer/cpp_lex_token.hpp>
#include <boost/wave/cpplexer/cpp_lex_iterator.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/depth_first_search.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/filesystem.hpp>
#include <boost/range.hpp>
#include <boost/algorithm/string.hpp>
#include "../sprig/algorithm/string.hpp"
#include "../sprig/graph/depth_first_search.hpp"
class include_graph_hooks
: public boost::wave::context_policies::default_preprocessing_hooks
{
private:
typedef boost::wave::context_policies::default_preprocessing_hooks base_type;
public:
// ƒOƒ‰ƒt̃m<C692>[ƒh
struct node_type {
boost::filesystem::path absolute;
boost::filesystem::path filename;
bool has_include_guard;
public:
node_type(boost::filesystem::path const& absolute, boost::filesystem::path const& filename)
: absolute(absolute), filename(filename), has_include_guard(false)
{}
operator boost::filesystem::path const&() const {
return filename;
}
operator std::string() const {
return filename.generic_string();
}
friend bool operator==(node_type const& lhs, node_type const& rhs) {
return lhs.absolute == rhs.absolute;
}
friend bool operator==(node_type const& lhs, boost::filesystem::path const& rhs) {
return lhs.absolute == rhs;
}
friend bool operator==(boost::filesystem::path const& lhs, node_type const& rhs) {
return lhs == rhs.absolute;
}
friend bool operator!=(node_type const& lhs, node_type const& rhs) {
return !(lhs == rhs);
}
friend bool operator!=(node_type const& lhs, boost::filesystem::path const& rhs) {
return !(lhs == rhs);
}
friend bool operator!=(boost::filesystem::path const& lhs, node_type const& rhs) {
return !(lhs == rhs);
}
template<typename Elem, typename Traits>
friend std::basic_ostream<Elem, Traits>& operator<<(std::basic_ostream<Elem, Traits>& lhs, node_type const& rhs) {
return lhs << rhs.filename.generic_string();
}
};
typedef std::pair<int, int> edge_type;
typedef boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::bidirectionalS,
boost::no_property
> graph_type;
private:
std::vector<node_type> node_list_;
std::vector<edge_type> edge_list_;
std::vector<int> current_list_;
int current_;
public:
explicit include_graph_hooks(boost::filesystem::path const& start)
: node_list_{node_type{boost::filesystem::absolute(start), start.filename()}}
, edge_list_{}
, current_list_{0}
, current_(0)
{}
std::vector<node_type> const&
nodes() const {
return node_list_;
}
std::vector<edge_type> const&
edges() const {
return edge_list_;
}
// ƒOƒ‰ƒt<C692><EFBFBD>¬
template<typename Graph>
Graph make_graph() const {
return Graph(edge_list_.begin(), edge_list_.end(), node_list_.size());
}
graph_type make_graph() const {
return make_graph<graph_type>();
}
// graphviz <20>o—Í
void write_graphviz(std::ostream& out) const {
boost::write_graphviz(
out,
make_graph<graph_type>(),
boost::make_label_writer(&node_list_[0])
);
}
// ”ñƒCƒ“ƒNƒ<C692>[ƒhƒK<C692>[ƒhŒŸ<C592>o
template<typename OutputIterator>
void collect_no_include_guard_files(OutputIterator result) const {
std::copy_if(
node_list_.begin() + 1, node_list_.end(), result,
[](node_type const& e) { return !e.has_include_guard; }
);
}
// <20>zŠÂƒCƒ“ƒNƒ<C692>[ƒhŒŸ<C592>o
template<typename OutputIterator>
void collect_circulated_includes(OutputIterator result) const {
boost::depth_first_search(
make_graph<graph_type>(),
boost::visitor(sprig::make_back_edge_recorder(result))
);
}
// ŒÇ—§ƒtƒ@ƒCƒŒŸ<C592>o
template<typename OutputIterator>
void collect_isolated_files(OutputIterator result, boost::filesystem::path const& path) const {
typedef boost::filesystem::recursive_directory_iterator iterator;
for (auto it = iterator(path), last = iterator(); it != last; ++it) {
if (!boost::filesystem::is_directory(*it)) {
boost::filesystem::path abspath(boost::filesystem::absolute(*it));
auto found = std::find(node_list_.begin(), node_list_.end(), abspath);
if (found == node_list_.end()) { // ƒCƒ“ƒNƒ<C692>[ƒh³êĢȢÈçÎŒ‰ÊÉljÁ
*result++ = abspath.generic_string();
}
}
}
}
public:
// ƒCƒ“ƒNƒ<C692>[ƒhƒtƒ@ƒCƒƒpƒX<C692>Ýè<E28099>ˆ<CB86>ðƒtƒbƒN
template<typename Context>
bool locate_include_file(
Context& ctx,
std::string& file_path,
bool is_system,
char const* current_name,
std::string& dir_path,
std::string& native_name
)
{
std::string filename = is_system ? ('<' + file_path + '>') : ('\"' + file_path + '\"');
if (!base_type::locate_include_file(ctx, file_path, is_system, current_name, dir_path, native_name)) {
return false;
}
dir_path = filename; // ƒCƒ“ƒNƒ<C692>[ƒhƒfƒBƒŒƒNƒeƒBƒũeƒLƒXƒgÅ<E2809A>ã<EFBFBD>«
return true;
}
// ƒCƒ“ƒNƒ<C692>[ƒhƒtƒ@ƒCƒ‰ð<E280B0>ÍŠJŽnðƒtƒbƒN
template<typename Context>
void opened_include_file(
Context const& /*ctx*/,
std::string const& relname,
std::string const& absname,
bool /*is_system_include*/
)
{
boost::filesystem::path abspath(boost::filesystem::absolute(absname));
auto found = std::find(node_list_.begin(), node_list_.end(), abspath);
auto to = std::distance(node_list_.begin(), found);
if (found == node_list_.end()) { // <20>Å<EFBFBD>̃Cƒ“ƒNƒ<C692>[ƒhÈç΃m<C692>[ƒhÉljÁ
node_list_.emplace_back(abspath, relname);
}
edge_list_.emplace_back(current_list_.back(), to);
current_list_.push_back(to); // ƒJƒŒƒ“ƒgð<E2809A>X<EFBFBD>V
current_ = to;
}
// ƒCƒ“ƒNƒ<C692>[ƒhƒtƒ@ƒCƒ‰ð<E280B0>ÍŠ®—¹ðƒtƒbƒN
template<typename Context>
void returning_from_include_file(Context const& /*ctx*/) {
current_ = current_list_.back();
current_list_.pop_back(); // ƒJƒŒƒ“ƒgðß·
}
// ƒCƒ“ƒNƒ<C692>[ƒhƒK<C692>[ƒhŒŸ<C592>oðƒtƒbƒN
template<typename Context>
void detected_include_guard(
Context const& /*ctx*/,
std::string const& /*filename*/,
std::string const& /*include_guard*/
)
{
node_list_.at(current_).has_include_guard = true;
}
template<typename Context, typename Token>
void detected_pragma_once(
Context const& /*ctx*/,
Token const& /*pragma_token*/,
std::string const& /*filename*/
)
{
node_list_.at(current_).has_include_guard = true;
}
};
// ƒVƒXƒeƒ€ƒCƒ“ƒNƒ<C692>[ƒhƒpƒX̎擾
template<typename OutputIterator>
void collect_sysinclude_paths(OutputIterator result, std::string const& command = "g++") {
{
std::ofstream ofs("_collect_sysinclude_paths.cpp");
}
std::system((command + " -v -E _collect_sysinclude_paths.cpp 1> /dev/null 2> _collect_sysinclude_paths").c_str());
{
std::ifstream ifs("_collect_sysinclude_paths");
std::string text(
std::istreambuf_iterator<char>(ifs.rdbuf()),
std::istreambuf_iterator<char>()
);
auto rng = boost::make_iterator_range(text);
rng = sprig::find_skip(rng, boost::algorithm::first_finder("#include <...>")); // ƒCƒ“ƒNƒ<C692>[ƒhƒpƒXÌŽn“_ÜŃXƒLƒbƒv
rng = sprig::find_skip(rng, boost::algorithm::first_finder("\n"));
while (boost::algorithm::starts_with(rng, " ")) {
auto found = sprig::find_between(
rng,
boost::algorithm::token_finder(boost::algorithm::is_space(), boost::algorithm::token_compress_on),
boost::algorithm::first_finder("\n")
);
*result++ = std::string(boost::begin(found), boost::end(found));
rng = boost::make_iterator_range(boost::end(found), boost::end(rng));
rng = sprig::find_skip(rng, boost::algorithm::first_finder("\n"));
}
}
}
int main(int argc, const char* argv[]) {
std::string src;
std::string text;
if (argc >= 2) {
src = argv[1];
// ƒtƒ@ƒCƒÌ“à—eðS•” text É“ÇÝ<E2809A>žÞ
std::ifstream ifs(argv[1]);
text = std::string(
std::istreambuf_iterator<char>(ifs.rdbuf()),
std::istreambuf_iterator<char>()
);
}
try {
// ƒvƒŠƒvƒ<76>ƒZƒbƒTð—pˆÓ
typedef boost::wave::context<
std::string::iterator,
boost::wave::cpplexer::lex_iterator<boost::wave::cpplexer::lex_token<> >,
boost::wave::iteration_context_policies::load_file_to_string,
::include_graph_hooks
> context_type;
context_type ctx(text.begin(), text.end(), src.c_str(), ::include_graph_hooks(src));
// ƒ‰ƒ“ƒQ<C692>[ƒWÌ<E2809A>Ýè
ctx.set_language(
boost::wave::language_support(
boost::wave::support_cpp11
| boost::wave::support_option_include_guard_detection // ƒCƒ“ƒNƒ<C692>[ƒhƒK<C692>[ƒhŒŸ<C592>o
)
);
// ƒCƒ“ƒNƒ<C692>[ƒhƒpƒXÌ<E2809A>Ýè
{
std::vector<std::string> list;
::collect_sysinclude_paths(std::back_inserter(list));
std::cout
<< "sysinclude paths :\n"
;
for (auto&& e : list) {
std::cout
<< " " << e << "\n"
;
ctx.add_sysinclude_path(e.c_str());
}
std::cout
<< std::flush
;
}
if (!src.empty()) {
// ƒvƒŠƒvƒ<76>ƒZƒXðç¹é
for (auto&& e : ctx) {
//std::cout << e.get_value();
}
}
for ( ; ;) {
std::cout
<< "> "
<< std::flush
;
std::string line;
std::getline(std::cin, line);
if (line.empty()) {
break;
}
std::vector<std::string> tokens;
boost::algorithm::split(tokens, line, boost::algorithm::is_space());
if (tokens.at(0) == "find") {
// ƒCƒ“ƒNƒ<C692>[ƒhƒtƒ@ƒCƒðŒŸ<C592>õ
if (tokens.size() < 2) {
std::cout
<< "missing parameter.\n"
<< std::flush
;
continue;
}
std::cout
<< "find <" << tokens.at(1) << "> :\n"
;
std::string filepath(tokens.at(1));
std::string dirpath;
if (!ctx.find_include_file(filepath, dirpath, true, 0)) {
std::cout
<< " not found\n"
<< std::flush
;
continue;
}
std::cout
<< " " << filepath << "\n"
<< std::flush
;
} else if (tokens.at(0) == "graph") {
// ƒOƒ‰ƒt<C692>o—Í
std::cout
<< "graph output > out.graph.dot\n"
;
std::ofstream ofs("out.graph.dot");
ctx.get_hooks().write_graphviz(ofs);
std::cout
<< std::flush
;
} else if (tokens.at(0) == "noguard") {
// ”ñƒCƒ“ƒNƒ<C692>[ƒhƒK<C692>[ƒhð<E2809A>o—Í
std::vector<::include_graph_hooks::node_type> list;
ctx.get_hooks().collect_no_include_guard_files(std::back_inserter(list));
std::cout
<< "no include guarde files (" << list.size() << ") :\n"
;
for (auto const& e : list) {
std::cout
<< " " << e << "\n"
;
}
std::cout
<< std::flush
;
} else if (tokens.at(0) == "circulated") {
// <20>zŠÂƒCƒ“ƒNƒ<C692>[ƒhð<E2809A>o—Í
std::vector<typename boost::graph_traits<::include_graph_hooks::graph_type>::edge_descriptor> list;
ctx.get_hooks().collect_circulated_includes(std::back_inserter(list));
auto g = ctx.get_hooks().make_graph();
std::cout
<< "circulated includes (" << list.size() << ") :\n"
;
for (auto const& e : list) {
std::cout
<< " " << boost::source(e, g) << "[" << ctx.get_hooks().nodes()[boost::source(e, g)] << "]\n"
<< " -> " << boost::target(e, g) << "[" << ctx.get_hooks().nodes()[boost::target(e, g)] << "]\n"
;
}
std::cout
<< std::flush
;
} else if (tokens.at(0) == "isolated") {
// Sprout ̌Ǘ§ƒtƒ@ƒCƒð<E2809A>o—Í
std::cout
<< "isolated files output > out.isolated.txt\n"
;
std::ofstream ofs("out.isolated.txt");
// Sprout ̃VƒXƒeƒ€ƒCƒ“ƒNƒ<C692>|ƒhƒpƒXðŽæ“¾
std::string filepath("sprout/config.hpp");
std::string dirpath;
if (!ctx.find_include_file(filepath, dirpath, true, 0)) {
std::cerr
<< "#error sprout not found\n"
<< std::flush
;
continue;
}
dirpath = boost::filesystem::path(filepath).parent_path().generic_string();
// ƒŠƒXƒg<C692>o—Í
std::vector<std::string> list;
ctx.get_hooks().collect_isolated_files(std::back_inserter(list), dirpath);
std::sort(list.begin(), list.end());
std::copy(list.begin(), list.end(), std::ostream_iterator<std::string>(ofs, "\n"));
std::cout
<< std::flush
;
} else {
std::cout
<< "invalid command\n"
<< std::flush
;
}
}
} catch (boost::wave::cpp_exception& e) {
// ƒvƒŠƒvƒ<76>ƒZƒXŃGƒ‰<C692>[”­<E2809D>
std::cerr
<< "#error " << e.file_name() << "(" << e.line_no() << "):" << e.description() << "\n"
<< std::flush
;
}
}