Last active
August 29, 2015 14:02
-
-
Save bolero-MURAKAMI/1a0070870c6965aaf3e9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*============================================================================= | |
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 <boost/range/functions.hpp> | |
#include <boost/range/metafunctions.hpp> | |
#include <boost/range/iterator_range.hpp> | |
#include <boost/algorithm/string/find.hpp> | |
namespace sprig { | |
// | |
// skip_finder | |
// | |
template<typename Finder> | |
struct skip_finderF { | |
private: | |
Finder finder_; | |
public: | |
explicit skip_finderF(Finder const& finder) | |
: finder_(finder) | |
{} | |
template<typename ForwardIterator> | |
boost::iterator_range<ForwardIterator> | |
operator()(ForwardIterator first, ForwardIterator last) const { | |
return boost::iterator_range<ForwardIterator>(boost::end(finder_(first, last)), last); | |
} | |
}; | |
template<typename Finder> | |
inline sprig::skip_finderF<Finder> | |
skip_finder(Finder const& finder) { | |
return sprig::skip_finderF<Finder>(finder); | |
} | |
// | |
// find_skip | |
// | |
template<typename Range, typename Finder> | |
inline boost::iterator_range<typename boost::range_iterator<Range>::type> | |
find_skip(Range& input, Finder const& finder) { | |
return boost::algorithm::find(input, sprig::skip_finder(finder)); | |
} | |
// | |
// skip_backward_finder | |
// | |
template<typename Finder> | |
struct skip_backward_finderF { | |
private: | |
Finder finder_; | |
public: | |
explicit skip_backward_finderF(Finder const& finder) | |
: finder_(finder) | |
{} | |
template<typename ForwardIterator> | |
boost::iterator_range<ForwardIterator> | |
operator()(ForwardIterator first, ForwardIterator last) const { | |
return boost::iterator_range<ForwardIterator>(first, boost::begin(finder_(first, last))); | |
} | |
}; | |
template<typename Finder> | |
inline sprig::skip_backward_finderF<Finder> | |
skip_backward_finder(Finder const& finder) { | |
return sprig::skip_backward_finderF<Finder>(finder); | |
} | |
// | |
// find_skip_backward | |
// | |
template<typename Range, typename Finder> | |
inline boost::iterator_range<typename boost::range_iterator<Range>::type> | |
find_skip_backward(Range& input, Finder const& finder) { | |
return boost::algorithm::find(input, sprig::skip_backward_finder(finder)); | |
} | |
// | |
// between_finder | |
// | |
template<typename Finder1, typename Finder2> | |
struct between_finderF { | |
private: | |
Finder1 finder1_; | |
Finder2 finder2_; | |
public: | |
between_finderF(Finder1 const& finder1, Finder2 const& finder2) | |
: finder1_(finder1), finder2_(finder2) | |
{} | |
template<typename ForwardIterator> | |
boost::iterator_range<ForwardIterator> | |
operator()(ForwardIterator first, ForwardIterator last) const { | |
first = boost::end(finder1_(first, last)); | |
return boost::iterator_range<ForwardIterator>(first, boost::begin(finder2_(first, last))); | |
} | |
}; | |
template<typename Finder1, typename Finder2> | |
inline sprig::between_finderF<Finder1, Finder2> | |
between_finder(Finder1 const& finder1, Finder2 const& finder2) { | |
return sprig::between_finderF<Finder1, Finder2>(finder1, finder2); | |
} | |
// | |
// find_between | |
// | |
template<typename Range, typename Finder1, typename Finder2> | |
inline boost::iterator_range<typename boost::range_iterator<Range>::type> | |
find_between(Range& input, Finder1 const& finder1, Finder2 const& finder2) { | |
return boost::algorithm::find(input, sprig::between_finder(finder1, finder2)); | |
} | |
} // namespace sprig | |
#include <boost/graph/depth_first_search.hpp> | |
namespace sprig { | |
// | |
// back_edge_recorder | |
// | |
template<typename OutputIterator> | |
struct back_edge_recorder | |
: public boost::default_dfs_visitor | |
{ | |
private: | |
OutputIterator out_; | |
public: | |
explicit back_edge_recorder(OutputIterator out) | |
: out_(out) | |
{} | |
template<typename Edge, typename Graph> | |
void back_edge(Edge const& e, Graph const&) { | |
*out_++ = e; | |
} | |
}; | |
// | |
// make_back_edge_recorder | |
// | |
template<typename OutputIterator > | |
inline sprig::back_edge_recorder<OutputIterator> | |
make_back_edge_recorder(OutputIterator out) { | |
return sprig::back_edge_recorder<OutputIterator>(out); | |
} | |
} // namespace sprig | |
#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> | |
class include_graph_hooks | |
: public boost::wave::context_policies::default_preprocessing_hooks | |
{ | |
private: | |
typedef boost::wave::context_policies::default_preprocessing_hooks base_type; | |
public: | |
// グラフのノード | |
struct node_type { | |
boost::filesystem::path absolute; | |
boost::filesystem::path filename; | |
public: | |
node_type(boost::filesystem::path const& absolute, boost::filesystem::path const& filename) | |
: absolute(absolute) | |
, filename(filename) | |
{} | |
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; | |
} | |
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::property<boost::vertex_name_t, std::string> | |
> graph_type; | |
private: | |
std::vector<node_type> node_list_; | |
std::vector<edge_type> edge_list_; | |
std::vector<int> current_; | |
public: | |
explicit include_graph_hooks(boost::filesystem::path const& start) | |
: node_list_{node_type{boost::filesystem::absolute(start), start.filename()}} | |
, current_{0} | |
{} | |
std::vector<node_type> const& | |
nodes() const { | |
return node_list_; | |
} | |
std::vector<edge_type> const& | |
edges() const { | |
return edge_list_; | |
} | |
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>(); | |
} | |
void write_graphviz(std::ostream& out) const { | |
boost::write_graphviz( | |
out, | |
make_graph<graph_type>(), | |
boost::make_label_writer(&node_list_[0]) | |
); | |
} | |
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)) | |
); | |
} | |
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()) { // インクルードされていないならば結果に追加 | |
*result++ = abspath.generic_string(); | |
} | |
} | |
} | |
} | |
public: | |
// インクルードファイルパス設定処理をフック | |
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; // インクルードディレクティブのテキストで上書き | |
return true; | |
} | |
// インクルードファイル解析開始をフック | |
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()) { // 最初のインクルードならばノードに追加 | |
node_list_.emplace_back(abspath, relname); | |
} | |
edge_list_.emplace_back(current_.back(), to); | |
current_.push_back(to); // カレントを更新 | |
} | |
// インクルードファイル解析完了をフック | |
template<typename Context> | |
void returning_from_include_file(Context const& /*ctx*/) { | |
current_.pop_back(); // カレントを戻す | |
} | |
}; | |
// システムインクルードパスの取得 | |
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 <...>")); // インクルードパスの始点までスキップ | |
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[]) { | |
if (argc < 2) { | |
std::cerr | |
<< "#error missing parameter.\n" | |
<< std::flush | |
; | |
return 0; | |
} | |
// ファイルの内容を全部 text に読み込む | |
std::ifstream ifs(argv[1]); | |
std::string text( | |
std::istreambuf_iterator<char>(ifs.rdbuf()), | |
std::istreambuf_iterator<char>() | |
); | |
std::string const src(argv[1]); | |
try { | |
// プリプロセッサを用意 | |
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)); | |
// ランゲージの設定 | |
ctx.set_language( | |
boost::wave::language_support( | |
boost::wave::support_cpp11 | |
| boost::wave::support_option_include_guard_detection // インクルードガード検出 | |
) | |
); | |
// インクルードパスの設定 | |
{ | |
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 | |
; | |
} | |
// プリプロセスを走らせる | |
for (auto&& e : ctx) { | |
// std::cout << e.get_value(); | |
} | |
for ( ; ;) { | |
std::string line; | |
std::cout | |
<< "> " | |
<< std::flush | |
; | |
std::getline(std::cin, line); | |
if (line == "graph") { | |
// グラフ出力 | |
std::cout | |
<< "graph output > out.graph.dot\n" | |
<< std::flush | |
; | |
std::ofstream ofs("out.graph.dot"); | |
ctx.get_hooks().write_graphviz(ofs); | |
} else if (line == "isolated") { | |
// Sprout の孤立ファイルを出力 | |
std::cout | |
<< "isolated output > out.isolated.txt\n" | |
<< std::flush | |
; | |
std::ofstream ofs("out.isolated.txt"); | |
// Sprout のシステムインクル-ドパスを取得 | |
std::string dirpath; | |
std::string filepath("sprout/config.hpp"); | |
if (!ctx.find_include_file(filepath, dirpath, true, 0)) { | |
std::cerr | |
<< "#error sprout not found\n" | |
<< std::flush | |
; | |
break; | |
} | |
dirpath = boost::filesystem::path(filepath).parent_path().generic_string(); | |
// リスト出力 | |
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")); | |
} else if (line == "circulated") { | |
// 循環インクルードを出力 | |
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(); | |
if (!list.empty()) { | |
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 { | |
std::cout | |
<< "circulated includes not found\n" | |
<< std::flush | |
; | |
} | |
} else if (line.empty()) { | |
break; | |
} else { | |
std::cout | |
<< "invalid command\n" | |
<< std::flush | |
; | |
} | |
} | |
} catch (boost::wave::cpp_exception& e) { | |
// プリプロセスでエラー発生 | |
std::cerr | |
<< "#error " << e.file_name() << "(" << e.line_no() << "):" << e.description() << "\n" | |
<< std::flush | |
; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment