Last active
August 29, 2015 14:02
-
-
Save bolero-MURAKAMI/737a3087c9587a21b890 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 <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/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; | |
// グラフのノード | |
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::no_property | |
> 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} | |
{} | |
template<typename Graph> | |
Graph make_graph() const { | |
return Graph(edge_list_.begin(), edge_list_.end(), node_list_.size()); | |
} | |
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_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.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