From 227d3f39420f49a2bec48c08b7e31e81b88805f6 Mon Sep 17 00:00:00 2001 From: Martchus Date: Fri, 17 Nov 2017 21:42:49 +0100 Subject: [PATCH] Allow passing target config to generator invocation So the code generator 'sees' the source code in the same way as the compiler does. --- README.md | 10 ++++++ generator/CMakeLists.txt | 2 ++ generator/codefactory.cpp | 3 +- generator/codefactory.h | 4 +-- generator/main.cpp | 19 +++++++++-- lib/cmake/modules/ReflectionGenerator.cmake | 38 ++++++++++++++++++--- 6 files changed, 66 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ca2065a..ba28ad2 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ Note that the header included at the bottom must be generated by invoking the co reflective_rapidjson_generator -i "$srcdir/code-defining-structs.cpp" -o "$builddir/reflection/code-defining-structs.h" ``` +#### Invoking code generator with CMake macro It is possible to use the provided CMake macro to automate this task: ``` find_package(reflective-rapidjson REQUIRED) @@ -99,6 +100,15 @@ will always have the extension "`.h`", independently of the extension of the inp The full paths of the generated files are also appended to the variable `LIST_OF_GENERATED_HEADERS` which then can be added to the sources of your target. Of course this can be skipped if not required/wanted. +#### Passing Clang options +It is possible to pass additional options to the Clang tool invocation used by the code generator. +This can be done using the `--clang-opt` argument or the `CLANG_OPTIONS` argument when using the CMake macro. + +It makes most sense to specify the same options as during compilation so the code generator uses the same flags, +defines and include directories as the compiler and hence behaves like the compiler. +When using the CMake macro it is possible to automatically pass all compile flags, compile definitions and include directories +from certain targets to the code generator. The targets can ge specified using the `CLANG_OPTIONS_FROM_TARGETS` argument. + ### Using Boost.Hana instead of the code generator The same example as above. However, this time Boost.Hana is used - so it doesn't require invoking the generator. diff --git a/generator/CMakeLists.txt b/generator/CMakeLists.txt index d6d8a7e..7ceae6c 100644 --- a/generator/CMakeLists.txt +++ b/generator/CMakeLists.txt @@ -59,6 +59,8 @@ add_reflection_generator_invocation( json OUTPUT_LISTS TEST_HEADER_FILES + CLANG_OPTIONS_FROM_TARGETS + reflective_rapidjson_generator_tests JSON_CLASSES OtherNotJsonSerializable # test specifying classes for JSON (de)serialization manually SomeOtherClassName # specifying a class that does not exist should not cause any problems diff --git a/generator/codefactory.cpp b/generator/codefactory.cpp index 6b441b9..729688a 100644 --- a/generator/codefactory.cpp +++ b/generator/codefactory.cpp @@ -25,8 +25,7 @@ CodeFactory::ToolInvocation::ToolInvocation(CodeFactory &factory) fileManager.Retain(); } -CodeFactory::CodeFactory( - const char *applicationPath, const std::vector &sourceFiles, const std::vector &clangOptions, std::ostream &os) +CodeFactory::CodeFactory(const char *applicationPath, const std::vector &sourceFiles, const std::vector &clangOptions, std::ostream &os) : m_applicationPath(applicationPath) , m_sourceFiles(sourceFiles) , m_clangOptions(clangOptions) diff --git a/generator/codefactory.h b/generator/codefactory.h index 9b202c0..d21e31b 100644 --- a/generator/codefactory.h +++ b/generator/codefactory.h @@ -29,7 +29,7 @@ class CodeFactory { public: CodeFactory( - const char *applicationPath, const std::vector &sourceFiles, const std::vector &clangOptions, std::ostream &os); + const char *applicationPath, const std::vector &sourceFiles, const std::vector &clangOptions, std::ostream &os); ~CodeFactory(); const std::vector> &generators() const; @@ -48,7 +48,7 @@ private: const char *const m_applicationPath; const std::vector &m_sourceFiles; - const std::vector &m_clangOptions; + const std::vector &m_clangOptions; std::ostream &m_os; std::vector> m_generators; std::unique_ptr m_toolInvocation; diff --git a/generator/main.cpp b/generator/main.cpp index c5c2705..59537de 100644 --- a/generator/main.cpp +++ b/generator/main.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,7 @@ using namespace std; using namespace ApplicationUtilities; +using namespace ConversionUtilities; using namespace EscapeCodes; using namespace IoUtilities; using namespace ReflectiveRapidJSON; @@ -63,10 +65,23 @@ int main(int argc, char *argv[]) os = &cout; } + // compose options passed to the clang tool invocation + vector clangOptions; + if(clangOptionsArg.isPresent()) { + // add additional options specified via CLI argument + for(const auto *const value : clangOptionsArg.values(0)) { + // split options by ";" - not nice but this eases using CMake generator expressions + const auto splittedValues(splitString>(value, ";", EmptyPartsTreat::Omit)); + clangOptions.reserve(clangOptions.size() + splittedValues.size()); + for(const auto &splittedValue : splittedValues) { + clangOptions.emplace_back(move(splittedValue)); + } + } + } + // configure code generator - vector defaultClangOptions; CodeFactory factory( - parser.executable(), inputFileArg.values(0), clangOptionsArg.isPresent() ? clangOptionsArg.values(0) : defaultClangOptions, *os); + parser.executable(), inputFileArg.values(0), clangOptions, *os); // add only specified generators if the --generator argument is present if (generatorsArg.isPresent()) { // find and construct generators by name diff --git a/lib/cmake/modules/ReflectionGenerator.cmake b/lib/cmake/modules/ReflectionGenerator.cmake index 123f71c..1c5ed99 100644 --- a/lib/cmake/modules/ReflectionGenerator.cmake +++ b/lib/cmake/modules/ReflectionGenerator.cmake @@ -26,13 +26,23 @@ if(NOT REFLECTION_GENERATOR_EXECUTABLE) message(FATAL_ERROR "Unable to find executable of generator for reflection code.") endif() -# define helper functions +# define helper function to add a reflection generator invocation for a specified list of source files include(CMakeParseArguments) function(add_reflection_generator_invocation) # parse arguments - set(OPTIONAL_ARGS) - set(ONE_VALUE_ARGS OUTPUT_DIRECTORY JSON_VISIBILITY) - set(MULTI_VALUE_ARGS INPUT_FILES GENERATORS OUTPUT_LISTS CLANG_OPTIONS JSON_CLASSES) + set(OPTIONAL_ARGS + ) + set(ONE_VALUE_ARGS + OUTPUT_DIRECTORY + JSON_VISIBILITY + ) + set(MULTI_VALUE_ARGS + INPUT_FILES + GENERATORS + OUTPUT_LISTS + CLANG_OPTIONS + CLANG_OPTIONS_FROM_TARGETS + JSON_CLASSES) cmake_parse_arguments(ARGS "${OPTIONAL_ARGS}" "${ONE_VALUE_ARGS}" "${MULTI_VALUE_ARGS}" ${ARGN}) # determine file name or file path if none specified @@ -41,10 +51,29 @@ function(add_reflection_generator_invocation) file(MAKE_DIRECTORY "${ARGS_OUTPUT_DIRECTORY}") endif() + # add options to be passed to clang from the specified targets + if(ARGS_CLANG_OPTIONS_FROM_TARGETS) + foreach(TARGET_NAME ${ARGS_CLANG_OPTIONS_FROM_TARGETS}) + # add compile flags + set(PROP "$") + list(APPEND ARGS_CLANG_OPTIONS "$<$:$>>") + # add compile definitions + set(PROP "$") + list(APPEND ARGS_CLANG_OPTIONS "$<$:-D$-D>>") + # add include directories + set(PROP "$") + list(APPEND ARGS_CLANG_OPTIONS "$<$:-I$-I>>") + endforeach() + endif() + + # create a custom command for each input file foreach(INPUT_FILE ${ARGS_INPUT_FILES}) + # determine the output file get_filename_component(OUTPUT_NAME "${INPUT_FILE}" NAME_WE) set(OUTPUT_FILE "${ARGS_OUTPUT_DIRECTORY}/${OUTPUT_NAME}.h") message(STATUS "Adding generator command for ${INPUT_FILE} producing ${OUTPUT_FILE}") + + # compose the CLI arguments and actually add the custom command set(CLI_ARGUMENTS --output-file "${OUTPUT_FILE}" --input-file "${INPUT_FILE}" @@ -62,6 +91,7 @@ function(add_reflection_generator_invocation) DEPENDS "${INPUT_FILE}" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMMENT "Generating reflection code for ${INPUT_FILE}" + VERBATIM ) # append the output file to lists specified via OUTPUT_LISTS