diff --git a/CMake/mitkFunctionCMakeDoxygenFilterCompile.cmake b/CMake/mitkFunctionCMakeDoxygenFilterCompile.cmake index a79c6c11e0..67634dfd05 100644 --- a/CMake/mitkFunctionCMakeDoxygenFilterCompile.cmake +++ b/CMake/mitkFunctionCMakeDoxygenFilterCompile.cmake @@ -1,95 +1,87 @@ #! #! \brief Download and compile a CMake doxygen input filter #! #! \param OUT (optional) Supply an absolute filename for #! the generated executable. #! \param NAMESPACE (optional) Supply a C++ namespace in #! which the generated function declrarations #! should be wrapped. #! #! \return This function sets the CMakeDoxygenFilter_EXECUTABLE #! variable to the absolute path of the generated input filter executable #! in the parent scope. If is specified, they will be the same. #! #! This CMake function compiles the http://github.com/saschazelzer/CMakeDoxygenFilter #! project into a doxygen input filter executable. See #! http://github.com/saschazelzer/CMakeDoxygenFilter/blob/master/README for more details. #! function(mitkFunctionCMakeDoxygenFilterCompile) #-------------------- parse function arguments ------------------- set(DEFAULT_ARGS) set(prefix "FILTER") set(arg_names "OUT;NAMESPACE") set(option_names "") foreach(arg_name ${arg_names}) set(${prefix}_${arg_name}) endforeach(arg_name) foreach(option ${option_names}) set(${prefix}_${option} FALSE) endforeach(option) set(current_arg_name DEFAULT_ARGS) set(current_arg_list) foreach(arg ${ARGN}) set(larg_names ${arg_names}) list(FIND larg_names "${arg}" is_arg_name) if(is_arg_name GREATER -1) set(${prefix}_${current_arg_name} ${current_arg_list}) set(current_arg_name "${arg}") set(current_arg_list) else(is_arg_name GREATER -1) set(loption_names ${option_names}) list(FIND loption_names "${arg}" is_option) if(is_option GREATER -1) set(${prefix}_${arg} TRUE) else(is_option GREATER -1) set(current_arg_list ${current_arg_list} "${arg}") endif(is_option GREATER -1) endif(is_arg_name GREATER -1) endforeach(arg ${ARGN}) set(${prefix}_${current_arg_name} ${current_arg_list}) #------------------- finished parsing arguments ---------------------- if(FILTER_OUT) set(copy_file "${FILTER_OUT}") else() set(copy_file "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CMakeDoxygenFilter${CMAKE_EXECUTABLE_SUFFIX}") endif() set(compile_defs "") if(FILTER_NAMESPACE) set(compile_defs "${compile_defs} -DUSE_NAMESPACE=${FILTER_NAMESPACE}") endif() - set(cmake_doxygen_filter_url "https://github.com/saschazelzer/CMakeDoxygenFilter/raw/master/CMakeDoxygenFilter.cpp") - set(cmake_doxygen_filter_src "${CMAKE_CURRENT_BINARY_DIR}/CMakeDoxygenFilter.cpp") - - file(DOWNLOAD "${cmake_doxygen_filter_url}" "${cmake_doxygen_filter_src}" STATUS status) - list(GET status 0 error_code) - list(GET status 1 error_msg) - if(error_code) - message(FATAL_ERROR "error: Failed to download ${cmake_doxygen_filter_url} - ${error_msg}") - endif() + set(cmake_doxygen_filter_src "${CMAKE_CURRENT_SOURCE_DIR}/CMakeDoxygenFilter.cpp") try_compile(result_var "${CMAKE_CURRENT_BINARY_DIR}" "${cmake_doxygen_filter_src}" COMPILE_DEFINITIONS ${compile_defs} OUTPUT_VARIABLE compile_output COPY_FILE ${copy_file} ) if(NOT result_var) message(FATAL_ERROR "error: Faild to compile ${cmake_doxygen_filter_src} (result: ${result_var})\n${compile_output}") endif() set(CMakeDoxygenFilter_EXECUTABLE "${copy_file}" PARENT_SCOPE) endfunction() diff --git a/Documentation/CMakeDoxygenFilter.cpp b/Documentation/CMakeDoxygenFilter.cpp new file mode 100644 index 0000000000..e1d18cf175 --- /dev/null +++ b/Documentation/CMakeDoxygenFilter.cpp @@ -0,0 +1,406 @@ +/*============================================================================= + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include +#include +#include +#include + +#include + +//-------------------------------------- +// Utilitiy classes and functions +//-------------------------------------- + +struct ci_char_traits : public std::char_traits + // just inherit all the other functions + // that we don't need to override +{ + static bool eq(char c1, char c2) + { return std::toupper(c1) == std::toupper(c2); } + + static bool ne(char c1, char c2) + { return std::toupper(c1) != std::toupper(c2); } + + static bool lt(char c1, char c2) + { return std::toupper(c1) < std::toupper(c2); } + + static bool gt(char c1, char c2) + { return std::toupper(c1) > std::toupper(c2); } + + static int compare(const char* s1, const char* s2, std::size_t n) + { + while (n-- > 0) + { + if (lt(*s1, *s2)) return -1; + if (gt(*s1, *s2)) return 1; + ++s1; ++s2; + } + return 0; + } + + static const char* find(const char* s, int n, char a) + { + while (n-- > 0 && std::toupper(*s) != std::toupper(a)) + { + ++s; + } + return s; + } +}; + +typedef std::basic_string ci_string; + +//-------------------------------------- +// Lexer +//-------------------------------------- + +class CMakeLexer +{ +public: + + enum Token { + TOK_EOF = -1, + + // commands + TOK_MACRO = -2, TOK_ENDMACRO = -3, + TOK_FUNCTION = -4, TOK_ENDFUNCTION = -5, + TOK_DOXYGEN_COMMENT = -6, + + TOK_STRING_LITERAL = -100, + + // primary + TOK_IDENTIFIER = -200 + }; + + CMakeLexer(std::istream& is) + : _lastChar(' '), _is(is), _line(1), _col(1) + {} + + int getToken() + { + // skip whitespace + while (std::isspace(_lastChar)) + { + _lastChar = getChar(); + } + + if (std::isalpha(_lastChar) || _lastChar == '_') + { + _identifier = _lastChar; + while (std::isalnum(_lastChar = getChar()) || _lastChar == '-' || _lastChar == '_') + { + _identifier += _lastChar; + } + + if (_identifier == "function") + return TOK_FUNCTION; + if (_identifier == "macro") + return TOK_MACRO; + if (_identifier == "endfunction") + return TOK_ENDFUNCTION; + if (_identifier == "endmacro") + return TOK_ENDMACRO; + return TOK_IDENTIFIER; + } + + if (_lastChar == '#') + { + _lastChar = getChar(); + if (_lastChar == '!') + { + // found a doxygen comment marker + _identifier.clear(); + + _lastChar = getChar(); + while (_lastChar != EOF && _lastChar != '\n' && _lastChar != '\r') + { + _identifier += _lastChar; + _lastChar = getChar(); + } + return TOK_DOXYGEN_COMMENT; + } + + // skip the comment + while (_lastChar != EOF && _lastChar != '\n' && _lastChar != '\r') + { + _lastChar = getChar(); + } + } + + if (_lastChar == '"') + { + _lastChar = getChar(); + _identifier.clear(); + while (_lastChar != EOF && _lastChar != '"') + { + _identifier += _lastChar; + _lastChar = getChar(); + } + + // eat the closing " + _lastChar = getChar(); + return TOK_STRING_LITERAL; + } + + // don't eat the EOF + if (_lastChar == EOF) return TOK_EOF; + + // return the character as its ascii value + int thisChar = _lastChar; + _lastChar = getChar(); + return thisChar; + } + + std::string getIdentifier() const + { + return std::string(_identifier.c_str()); + } + + int curLine() const + { return _line; } + + int curCol() const + { return _col; } + + int getChar() + { + int c = _is.get(); + updateLoc(c); + return c; + } + +private: + + void updateLoc(int c) + { + if (c == '\n' || c == '\r') + { + ++_line; + _col = 1; + } + else + { + ++_col; + } + } + + ci_string _identifier; + int _lastChar; + std::istream& _is; + + int _line; + int _col; +}; + +//-------------------------------------- +// Parser +//-------------------------------------- + +class CMakeParser +{ + +public: + + CMakeParser(std::istream& is, std::ostream& os) + : _is(is), _os(os), _lexer(is), _curToken(CMakeLexer::TOK_EOF) + { } + + int curToken() + { + return _curToken; + } + + int nextToken() + { + return _curToken = _lexer.getToken(); + } + + void handleMacro() + { + if(!parseMacro()) + { + // skip token for error recovery + nextToken(); + } + } + + void handleFunction() + { + if(!parseFunction()) + { + // skip token for error recovery + nextToken(); + } + } + + void handleDoxygenComment() + { + _os << "///" << _lexer.getIdentifier() << std::endl; + nextToken(); + } + + void handleTopLevelExpression() + { + // skip token + nextToken(); + } + +private: + + void printError(const char* str) + { + std::cerr << "Error: " << str << " (at line " << _lexer.curLine() << ", col " << _lexer.curCol() << ")\n"; + } + + bool parseMacro() + { + if (nextToken() != '(') + { + printError("Expected '(' after MACRO"); + return false; + } + + nextToken(); + std::string macroName = _lexer.getIdentifier(); + if (curToken() != CMakeLexer::TOK_IDENTIFIER || macroName.empty()) + { + printError("Expected macro name"); + return false; + } + + _os << macroName << '('; + if (nextToken() == CMakeLexer::TOK_IDENTIFIER) + { + _os << _lexer.getIdentifier(); + while (nextToken() == CMakeLexer::TOK_IDENTIFIER) + { + _os << ", " << _lexer.getIdentifier(); + } + } + + if (curToken() != ')') + { + printError("Missing expected ')'"); + } + else + { + _os << ");" << std::endl; + } + + // eat the ')' + nextToken(); + return true; + } + + bool parseFunction() + { + if (nextToken() != '(') + { + printError("Expected '(' after FUNCTION"); + return false; + } + + nextToken(); + std::string funcName = _lexer.getIdentifier(); + if (curToken() != CMakeLexer::TOK_IDENTIFIER || funcName.empty()) + { + printError("Expected function name"); + return false; + } + + _os << funcName << '('; + if (nextToken() == CMakeLexer::TOK_IDENTIFIER) + { + _os << _lexer.getIdentifier(); + while (nextToken() == CMakeLexer::TOK_IDENTIFIER) + { + _os << ", " << _lexer.getIdentifier(); + } + } + + if (curToken() != ')') + { + printError("Missing expected ')'"); + } + else + { + _os << ");" << std::endl; + } + + // eat the ')' + nextToken(); + + return true; + } + + std::istream& _is; + std::ostream& _os; + CMakeLexer _lexer; + int _curToken; +}; + + +#define STRINGIFY(a) #a +#define DOUBLESTRINGIFY(a) STRINGIFY(a) + +int main(int argc, char** argv) +{ + assert(argc > 1); + + for (int i = 1; i < argc; ++i) + { + std::ifstream ifs(argv[i]); + std::ostream& os = std::cout; + + #ifdef USE_NAMESPACE + os << "namespace " << DOUBLESTRINGIFY(USE_NAMESPACE) << " {\n"; + #endif + + CMakeParser parser(ifs, os); + parser.nextToken(); + while (ifs.good()) + { + switch (parser.curToken()) + { + case CMakeLexer::TOK_EOF: + return ifs.get(); // eat EOF + case CMakeLexer::TOK_MACRO: + parser.handleMacro(); + break; + case CMakeLexer::TOK_FUNCTION: + parser.handleFunction(); + break; + case CMakeLexer::TOK_DOXYGEN_COMMENT: + parser.handleDoxygenComment(); + break; + default: + parser.handleTopLevelExpression(); + break; + } + } + + #ifdef USE_NAMESPACE + os << "}\n"; + #endif + } + + return EXIT_SUCCESS; +} +