blob: 00d77512fb5297f13929cb1421013ae9f748f28b [file] [log] [blame]
/*=============================================================================
Boost.Wave: A Standard compliant C++ preprocessor library
http://www.boost.org/
Copyright (c) 2001-2011 Hartmut Kaiser. 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)
=============================================================================*/
#if !defined(CPP_INCLUDE_PATHS_HPP_AF620DA4_B3D2_4221_AD91_8A1ABFFB6944_INCLUDED)
#define CPP_INCLUDE_PATHS_HPP_AF620DA4_B3D2_4221_AD91_8A1ABFFB6944_INCLUDED
#include <string>
#include <list>
#include <utility>
#include <boost/wave/wave_config.hpp>
#include <boost/wave/util/filesystem_compatibility.hpp>
#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#endif
#if BOOST_WAVE_SERIALIZATION != 0
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/collections_save_imp.hpp>
#include <boost/serialization/collections_load_imp.hpp>
#include <boost/serialization/split_free.hpp>
#endif
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
// this must occur after all of the includes and before any code appears
#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_PREFIX
#endif
///////////////////////////////////////////////////////////////////////////////
namespace boost { namespace wave { namespace util {
#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
///////////////////////////////////////////////////////////////////////////////
// Tags for accessing both sides of a bidirectional map
struct from {};
struct to {};
///////////////////////////////////////////////////////////////////////////////
// The class template bidirectional_map wraps the specification
// of a bidirectional map based on multi_index_container.
template<typename FromType, typename ToType>
struct bidirectional_map
{
typedef std::pair<FromType, ToType> value_type;
#if defined(BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS) || \
(defined(BOOST_MSVC) && \
( (BOOST_MSVC < 1300) || (BOOST_MSVC == 1600) )) || \
(defined(BOOST_INTEL_CXX_VERSION) && \
(defined(_MSC_VER) && (BOOST_INTEL_CXX_VERSION <= 700)))
BOOST_STATIC_CONSTANT(unsigned, from_offset = offsetof(value_type, first));
BOOST_STATIC_CONSTANT(unsigned, to_offset = offsetof(value_type, second));
typedef boost::multi_index::multi_index_container<
value_type,
boost::multi_index::indexed_by<
boost::multi_index::ordered_unique<
boost::multi_index::tag<from>,
boost::multi_index::member_offset<value_type, FromType, from_offset>
>,
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<to>,
boost::multi_index::member_offset<value_type, ToType, to_offset>
>
>
> type;
#else
typedef boost::multi_index::multi_index_container<
value_type,
boost::multi_index::indexed_by<
boost::multi_index::ordered_unique<
boost::multi_index::tag<from>,
boost::multi_index::member<value_type, FromType, &value_type::first>
>,
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<to>,
boost::multi_index::member<value_type, ToType, &value_type::second>
>
>
> type;
#endif
};
#endif // BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
#if BOOST_WAVE_SERIALIZATION != 0
struct load_filepos
{
static unsigned int get_line() { return 0; }
static unsigned int get_column() { return 0; }
static std::string get_file() { return "<loading-state>"; }
};
#endif
///////////////////////////////////////////////////////////////////////////////
//
// include_paths - controlling the include path search order
//
// General notes:
//
// Any directories specified with the 'add_include_path()' function before
// the function 'set_sys_include_delimiter()' is called are searched only
// for the case of '#include "file"' directives, they are not searched for
// '#include <file>' directives. If additional directories are specified
// with the 'add_include_path()' function after a call to the function
// 'set_sys_include_delimiter()', these directories are searched for all
// '#include' directives.
//
// In addition, a call to the function 'set_sys_include_delimiter()'
// inhibits the use of the current directory as the first search directory
// for '#include "file"' directives. Therefore, the current directory is
// searched only if it is requested explicitly with a call to the function
// 'add_include_path(".")'.
//
// Calling both functions, the 'set_sys_include_delimiter()' and
// 'add_include_path(".")' allows you to control precisely which
// directories are searched before the current one and which are searched
// after.
//
///////////////////////////////////////////////////////////////////////////////
class include_paths
{
private:
typedef std::list<std::pair<boost::filesystem::path, std::string> >
include_list_type;
typedef include_list_type::value_type include_value_type;
#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
typedef bidirectional_map<std::string, std::string>::type
pragma_once_set_type;
#endif
public:
include_paths()
: was_sys_include_path(false),
current_dir(initial_path()),
current_rel_dir(initial_path())
{}
bool add_include_path(char const *path_, bool is_system = false)
{
return add_include_path(path_, (is_system || was_sys_include_path) ?
system_include_paths : user_include_paths);
}
void set_sys_include_delimiter() { was_sys_include_path = true; }
bool find_include_file (std::string &s, std::string &dir, bool is_system,
char const *current_file) const;
void set_current_directory(char const *path_);
boost::filesystem::path get_current_directory() const
{ return current_dir; }
protected:
bool find_include_file (std::string &s, std::string &dir,
include_list_type const &pathes, char const *) const;
bool add_include_path(char const *path_, include_list_type &pathes_);
private:
include_list_type user_include_paths;
include_list_type system_include_paths;
bool was_sys_include_path; // saw a set_sys_include_delimiter()
boost::filesystem::path current_dir;
boost::filesystem::path current_rel_dir;
#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
public:
bool has_pragma_once(std::string const &filename)
{
using namespace boost::multi_index;
return get<from>(pragma_once_files).find(filename) != pragma_once_files.end();
}
bool add_pragma_once_header(std::string const &filename,
std::string const& guard_name)
{
typedef pragma_once_set_type::value_type value_type;
return pragma_once_files.insert(value_type(filename, guard_name)).second;
}
bool remove_pragma_once_header(std::string const& guard_name)
{
typedef pragma_once_set_type::index_iterator<to>::type to_iterator;
typedef std::pair<to_iterator, to_iterator> range_type;
range_type r = pragma_once_files.get<to>().equal_range(guard_name);
if (r.first != r.second) {
using namespace boost::multi_index;
get<to>(pragma_once_files).erase(r.first, r.second);
return true;
}
return false;
}
private:
pragma_once_set_type pragma_once_files;
#endif
#if BOOST_WAVE_SERIALIZATION != 0
public:
BOOST_STATIC_CONSTANT(unsigned int, version = 0x10);
BOOST_STATIC_CONSTANT(unsigned int, version_mask = 0x0f);
private:
friend class boost::serialization::access;
template<typename Archive>
void save(Archive & ar, const unsigned int version) const
{
using namespace boost::serialization;
#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
ar & make_nvp("pragma_once_files", pragma_once_files);
#endif
ar & make_nvp("user_include_paths", user_include_paths);
ar & make_nvp("system_include_paths", system_include_paths);
ar & make_nvp("was_sys_include_path", was_sys_include_path);
}
template<typename Archive>
void load(Archive & ar, const unsigned int loaded_version)
{
using namespace boost::serialization;
if (version != (loaded_version & ~version_mask)) {
BOOST_WAVE_THROW(preprocess_exception, incompatible_config,
"cpp_include_path state version", load_filepos());
return;
}
#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
ar & make_nvp("pragma_once_files", pragma_once_files);
#endif
// verify that the old include paths match the current ones
include_list_type user_paths, system_paths;
ar & make_nvp("user_include_paths", user_paths);
ar & make_nvp("system_include_paths", system_paths);
if (user_paths != user_include_paths)
{
BOOST_WAVE_THROW(preprocess_exception, incompatible_config,
"user include paths", load_filepos());
return;
}
if (system_paths != system_include_paths)
{
BOOST_WAVE_THROW(preprocess_exception, incompatible_config,
"system include paths", load_filepos());
return;
}
ar & make_nvp("was_sys_include_path", was_sys_include_path);
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
#endif
};
///////////////////////////////////////////////////////////////////////////////
// Add an include path to one of the search lists (user include path or system
// include path).
inline
bool include_paths::add_include_path (
char const *path_, include_list_type &pathes_)
{
namespace fs = boost::filesystem;
if (path_) {
fs::path newpath = util::complete_path(create_path(path_), current_dir);
if (!fs::exists(newpath) || !fs::is_directory(newpath)) {
// the given path does not form a name of a valid file system directory
// item
return false;
}
pathes_.push_back (include_value_type(newpath, path_));
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
// Find an include file by traversing the list of include directories
inline
bool include_paths::find_include_file (std::string &s, std::string &dir,
include_list_type const &pathes, char const *current_file) const
{
namespace fs = boost::filesystem;
typedef include_list_type::const_iterator const_include_list_iter_t;
const_include_list_iter_t it = pathes.begin();
const_include_list_iter_t include_paths_end = pathes.end();
#if BOOST_WAVE_SUPPORT_INCLUDE_NEXT != 0
if (0 != current_file) {
// re-locate the directory of the current file (#include_next handling)
// #include_next does not distinguish between <file> and "file"
// inclusion, nor does it check that the file you specify has the same
// name as the current file. It simply looks for the file named, starting
// with the directory in the search path after the one where the current
// file was found.
fs::path file_path (create_path(current_file));
for (/**/; it != include_paths_end; ++it) {
fs::path currpath (create_path((*it).first.string()));
if (std::equal(currpath.begin(), currpath.end(), file_path.begin()))
{
++it; // start searching with the next directory
break;
}
}
}
#endif
for (/**/; it != include_paths_end; ++it) {
fs::path currpath (create_path(s));
if (!currpath.has_root_directory()) {
currpath = create_path((*it).first.string());
currpath /= create_path(s); // append filename
}
if (fs::exists(currpath)) {
fs::path dirpath (create_path(s));
if (!dirpath.has_root_directory()) {
dirpath = create_path((*it).second);
dirpath /= create_path(s);
}
dir = dirpath.string();
s = normalize(currpath).string(); // found the required file
return true;
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
// Find an include file by searching the user and system includes in the
// correct sequence (as it was configured by the user of the driver program)
inline bool
include_paths::find_include_file (std::string &s, std::string &dir,
bool is_system, char const *current_file) const
{
namespace fs = boost::filesystem;
// if not system include (<...>), then search current directory first
if (!is_system) {
if (!was_sys_include_path) { // set_sys_include_delimiter() not called
// first have a look at the current directory
fs::path currpath (create_path(s));
if (!currpath.has_root_directory()) {
currpath = create_path(current_dir.string());
currpath /= create_path(s);
}
if (fs::exists(currpath) && 0 == current_file) {
// if 0 != current_path (#include_next handling) it can't be
// the file in the current directory
fs::path dirpath (create_path(s));
if (!dirpath.has_root_directory()) {
dirpath = create_path(current_rel_dir.string());
dirpath /= create_path(s);
}
dir = dirpath.string();
s = normalize(currpath).string(); // found in local directory
return true;
}
// iterate all user include file directories to find the file
if (find_include_file(s, dir, user_include_paths, current_file))
return true;
// ... fall through
}
else {
// if set_sys_include_delimiter() was called, then user include files
// are searched in the user search path only
return find_include_file(s, dir, user_include_paths, current_file);
}
// if nothing found, fall through
// ...
}
// iterate all system include file directories to find the file
return find_include_file (s, dir, system_include_paths, current_file);
}
///////////////////////////////////////////////////////////////////////////////
// Set current directory from a given file name
inline bool
as_relative_to(boost::filesystem::path const& path,
boost::filesystem::path const& base, boost::filesystem::path& result)
{
if (path.has_root_path()) {
if (path.root_path() == base.root_path())
return as_relative_to(path.relative_path(), base.relative_path(), result);
result = path; // that's our result
}
else {
if (base.has_root_path()) {
// cannot find relative path from a relative path and a rooted base
return false;
}
else {
typedef boost::filesystem::path::const_iterator path_iterator;
path_iterator path_it = path.begin();
path_iterator base_it = base.begin();
while (path_it != path.end() && base_it != base.end() ) {
if (*path_it != *base_it)
break;
++path_it; ++base_it;
}
for (/**/; base_it != base.end(); ++base_it)
result /= "..";
for (/**/; path_it != path.end(); ++path_it)
result /= *path_it;
}
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
inline
void include_paths::set_current_directory(char const *path_)
{
namespace fs = boost::filesystem;
fs::path filepath (create_path(path_));
fs::path filename = util::complete_path(filepath, current_dir);
if (fs::exists(filename) && fs::is_directory(filename)) {
current_rel_dir.clear();
if (!as_relative_to(filepath, current_dir, current_rel_dir))
current_rel_dir = filepath;
current_dir = filename;
}
else {
current_rel_dir.clear();
if (!as_relative_to(branch_path(filepath), current_dir, current_rel_dir))
current_rel_dir = branch_path(filepath);
current_dir = branch_path(filename);
}
}
///////////////////////////////////////////////////////////////////////////////
}}} // namespace boost::wave::util
#if BOOST_WAVE_SERIALIZATION != 0
///////////////////////////////////////////////////////////////////////////////
namespace boost { namespace serialization {
///////////////////////////////////////////////////////////////////////////////
// Serialization support for boost::filesystem::path
template<class Archive>
inline void save (Archive & ar, boost::filesystem::path const& p,
const unsigned int /* file_version */)
{
using namespace boost::serialization;
std::string path_str(p.native_file_string());
ar & make_nvp("filepath", path_str);
}
template<class Archive>
inline void load (Archive & ar, boost::filesystem::path &p,
const unsigned int /* file_version */)
{
using namespace boost::serialization;
std::string path_str;
ar & make_nvp("filepath", path_str);
p = wave::util::create_path(path_str);
}
// split non-intrusive serialization function member into separate
// non intrusive save/load member functions
template<class Archive>
inline void serialize (Archive & ar, boost::filesystem::path &p,
const unsigned int file_version)
{
boost::serialization::split_free(ar, p, file_version);
}
///////////////////////////////////////////////////////////////////////////////
// Serialization support for the used multi_index
template<class Archive>
inline void save (Archive & ar,
const typename boost::wave::util::bidirectional_map<
std::string, std::string
>::type &t,
const unsigned int /* file_version */)
{
boost::serialization::stl::save_collection<
Archive,
typename boost::wave::util::bidirectional_map<
std::string, std::string
>::type
>(ar, t);
}
template<class Archive>
inline void load (Archive & ar,
typename boost::wave::util::bidirectional_map<std::string, std::string>::type &t,
const unsigned int /* file_version */)
{
typedef typename boost::wave::util::bidirectional_map<
std::string, std::string
>::type map_type;
boost::serialization::stl::load_collection<
Archive, map_type,
boost::serialization::stl::archive_input_unique<Archive, map_type>,
boost::serialization::stl::no_reserve_imp<map_type>
>(ar, t);
}
// split non-intrusive serialization function member into separate
// non intrusive save/load member functions
template<class Archive>
inline void serialize (Archive & ar,
typename boost::wave::util::bidirectional_map<
std::string, std::string
>::type &t,
const unsigned int file_version)
{
boost::serialization::split_free(ar, t, file_version);
}
///////////////////////////////////////////////////////////////////////////////
}} // namespace boost::serialization
BOOST_CLASS_VERSION(boost::wave::util::include_paths,
boost::wave::util::include_paths::version);
#endif // BOOST_WAVE_SERIALIZATION != 0
// the suffix header occurs after all of the code
#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_SUFFIX
#endif
#endif // !defined(CPP_INCLUDE_PATHS_HPP_AF620DA4_B3D2_4221_AD91_8A1ABFFB6944_INCLUDED)