From 5b146a3c729eb567889a1367244e170b1a43dc37 Mon Sep 17 00:00:00 2001 From: Hartmut Kaiser Date: Wed, 7 Oct 2009 00:04:14 +0000 Subject: [PATCH] Wave: merging from trunk [SVN r56629] --- ChangeLog | 9 +- doc/class_reference_ctxpolicy.html | 96 +++++++++++++++---- doc/class_reference_filepos.html | 18 ++-- doc/wave_driver.html | 17 +++- include/boost/wave/cpp_context.hpp | 14 ++- include/boost/wave/preprocessing_hooks.hpp | 82 +++++++++++++++- include/boost/wave/util/cpp_include_paths.hpp | 2 +- include/boost/wave/util/cpp_iterator.hpp | 18 ++-- include/boost/wave/util/file_position.hpp | 14 +-- include/boost/wave/util/interpret_pragma.hpp | 12 +-- test/testwave/collect_hooks_information.hpp | 83 ++++++++++++++++ test/testwave/testfiles/t_2_009.cpp | 1 + test/testwave/testfiles/t_2_019.cpp | 61 ++++++++++++ test/testwave/testfiles/t_2_019_001.hpp | 11 +++ test/testwave/testfiles/t_2_019_002.hpp | 20 ++++ test/testwave/testfiles/t_2_019_003.hpp | 20 ++++ test/testwave/testfiles/t_9_016.cpp | 1 + test/testwave/testfiles/test.cfg | 1 + tool/cpp.cpp | 33 ++++++- tool/trace_macro_expansion.hpp | 69 ++++++++++++- 20 files changed, 516 insertions(+), 66 deletions(-) create mode 100644 test/testwave/testfiles/t_2_019.cpp create mode 100644 test/testwave/testfiles/t_2_019_001.hpp create mode 100644 test/testwave/testfiles/t_2_019_002.hpp create mode 100644 test/testwave/testfiles/t_2_019_003.hpp diff --git a/ChangeLog b/ChangeLog index e49ba94..4eb45ea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,8 +16,6 @@ TODO (known issues): - Fix the trigraph backslash problem in the re2c (C/C++ and IDL) scanners, if there is the end of the (internal) buffer just in between a '??/' and a '\n'. -- Do a sourceforge release. - ------------------------------------------------------------------------------- CHANGELOG @@ -37,6 +35,13 @@ Boost V1.41.0 (default is cout). - Fixed compilation problems caused by recent changes to the multi_pass iterator from Spirit V2.1. +- Added a new preprocessing hooks detected_pragma_once() and + detected_include_guard() which are getting called whenever either a #pragma + once has been detected or if the include guard heuristics detected an + include guard for a particular include file. +- Added a new command line option to the wave driver tool: --listguards/-g + allowing to trace all include files which are either contain a #pragma once + or contain include guards. Boost V1.40.0 - V2.0.2 diff --git a/doc/class_reference_ctxpolicy.html b/doc/class_reference_ctxpolicy.html index 6af63bc..06082f6 100644 --- a/doc/class_reference_ctxpolicy.html +++ b/doc/class_reference_ctxpolicy.html @@ -4,6 +4,11 @@ The Context Policy + @@ -56,21 +61,33 @@ template <typename ContextT, typename ExceptionT>
void throw_exception(ContextT const &ctx,
ExceptionT const& e);

// test, whether a given token may be skipped
template <typename ContextT>
boolmay_skip_whitespace (ContextT const& ctx,
TokenT &token, bool &skipped_newline);
// Conditional compilation template <
typename ContextT, typename TokenT,
typename ContainerT
>
boolevaluated_conditional_expression(
ContextT const &ctx, TokenT const& directive,
ContainerT const& expression, bool expression_value);
- template <typename ContextT, typename TokenT>
voidskipped_token(ContextT const &ctx,
TokenT const& token);

template <typename ContextT, typename TokenT>
TokenT const& generated_token(ContextT const &ctx,
TokenT const& token);

// macro expansion tracing + template <typename ContextT, typename TokenT>
void skipped_token(ContextT const &ctx,
TokenT const& token);

template <typename ContextT, typename TokenT>
TokenT const& generated_token(ContextT const &ctx,
TokenT const& token);

// macro expansion tracing template < - typename ContextT, typename TokenT, typename ContainerT,
typename IteratorT
>
bool expanding_function_like_macro(
ContextT const &ctx, TokenT const &macrodef,
std::vector<TokenT> const &formal_args,
ContainerT const &definition, TokenT const &macrocall,
std::vector<ContainerT> const &arguments,
IteratorT const &seqstart, Iterator const &seqend);

template <typename ContextT, typename TokenT, typename ContainerT>
bool expanding_object_like_macro(
ContextT const &ctx, TokenT const &macro,
ContainerT const &definition, TokenT const &macrocall);

template <typename ContextT, typename ContainerT>
void expanded_macro(ContextT const &ctx,
ContainerT const &result);

template <typename ContextT, typename ContainerT>
void rescanned_macro(ContextT const &ctx,
ContainerT const &result);

// include file tracing functions - template <typename ContextT>
bool found_include_directive(ContextT const &ctx,
std::string const &filename, bool include_next);

template <typename ContextT>
void opened_include_file(ContextT const &ctx,
std::string const &relname, std::string const& absname,
bool is_system_include);

template <typename ContextT>
void returning_from_include_file(ContextT const &ctx);

// interpretation of #pragma's of the form + typename
ContextT, typename TokenT, typename ContainerT,
typename IteratorT
>
boolexpanding_function_like_macro(
ContextT const &ctx, TokenT const &macrodef,
std::vector<TokenT> const &formal_args,
ContainerT const &definition, TokenT const &macrocall,
std::vector<ContainerT> const &arguments,
IteratorT const &seqstart, Iterator const &seqend);

template <typename ContextT, typename TokenT, typename ContainerT>
boolexpanding_object_like_macro(
ContextT const &ctx, TokenT const &macro,
ContainerT const &definition, TokenT const &macrocall);

template <typename ContextT, typename ContainerT>
voidexpanded_macro(ContextT const &ctx,
ContainerT const &result);

template <typename ContextT, typename ContainerT>
voidrescanned_macro(ContextT const &ctx,
ContainerT const &result);

+ // include file tracing functions + template <typename ContextT>
boolfound_include_directive(ContextT const &ctx,
std::string const &filename, bool include_next);
+ template <typename ContextT>
void opened_include_file(ContextT const &ctx,
std::string const &relname, std::string const& absname,
bool is_system_include);
+ template <typename ContextT>
voidreturning_from_include_file(ContextT const &ctx);
+ template <typename ContextT>
voiddetected_include_guard(ContextT const &ctx, + std::string const& filename, + std::string const& include_guard);
+ template <typename ContextT, typename TokenT>
voiddetected_pragma_once(ContextT const &ctx, + TokenT const& pragma_token, + std::string const& filename);

+ // interpretation of #pragma's of the form // 'wave option[(value)]' - template <typename ContextT, typename ContainerT>
boolinterpret_pragma(ContextT const &ctx, ContainerT &pending,
typename ContextT::token_type const &option,
ContainerT const &values,
typename ContextT::token_type const &pragma_token);

// macro definition hooks + template <typename ContextT, typename ContainerT>
boolinterpret_pragma(ContextT const &ctx, ContainerT &pending,
typename ContextT::token_type const &option,
ContainerT const &values,
typename ContextT::token_type const &pragma_token);

+ // macro definition hookstemplate <
typename ContextT, typename TokenT, typename ParametersT, typename DefinitionT
>
voiddefined_macro(ContextT const &ctx, TokenT const &name, - bool is_functionlike, ParametersT const &parameters,
DefinitionT const &definition, bool is_predefined);

template <typename ContextT, typename TokenT>
voidundefined_macro(ContextT const &ctx,
TokenT const &name);

// #error and #warning directive hooks + bool is_functionlike, ParametersT const &parameters,
DefinitionT const &definition, bool is_predefined);

template <typename ContextT, typename TokenT>
voidundefined_macro(ContextT const &ctx,
TokenT const &name);

+ // #error and #warning directive hookstemplate <typename ContextT, typename ContainerT>
boolfound_warning_directive(ContextT const &ctx,
ContainerT const &message);

template <typename ContextT, typename ContainerT>
boolfound_error_directive(ContextT const &ctx,
ContainerT const &message);

// #line directive hooktemplate <typename ContextT, typename ContainerT>
voidfound_line_directive(ContextT const &ctx,
ContainerT const &arguments, unsigned int line,
std::string const& filename);
};

}}} // namespace boost::wave::context_policies

Member functions

General hook functions

found_directive

-
    template <typename ContextT, typename TokenT>
bool found_directive(ContextT const& ctx, TokenT const &directive);
+
    template <typename ContextT, typename TokenT>
bool found_directive(ContextT const& ctx, TokenT const &directive);

The function found_directive is called, whenever the preprocessor has detected one of the preprocessing directives (#define, #undef, #if, #idef, #ifndef, #elif, #endif, #error, #include, #pragma or #warning) .

@@ -83,14 +100,14 @@ value is false, the directive is processed in the normal manner.

throw_exception

-
        template <typename ContextT, typename ExceptionT>
void throw_exception(ContextT const &ctx,
ExceptionT const& e);
+
    template <typename ContextT, typename ExceptionT>
void throw_exception(ContextT const &ctx,
ExceptionT const& e);

he function throw_exception is called, whenever a preprocessing exception occurs .

The ctx parameter provides a reference to the context_type used during instantiation of the preprocessing iterators by the user. 

The parameter e is the exception object containing detailed error information.

may_skipwhitespace

-
    template <typename ContextT, typename TokenT>
bool may_skip_whitespace(ContextT const& ctx, TokenT &token,
bool& skipped_newline);
+
    template <typename ContextT, typename TokenT>
bool may_skip_whitespace(ContextT const& ctx, TokenT &token,
bool& skipped_newline);

The function may_skipwhitespace will be called by the library, whenever a token is about to be returned to the calling application.

@@ -104,7 +121,8 @@

Conditional compilation hook functions

evaluated_conditional_expression

-
    template <typename ContextT, typename TokenT, typename ContainerT>
bool evaluated_conditional_expression(ContextT const& ctx,
TokenT const& directive, ContainerT const& expression,
bool
expression_value);
+
   template <typename ContextT, typename TokenT, typename ContainerT>
+    bool evaluated_conditional_expression(ContextT const& ctx, 
TokenT const& directive, ContainerT const& expression,
bool
expression_value);

The function evaluated_conditional_expression is called, whenever the preprocessor has encountered a #if, #elif, #ifdef or #ifndef directive. This hook gets passed the non-expanded conditional @@ -124,7 +142,7 @@

skipped_token

-
    template <typename ContextT, typename TokenT>
void skipped_token(ContextT const& ctx, TokenT const& token);
+
    template <typename ContextT, typename TokenT>
void skipped_token(ContextT const& ctx, TokenT const& token);

The function skipped_token is called, whenever a token is about to be skipped due to a false preprocessor condition (code fragments to be @@ -134,7 +152,7 @@

The parameter token refers to the token to be skipped.

generated_token

-
    template <typename ContextT, typename TokenT>
TokenT const& generated_token(ContextT const& ctx, TokenT const& token);
+
    template <typename ContextT, typename TokenT>
TokenT const& generated_token(ContextT const& ctx, TokenT const& token);

The function generated_token is called, whenever a token is about to be returned from the library.

@@ -148,7 +166,7 @@

Macro expansion tracking functions

expanding_function_like_macro

-
    template <typename ContextT, typename TokenT, typename ContainerT>
bool expanding_function_like_macro(
ContextT const& ctx, TokenT const &macrodef,
std::vector<TokenT> const &formal_args,
ContainerT const &definition, TokenT const &macrocall,
std::vector<ContainerT> const &arguments,
IteratorT const &seqstart, Iterator const &seqend);
+
   template <typename ContextT, typename TokenT, typename ContainerT>
bool expanding_function_like_macro(
ContextT const& ctx, TokenT const &macrodef,
std::vector<TokenT> const &formal_args,
ContainerT const &definition, TokenT const &macrocall,
std::vector<ContainerT> const &arguments,
IteratorT const &seqstart, Iterator const &seqend);

The function expanding_function_like_macro is called, whenever a function-like macro is to be expanded, i.e. before the actual expansion @@ -181,7 +199,7 @@

expanding_object_like_macro

-
    template <typename ContextT, typename TokenT, typename ContainerT>
bool expanding_object_like_macro(
ContextT const& ctx, TokenT const &macro,
ContainerT const &definition, TokenT const &macrocall);
+
    template <typename ContextT, typename TokenT, typename ContainerT>
bool expanding_object_like_macro(
ContextT const& ctx, TokenT const &macro,
ContainerT const &definition, TokenT const &macrocall);

The function expanding_object_like_macro is called, whenever a object-like @@ -201,7 +219,7 @@

expanded_macro

-
    template <typename ContextT, typename ContainerT>
void expanded_macro(ContextT const& ctx, ContainerT const &result);
+
    template <typename ContextT, typename ContainerT>
void expanded_macro(ContextT const& ctx, ContainerT const &result);

The function expanded_macro is called whenever the expansion of @@ -213,7 +231,7 @@ so far. This is a standard STL container containing the generated token sequence.

rescanned_macro

-
    template <typename ContextT, typename ContainerT>
void rescanned_macro(ContextT const& ctx, ContainerT const &result);
+
    template <typename ContextT, typename ContainerT>
void rescanned_macro(ContextT const& ctx, ContainerT const &result);

The function rescanned_macro is called whenever the rescanning @@ -226,7 +244,7 @@

Include file tracing functions

found_include_directive

-
    template <typename ContextT>
bool found_include_directive(ContextT const& ctx,
std::string const &filename, bool include_next);
+
    template <typename ContextT>
bool found_include_directive(ContextT const& ctx,
std::string const &filename, bool include_next);

The function found_include_directive is called whenever whenever a #include directive was located..

@@ -246,7 +264,7 @@

opened_include_file

-
    template <typename ContextT>
void opened_include_file(ContextT const& ctx,
std::string const &rel_filename, std::string const &abs_filename,
bool is_system_include);
+
    template <typename ContextT>
void opened_include_file(ContextT const& ctx,
std::string const &rel_filename, std::string const &abs_filename,
bool is_system_include);

The function opened_include_file is called whenever a file referred @@ -263,7 +281,7 @@ as a result of a #include <...> directive.

returning_from_include_file

-
    template <typename ContextT>
void returning_from_include_file(ContextT const& ctx);
+
    template <typename ContextT>
void returning_from_include_file(ContextT const& ctx);

The function returning_from_include_file is called whenever an @@ -271,6 +289,43 @@

The ctx parameter provides a reference to the context_type used during instantiation of the preprocessing iterators by the user. Note, this parameter was added for the Boost V1.35.0 release.

+

detected_include_guard

+
    template <typename ContextT>
void detected_include_guard(ContextT const& ctx, + std::string const& filename, + std::string const& include_guard); +
+
+

The function detected_include_guard is called whenever an + include file is about to be added to the list of #pragma once headers as the + result of a detected include guard scheme. That means this header file will + not be opened and parsed again even if it is specified in a later + #include directive. 

+

The ctx parameter provides a reference to the context_type + used during instantiation of the preprocessing iterators by the user.

+

The parameter filename contains the file system path of the opened file + (this is relative to the directory of the currently processed file or a + absolute path depending on the paths given as the include search paths).

+

The parameter include_guard contains the name of the detected include guard.

+
+

detected_pragma_once

+
    template <typename ContextT, typename TokenT>
void detected_pragma_once(ContextT const& ctx, + TokenT const& pragma_token, + std::string const& filename);
+
+

The function detected_pragma_once is called whenever either an + include file is about to be added to the list of #pragma once headers as the + result of a detected #pragma once + directive. That means this header file will not be opened and parsed + again even if it is specified in a later #include + directive.

+

The ctx parameter provides a reference to the context_type + used during instantiation of the preprocessing iterators by the user.

+

The parameter pragma_token refers to the token "#pragma" + triggering this preprocessing hook.

+

The parameter filename contains the file system path of the opened file + (this is relative to the directory of the currently processed file or a + absolute path depending on the paths given as the include search paths).

+

Interpretation of #pragma's

interpret_pragma

    template <typename ContextT, typename ContextT, typename ContainerT>
bool interpret_pragma(ContextT const &ctx, ContainerT &pending,
typename ContextT::token_type const &option,
ContainerT const &values,
typename ContextT::token_type const &pragma_token);
@@ -346,7 +401,7 @@ exception of the type error_directive (normal execution continues), if the return value is true the execution continues as if no #error directive has been found and the overall directive is replaced by a single newline.

found_line_drective

-
    template <typename ContextT, typename ContainerT>
void found_line_drective(ContextT const& ctx,
ContainerT const &arguments, unsigned int line,
std::string const& filename);
+
    template <typename ContextT, typename ContainerT>
void found_line_drective(ContextT const& ctx,
ContainerT const &arguments, unsigned int line,
std::string const& filename);

The function found_line_directive is called whenever a #line directive has been encountered. Note, this function was added for the Boost V1.35.0 release.

@@ -373,7 +428,8 @@ 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

diff --git a/doc/class_reference_filepos.html b/doc/class_reference_filepos.html index 6f08d14..55a8a00 100644 --- a/doc/class_reference_filepos.html +++ b/doc/class_reference_filepos.html @@ -47,12 +47,12 @@ public: file_position(); explicit file_position(String const &file, - int line_ = 1, int column_ = 1); + unsigned int line_ = 1, unsigned int column_ = 1); // accessors String const &get_file() const; - int get_line() const; - int get_column() const; + unsigned int get_line() const; + unsigned int get_column() const; void set_file(String const &file); void set_line(int line); @@ -73,7 +73,7 @@

Constructors

        file_position();
         explicit file_position(String const &file, 
-            int line_ = 1, int column_ = 1);
+            unsigned int line_ = 1, unsigned int column_ = 1);
 

The constructors initialize a new instance of a file_position in @@ -82,8 +82,8 @@

get_file, get_line, get_column

        String const &get_file() const;
-        int get_line() const;
-        int get_column() const;
+        unsigned int get_line() const;
+        unsigned int get_column() const;
 

The get_... functions are used to access the current values of the @@ -92,8 +92,8 @@

set_file, set_line, set_column

        void set_file(String const &file);
-        void set_line(int line);
-        void set_column(int column);
+        void set_line(unsigned int line);
+        void set_column(unsigned int column);
 

The set_... functions are used to set new values to the file position @@ -113,7 +113,7 @@
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)

diff --git a/doc/wave_driver.html b/doc/wave_driver.html index 2f6fe7d..8efa15c 100644 --- a/doc/wave_driver.html +++ b/doc/wave_driver.html @@ -59,6 +59,8 @@ -l [ --listincludes ] arg: list included file to a file [arg] or to stdout [-] -m [ --macronames ] arg: list names of all defined macros to a file [arg] or to stdout [-] + -c [ --macrocounts ] arg list macro invocation counts to a file [arg] or to + stdout [-] -p [ --preserve ] arg (=0): preserve whitespace 0: no whitespace is preserved (default), 1: comments are preserved, @@ -68,6 +70,8 @@ 1: #line directives will be emitted (default) -x [ --extended ]: enable the #pragma wave system() directive -G [ --noguard ]: disable include guard detection + -g [ --listguards ]: list names of files flagged as 'include once' to a + file [arg] or to stdout [-] -s [ --state ] arg: load and save state information from/to the given file [arg] or 'wave.state' [-] (interactive mode only) @@ -194,6 +198,10 @@

Enable the output of all defined macros. This includes the macro names, its parameter names (if the macro is a function like macro) and its definition. The path specifies the filename to use for the output of the generated macro list. If the filename given equals to '-' (without the quotes), the macro list is put into the standard output stream (stdout).

+

-c [--macrocounts] path

+
+

Enable the output of all macro invocation counts. The path specifies the filename to use for the output of the generated list. If the filename given equals to '-' (without the quotes), the macro list is put into the standard output stream (stdout).

+

-p [--preserve] arg

Preserve the whitespace from the input stream not located inside of macro definitions. The argument defines the amount of whitespace to be preserved. A value of '0' (zero) skips all whitespace, a value of '1' preserves all the comments and a value of '2' will preserve all whitespace in the output.

@@ -212,6 +220,13 @@ is now disabled by default because it may cause a potential security threat. The

This option disables the automatic include guard detection normally performed by the Wave library during the processing of included files. For more information about automatic include guard detection please refer to The Context Object class reference.

+

-g [--listguards] arg

+
+

This option lists all found include files which either contain a + #pragma once or contain an include guard + into the given file. If the filename given equals to '-' (without the quotes), the + guards log is put into the standard output stream (stdout). For more information about automatic include guard detection please refer to The Context Object class reference.

+

-s [--state]

This option tries instructs the Wave tool to load the serialized information from the file given as the argument and to save back the internal state information at the end of the session to the same file. When using this option Wave loads and saves all defined macros (even the predefined ones) and the information about processed header files tagged with #pragma once and/or identified to have include guards.

@@ -273,7 +288,7 @@ is now disabled by default because it may cause a potential security threat. The 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)

diff --git a/include/boost/wave/cpp_context.hpp b/include/boost/wave/cpp_context.hpp index 4ecbf8d..ef334de 100644 --- a/include/boost/wave/cpp_context.hpp +++ b/include/boost/wave/cpp_context.hpp @@ -403,8 +403,18 @@ public: bool has_pragma_once(std::string const &filename_) { return includes.has_pragma_once(filename_); } bool add_pragma_once_header(std::string const &filename_, - std::string const& guard_name = "__BOOST_WAVE_PRAGMA_ONCE__") - { return includes.add_pragma_once_header(filename_, guard_name); } + std::string const& guard_name) + { + get_hooks().detected_include_guard(derived(), filename_, guard_name); + return includes.add_pragma_once_header(filename_, guard_name); + } + bool add_pragma_once_header(token_type const &pragma_, + std::string const &filename_) + { + get_hooks().detected_pragma_once(derived(), pragma_, filename_); + return includes.add_pragma_once_header(filename_, + "__BOOST_WAVE_PRAGMA_ONCE__"); + } #endif #if BOOST_WAVE_SERIALIZATION != 0 diff --git a/include/boost/wave/preprocessing_hooks.hpp b/include/boost/wave/preprocessing_hooks.hpp index 1d5b839..012ffc7 100644 --- a/include/boost/wave/preprocessing_hooks.hpp +++ b/include/boost/wave/preprocessing_hooks.hpp @@ -238,7 +238,7 @@ struct default_preprocessing_hooks std::string const& absname, bool is_system_include) {} #endif - + /////////////////////////////////////////////////////////////////////////// // // The function 'returning_from_include_file' is called, whenever an @@ -261,6 +261,80 @@ struct default_preprocessing_hooks {} #endif +#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 + /////////////////////////////////////////////////////////////////////////// + // + // The function 'detected_include_guard' is called whenever either a + // include file is about to be added to the list of #pragma once headers. + // That means this header file will not be opened and parsed again even + // if it is specified in a later #include directive. + // This function is called as the result of a detected include guard + // scheme. + // + // The implemented heuristics for include guards detects two forms of + // include guards: + // + // #ifndef INCLUDE_GUARD_MACRO + // #define INCLUDE_GUARD_MACRO + // ... + // #endif + // + // or + // + // if !defined(INCLUDE_GUARD_MACRO) + // #define INCLUDE_GUARD_MACRO + // ... + // #endif + // + // note, that the parenthesis are optional (i.e. !defined INCLUDE_GUARD_MACRO + // will work as well). The code allows for any whitespace, newline and single + // '#' tokens before the #if/#ifndef and after the final #endif. + // + // The parameter 'ctx' is a reference to the context object used for + // instantiating the preprocessing iterators by the user. + // + // The parameter 'filename' contains the file system path of the + // opened file (this is relative to the directory of the currently + // processed file or a absolute path depending on the paths given as the + // include search paths). + // + // The parameter contains the name of the detected include guard. + // + /////////////////////////////////////////////////////////////////////////// + template + void + detected_include_guard(ContextT const& ctx, std::string const& filename, + std::string const& include_guard) + {} + + /////////////////////////////////////////////////////////////////////////// + // + // The function 'detected_pragma_once' is called whenever either a + // include file is about to be added to the list of #pragma once headers. + // That means this header file will not be opened and parsed again even + // if it is specified in a later #include directive. + // This function is called as the result of a detected directive + // #pragma once. + // + // The parameter 'ctx' is a reference to the context object used for + // instantiating the preprocessing iterators by the user. + // + // The parameter pragma_token refers to the token "#pragma" triggering + // this preprocessing hook. + // + // The parameter 'filename' contains the file system path of the + // opened file (this is relative to the directory of the currently + // processed file or a absolute path depending on the paths given as the + // include search paths). + // + /////////////////////////////////////////////////////////////////////////// + template + void + detected_pragma_once(ContextT const& ctx, TokenT const& pragma_token, + std::string const& filename) + {} +#endif + /////////////////////////////////////////////////////////////////////////// // // The function 'interpret_pragma' is called, whenever a '#pragma command' @@ -368,7 +442,7 @@ struct default_preprocessing_hooks undefined_macro(ContextT const& ctx, TokenT const& macro_name) {} #endif - + /////////////////////////////////////////////////////////////////////////// // // The function 'found_directive' is called, whenever a preprocessor @@ -441,7 +515,7 @@ struct default_preprocessing_hooks bool expression_value) { return false; } // ok to continue, do not re-evaluate expression #endif - + /////////////////////////////////////////////////////////////////////////// // // The function 'skipped_token' is called, whenever a token is about to be @@ -591,7 +665,7 @@ struct default_preprocessing_hooks found_line_directive(ContextT const& ctx, ContainerT const& arguments, unsigned int line, std::string const& filename) {} - + /////////////////////////////////////////////////////////////////////////// // // The function 'throw_exception' will be called by the library whenever a diff --git a/include/boost/wave/util/cpp_include_paths.hpp b/include/boost/wave/util/cpp_include_paths.hpp index db56e4e..c7ebe7a 100644 --- a/include/boost/wave/util/cpp_include_paths.hpp +++ b/include/boost/wave/util/cpp_include_paths.hpp @@ -440,7 +440,7 @@ inline void load (Archive & ar, boost::filesystem::path &p, using namespace boost::serialization; std::string path_str; ar & make_nvp("filepath", path_str); - p = boost::filesystem::path(path_str, boost::filesystem::native); + p = wave::util::create_path(path_str); } // split non-intrusive serialization function member into separate diff --git a/include/boost/wave/util/cpp_iterator.hpp b/include/boost/wave/util/cpp_iterator.hpp index 9b7e589..48597b9 100644 --- a/include/boost/wave/util/cpp_iterator.hpp +++ b/include/boost/wave/util/cpp_iterator.hpp @@ -427,7 +427,10 @@ pp_iterator_functor::returned_from_include() // restore the actual current file and directory #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 - ctx.set_current_filename(iter_ctx->real_filename.c_str()); + namespace fs = boost::filesystem; + fs::path rfp(wave::util::create_path(iter_ctx->real_filename.c_str())); + std::string real_filename(rfp.string()); + ctx.set_current_filename(real_filename.c_str()); #endif ctx.set_current_directory(iter_ctx->real_filename.c_str()); @@ -1523,18 +1526,17 @@ fs::path native_path(wave::util::create_path(file_path)); } // test, if this file is known through a #pragma once directive + std::string native_path_str(wave::util::native_file_string(native_path)); #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 - if (!ctx.has_pragma_once(wave::util::native_file_string(native_path))) + if (!ctx.has_pragma_once(native_path.string())) #endif { // the new include file determines the actual current directory - ctx.set_current_directory( - wave::util::native_file_string(native_path).c_str()); + ctx.set_current_directory(native_path_str.c_str()); // preprocess the opened file boost::shared_ptr new_iter_ctx ( - new iteration_context_type(ctx, - wave::util::native_file_string(native_path).c_str(), + new iteration_context_type(ctx, native_path_str.c_str(), act_pos, boost::wave::enable_prefer_pp_numbers(ctx.get_language()))); // call the include policy trace function @@ -1559,7 +1561,9 @@ fs::path native_path(wave::util::create_path(file_path)); act_pos.set_file(iter_ctx->filename); // initialize file position #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 - ctx.set_current_filename(iter_ctx->real_filename.c_str()); + fs::path rfp(wave::util::create_path(iter_ctx->real_filename.c_str())); + std::string real_filename(rfp.string()); + ctx.set_current_filename(real_filename.c_str()); #endif act_pos.set_line(iter_ctx->line); diff --git a/include/boost/wave/util/file_position.hpp b/include/boost/wave/util/file_position.hpp index a8f291c..72c213d 100644 --- a/include/boost/wave/util/file_position.hpp +++ b/include/boost/wave/util/file_position.hpp @@ -78,12 +78,12 @@ struct file_position { public: typedef StringT string_type; - + file_position() : file(), line(1), column(1) {} - explicit file_position(string_type const& file_, int line_ = 1, - int column_ = 1) + explicit file_position(string_type const& file_, unsigned int line_ = 1, + unsigned int column_ = 1) : file(file_), line(line_), column(column_) { BOOST_ASSERT(!debug::is_escaped_lit(file)); @@ -93,7 +93,7 @@ public: string_type const &get_file() const { return file; } unsigned int get_line() const { return line; } unsigned int get_column() const { return column; } - + void set_file(string_type const &file_) { file = file_; @@ -101,7 +101,7 @@ public: } void set_line(unsigned int line_) { line = line_; } void set_column(unsigned int column_) { column = column_; } - + private: #if BOOST_WAVE_SERIALIZATION != 0 friend class boost::serialization::access; @@ -154,11 +154,11 @@ struct position_iterator : boost::spirit::classic::position_iterator { typedef boost::spirit::classic::position_iterator base_type; - + position_iterator() { } - + position_iterator(IteratorT const &begin, IteratorT const &end, PositionT const &pos) : base_type(begin, end, pos) diff --git a/include/boost/wave/util/interpret_pragma.hpp b/include/boost/wave/util/interpret_pragma.hpp index ac0bf44..410f5a5 100644 --- a/include/boost/wave/util/interpret_pragma.hpp +++ b/include/boost/wave/util/interpret_pragma.hpp @@ -58,7 +58,7 @@ interpret_pragma(ContextT &ctx, typename ContextT::token_type const &act_token, { typedef typename ContextT::token_type token_type; typedef typename token_type::string_type string_type; - + using namespace cpplexer; if (T_IDENTIFIER == token_id(*it)) { // check for pragma wave ... @@ -111,7 +111,7 @@ interpret_pragma(ContextT &ctx, typename ContextT::token_type const &act_token, act_token.get_position()); return false; } - + // remove the falsely matched surrounding parenthesis's if (values.size() >= 2) { BOOST_ASSERT(T_LEFTPAREN == values.front() && T_RIGHTPAREN == values.back()); @@ -119,7 +119,7 @@ interpret_pragma(ContextT &ctx, typename ContextT::token_type const &act_token, typename ContainerT::reverse_iterator rit = values.rbegin(); values.erase((++rit).base()); } - + // decode the option (call the context_policy hook) if (!ctx.get_hooks().interpret_pragma( ctx.derived(), pending, option, values, act_token)) @@ -143,7 +143,7 @@ interpret_pragma(ContextT &ctx, typename ContextT::token_type const &act_token, #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 else if ((*it).get_value() == "once") { // #pragma once - return ctx.add_pragma_once_header(ctx.get_current_filename()); + return ctx.add_pragma_once_header(act_token, ctx.get_current_filename()); } #endif #if BOOST_WAVE_SUPPORT_PRAGMA_MESSAGE != 0 @@ -151,7 +151,7 @@ interpret_pragma(ContextT &ctx, typename ContextT::token_type const &act_token, // #pragma message(...) or #pragma message ... using namespace boost::spirit::classic; ContainerT values; - + if (!parse (++it, end, ( ( ch_p(T_LEFTPAREN) >> lexeme_d[ @@ -173,7 +173,7 @@ interpret_pragma(ContextT &ctx, typename ContextT::token_type const &act_token, act_token.get_position()); return false; } - + // remove the falsely matched closing parenthesis/newline if (values.size() > 0) { BOOST_ASSERT(T_RIGHTPAREN == values.back() || T_NEWLINE == values.back()); diff --git a/test/testwave/collect_hooks_information.hpp b/test/testwave/collect_hooks_information.hpp index de1b2fa..06925f5 100644 --- a/test/testwave/collect_hooks_information.hpp +++ b/test/testwave/collect_hooks_information.hpp @@ -671,6 +671,89 @@ public: return this->base_type::throw_exception(ctx, e); } +#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 + /////////////////////////////////////////////////////////////////////////// + // + // The function 'detected_include_guard' is called whenever either a + // include file is about to be added to the list of #pragma once headers. + // That means this header file will not be opened and parsed again even + // if it is specified in a later #include directive. + // This function is called as the result of a detected include guard + // scheme. + // + // The implemented heuristics for include guards detects two forms of + // include guards: + // + // #ifndef INCLUDE_GUARD_MACRO + // #define INCLUDE_GUARD_MACRO + // ... + // #endif + // + // or + // + // if !defined(INCLUDE_GUARD_MACRO) + // #define INCLUDE_GUARD_MACRO + // ... + // #endif + // + // note, that the parenthesis are optional (i.e. !defined INCLUDE_GUARD_MACRO + // will work as well). The code allows for any whitespace, newline and single + // '#' tokens before the #if/#ifndef and after the final #endif. + // + // The parameter 'ctx' is a reference to the context object used for + // instantiating the preprocessing iterators by the user. + // + // The parameter 'filename' contains the file system path of the + // opened file (this is relative to the directory of the currently + // processed file or a absolute path depending on the paths given as the + // include search paths). + // + // The parameter contains the name of the detected include guard. + // + /////////////////////////////////////////////////////////////////////////// + template + void + detected_include_guard(ContextT const& ctx, std::string const& filename, + std::string const& include_guard) + { + BOOST_WAVETEST_OSSTREAM strm; + strm << "19: " << filename << ": " << include_guard << std::endl; + hooks_trace += BOOST_WAVETEST_GETSTRING(strm); + } + + /////////////////////////////////////////////////////////////////////////// + // + // The function 'detected_pragma_once' is called whenever either a + // include file is about to be added to the list of #pragma once headers. + // That means this header file will not be opened and parsed again even + // if it is specified in a later #include directive. + // This function is called as the result of a detected directive + // #pragma once. + // + // The parameter 'ctx' is a reference to the context object used for + // instantiating the preprocessing iterators by the user. + // + // The parameter pragma_token refers to the token "#pragma" triggering + // this preprocessing hook. + // + // The parameter 'filename' contains the file system path of the + // opened file (this is relative to the directory of the currently + // processed file or a absolute path depending on the paths given as the + // include search paths). + // + /////////////////////////////////////////////////////////////////////////// + template + void + detected_pragma_once(ContextT const& ctx, TokenT const& pragma_token, + std::string const& filename) + { + BOOST_WAVETEST_OSSTREAM strm; + strm << "20: " << repr(pragma_token.get_position()) << ": " + << pragma_token.get_value() << ": " << filename << std::endl; + hooks_trace += BOOST_WAVETEST_GETSTRING(strm); + } +#endif + private: std::string& hooks_trace; }; diff --git a/test/testwave/testfiles/t_2_009.cpp b/test/testwave/testfiles/t_2_009.cpp index 63cb4bf..6708742 100644 --- a/test/testwave/testfiles/t_2_009.cpp +++ b/test/testwave/testfiles/t_2_009.cpp @@ -47,6 +47,7 @@ including "t_2_009.cpp" //H 10: t_2_009.cpp(15): #if //H 11: t_2_009.cpp(15): #if !defined(FILE_002_009_CPP) : 0 //H 06: +//H 19: $B(t_2_009.cpp): FILE_002_009_CPP //H 10: t_2_009.cpp(29): #include //H 01: t_2_009.cpp(19): USER_HEADER //H 02: "t_2_009.cpp" diff --git a/test/testwave/testfiles/t_2_019.cpp b/test/testwave/testfiles/t_2_019.cpp new file mode 100644 index 0000000..043fb64 --- /dev/null +++ b/test/testwave/testfiles/t_2_019.cpp @@ -0,0 +1,61 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + http://www.boost.org/ + + Copyright (c) 2001-2009 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) +=============================================================================*/ + +// Check if #pragma once, include guard detection, and related hooks work as +// expected + +#include "t_2_019_001.hpp" // #pragma once +#include "t_2_019_002.hpp" // include guard style 1 +#include "t_2_019_003.hpp" // include guard style 2 + +// repeat inclusion, should do nothing +#include "t_2_019_001.hpp" +#include "t_2_019_002.hpp" +#include "t_2_019_003.hpp" + +//R #line 11 "t_2_019_001.hpp" +//R t_2_019_001 +//R #line 16 "t_2_019_002.hpp" +//R t_2_019_002 +//R #line 16 "t_2_019_003.hpp" +//R t_2_019_003 + +//H 10: t_2_019.cpp(13): #include "t_2_019_001.hpp" +//H 04: "t_2_019_001.hpp" +//H 05: $B(t_2_019_001.hpp) ($B(t_2_019_001.hpp)) +//H 10: t_2_019_001.hpp(10): #pragma +//H 20: t_2_019_001.hpp(10): #pragma: $B(t_2_019_001.hpp) +//H 06: +//H 10: t_2_019.cpp(14): #include "t_2_019_002.hpp" +//H 04: "t_2_019_002.hpp" +//H 05: $B(t_2_019_002.hpp) ($B(t_2_019_002.hpp)) +//H 10: t_2_019_002.hpp(12): #if +//H 11: t_2_019_002.hpp(12): #if !defined(T_2_019_002): 1 +//H 10: t_2_019_002.hpp(14): #define +//H 08: t_2_019_002.hpp(14): T_2_019_002= +//H 10: t_2_019_002.hpp(18): #endif +//H 06: +//H 19: $B(t_2_019_002.hpp): T_2_019_002 +//H 10: t_2_019.cpp(15): #include "t_2_019_003.hpp" +//H 04: "t_2_019_003.hpp" +//H 05: $B(t_2_019_003.hpp) ($B(t_2_019_003.hpp)) +//H 10: t_2_019_003.hpp(12): #ifndef +//H 11: t_2_019_003.hpp(12): #ifndef T_2_019_003: 0 +//H 10: t_2_019_003.hpp(14): #define +//H 08: t_2_019_003.hpp(14): T_2_019_003= +//H 10: t_2_019_003.hpp(18): #endif +//H 06: +//H 19: $B(t_2_019_003.hpp): T_2_019_003 +//H 10: t_2_019.cpp(18): #include "t_2_019_001.hpp" +//H 04: "t_2_019_001.hpp" +//H 10: t_2_019.cpp(19): #include "t_2_019_002.hpp" +//H 04: "t_2_019_002.hpp" +//H 10: t_2_019.cpp(20): #include "t_2_019_003.hpp" +//H 04: "t_2_019_003.hpp" + diff --git a/test/testwave/testfiles/t_2_019_001.hpp b/test/testwave/testfiles/t_2_019_001.hpp new file mode 100644 index 0000000..d62baec --- /dev/null +++ b/test/testwave/testfiles/t_2_019_001.hpp @@ -0,0 +1,11 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + http://www.boost.org/ + + Copyright (c) 2001-2009 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) +=============================================================================*/ + +#pragma once +t_2_019_001 diff --git a/test/testwave/testfiles/t_2_019_002.hpp b/test/testwave/testfiles/t_2_019_002.hpp new file mode 100644 index 0000000..f746e0b --- /dev/null +++ b/test/testwave/testfiles/t_2_019_002.hpp @@ -0,0 +1,20 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + http://www.boost.org/ + + Copyright (c) 2001-2009 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(T_2_019_002) +# // more comments here +#define T_2_019_002 + +t_2_019_002 + +#endif +# // and here + diff --git a/test/testwave/testfiles/t_2_019_003.hpp b/test/testwave/testfiles/t_2_019_003.hpp new file mode 100644 index 0000000..4c5ae54 --- /dev/null +++ b/test/testwave/testfiles/t_2_019_003.hpp @@ -0,0 +1,20 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + http://www.boost.org/ + + Copyright (c) 2001-2009 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) +=============================================================================*/ + +# + +#ifndef T_2_019_003 +# // more comments here +#define T_2_019_003 + +t_2_019_003 + +#endif +# // and here + diff --git a/test/testwave/testfiles/t_9_016.cpp b/test/testwave/testfiles/t_9_016.cpp index 075bfec..4944598 100644 --- a/test/testwave/testfiles/t_9_016.cpp +++ b/test/testwave/testfiles/t_9_016.cpp @@ -34,6 +34,7 @@ EXPAND(#) define later //H 03: # //H 10: t_9_016.cpp(19): #endif //H 06: +//H 19: $B(t_9_016.cpp): inclusion //H 10: t_9_016.cpp(13): # include "t_9_016.hpp" //H 04: "t_9_016.hpp" //H 05: $B(t_9_016.hpp) ($B(t_9_016.hpp)) diff --git a/test/testwave/testfiles/test.cfg b/test/testwave/testfiles/test.cfg index 26191ef..f0540cf 100644 --- a/test/testwave/testfiles/test.cfg +++ b/test/testwave/testfiles/test.cfg @@ -71,6 +71,7 @@ t_2_015.cpp t_2_016.cpp t_2_017.cpp t_2_018.cpp +t_2_019.cpp # # t_3: Predefined macros diff --git a/tool/cpp.cpp b/tool/cpp.cpp index b15fb33..07e2b60 100644 --- a/tool/cpp.cpp +++ b/tool/cpp.cpp @@ -672,6 +672,7 @@ int error_count = 0; std::ofstream output; std::ofstream traceout; std::ofstream includelistout; + std::ofstream listguardsout; trace_flags enable_trace = trace_nothing; @@ -723,6 +724,31 @@ int error_count = 0; rdbuf(cout.rdbuf()); } + // Open the stream where to output the list of included file names + if (vm.count("listguards")) { + // try to open the file, where to put the include list + fs::path listguards_file(boost::wave::util::create_path( + vm["listguards"].as())); + + if (listguards_file != "-") { + fs::create_directories(boost::wave::util::branch_path(listguards_file)); + listguardsout.open(listguards_file.string().c_str()); + if (!listguardsout.is_open()) { + cerr << "wave: could not open include guard list file: " + << listguards_file.string() << endl; + return -1; + } + } + enable_trace = trace_flags(enable_trace | trace_guards); + } + if ((enable_trace & trace_guards) && !listguardsout.is_open()) { + // by default list included names to std::cout + listguardsout.copyfmt(cout); + listguardsout.clear(cout.rdstate()); + static_cast &>(listguardsout). + rdbuf(cout.rdbuf()); + } + // enable preserving comments mode bool preserve_comments = false; bool preserve_whitespace = false; @@ -759,8 +785,8 @@ int error_count = 0; bool allow_output = true; // will be manipulated from inside the hooks object std::string default_outfile; // will be used from inside the hooks object trace_macro_expansion hooks(preserve_whitespace, - output, traceout, includelistout, enable_trace, enable_system_command, - allow_output, default_outfile); + output, traceout, includelistout, listguardsout, enable_trace, + enable_system_command, allow_output, default_outfile); // enable macro invocation count, if appropriate if (vm.count("macrocounts")) @@ -1206,6 +1232,9 @@ main (int argc, char *argv[]) ("extended,x", "enable the #pragma wave system() directive") #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 ("noguard,G", "disable include guard detection") + ("listguards,g", po::value(), + "list names of files flagged as 'include once' to a file [arg] " + "or to stdout [-]") #endif #if BOOST_WAVE_SERIALIZATION != 0 ("state,s", po::value(), diff --git a/tool/trace_macro_expansion.hpp b/tool/trace_macro_expansion.hpp index 877c49d..48fd408 100644 --- a/tool/trace_macro_expansion.hpp +++ b/tool/trace_macro_expansion.hpp @@ -54,7 +54,8 @@ enum trace_flags { trace_nothing = 0, // disable tracing trace_macros = 1, // enable macro tracing trace_macro_counts = 2, // enable invocation counting - trace_includes = 4 // enable include file tracing + trace_includes = 4, // enable include file tracing + trace_guards = 8 // enable include guard tracing }; /////////////////////////////////////////////////////////////////////////////// @@ -144,10 +145,11 @@ class trace_macro_expansion public: trace_macro_expansion(bool preserve_whitespace_, std::ofstream &output_, std::ostream &tracestrm_, - std::ostream &includestrm_, trace_flags flags_, - bool enable_system_command_, bool& generate_output_, - std::string const& default_outfile_) - : outputstrm(output_), tracestrm(tracestrm_), includestrm(includestrm_), + std::ostream &includestrm_, std::ostream &guardstrm_, + trace_flags flags_, bool enable_system_command_, + bool& generate_output_, std::string const& default_outfile_) + : outputstrm(output_), tracestrm(tracestrm_), + includestrm(includestrm_), guardstrm(guardstrm_), level(0), flags(flags_), logging_flags(trace_nothing), enable_system_command(enable_system_command_), preserve_whitespace(preserve_whitespace_), @@ -571,6 +573,58 @@ public: } } +#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 + /////////////////////////////////////////////////////////////////////////// + // + // The function 'detected_include_guard' is called whenever either a + // include file is about to be added to the list of #pragma once headers. + // That means this header file will not be opened and parsed again even + // if it is specified in a later #include directive. + // This function is called as the result of a detected include guard + // scheme. + // + // The implemented heuristics for include guards detects two forms of + // include guards: + // + // #ifndef INCLUDE_GUARD_MACRO + // #define INCLUDE_GUARD_MACRO + // ... + // #endif + // + // or + // + // if !defined(INCLUDE_GUARD_MACRO) + // #define INCLUDE_GUARD_MACRO + // ... + // #endif + // + // note, that the parenthesis are optional (i.e. !defined INCLUDE_GUARD_MACRO + // will work as well). The code allows for any whitespace, newline and single + // '#' tokens before the #if/#ifndef and after the final #endif. + // + // The parameter 'ctx' is a reference to the context object used for + // instantiating the preprocessing iterators by the user. + // + // The parameter 'filename' contains the file system path of the + // opened file (this is relative to the directory of the currently + // processed file or a absolute path depending on the paths given as the + // include search paths). + // + // The parameter contains the name of the detected include guard. + // + /////////////////////////////////////////////////////////////////////////// + template + void + detected_include_guard(ContextT const& ctx, std::string const& filename, + std::string const& include_guard) + { + if (enabled_guard_tracing()) { + guardstrm << include_guard << ":" << std::endl + << " " << filename << std::endl; + } + } +#endif + /////////////////////////////////////////////////////////////////////////// // // The function 'may_skip_whitespace' will be called by the @@ -1129,6 +1183,10 @@ protected: { return (flags & trace_includes); } + bool enabled_guard_tracing() const + { + return (flags & trace_guards); + } bool enabled_macro_counting() const { return logging_flags & trace_macro_counts; @@ -1178,6 +1236,7 @@ private: std::ofstream &outputstrm; // main output stream std::ostream &tracestrm; // trace output stream std::ostream &includestrm; // included list output stream + std::ostream &guardstrm; // include guard output stream int level; // indentation level trace_flags flags; // enabled globally trace_flags logging_flags; // enabled by a #pragma