About

That page provides information about flextool integration with C++ projects.

Example project: flex_meta_demo

Github repository: https://github.com/blockspacer/flex_meta_demo

Uses conanfile and CMakeLists.txt to integrate with flextool

Adding flextool to conanfile and CMakeLists.txt

Make sure you installed flextool using conan. You can find installation instructions in README.md at https://github.com/blockspacer/flextool

If you built flextool using conan create, than flextool must be in output of command conan search flextool

Make sure that your project uses conanfile.py. See https://docs.conan.io/en/latest/mastering/conanfile_py.html for details

Add in conanfile.py:

      self.requires("flextool/master@conan/stable")

Run conan install. See https://docs.conan.io/en/latest/using_packages/conanfile_txt.html for details

Now you can use flextool in CMakeLists.txt of your project

Add in CMakeLists.txt:

list(APPEND CMAKE_PROGRAM_PATH ${CONAN_BIN_DIRS})

find_package(flextool MODULE REQUIRED)

find_program(flextool flextool NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH)
message(STATUS "find_program for flextool ${flextool}")

if(${flextool} STREQUAL "")
    message(FATAL_ERROR "flextool not found ${flextool}")
endif()

Run configure step for your cmake based project. See https://cmake.org/cmake/help/latest/guide/tutorial/index.html for details

Make sure that added message prints valid path to flextool installed using conan package manager.

Now you can use execute_process to run ${flextool} using cmake with desired arguments.

Example that prints version of flextool:

# NOTE: You may want to generate files only if some source files were changed, so prefer `add_custom_command` with `DEPENDS` to `execute_process`
execute_process(
  COMMAND ${flextool}
          --version
  RESULT_VARIABLE retcode
  ERROR_VARIABLE _ERROR_VARIABLE)
if(NOT "${retcode}" STREQUAL "0")
  message(FATAL_ERROR "Bad exit status ${retcode} ${_ERROR_VARIABLE}")
endif()
message(STATUS "flextool output: ${retcode} ${_ERROR_VARIABLE}")

You can find full code of example project at https://github.com/blockspacer/flex_meta_demo/blob/master/CMakeLists.txt

Adding existing plugins to conanfile and CMakeLists.txt

flextool uses plugins to execute custom logic while parsing code using clang LibTooling.

Make sure that your project uses conanfile.py. See https://docs.conan.io/en/latest/mastering/conanfile_py.html for details

Add in conanfile.py:

      self.requires("flex_reflect_plugin/master@conan/stable")

      self.requires("flex_meta_plugin/master@conan/stable")

      self.requires("flex_support_headers/master@conan/stable")

Where flex_meta_plugin is plugin example that can be found at https://github.com/blockspacer/flex_meta_plugin

Where flex_reflect_plugin is plugin example that can be found at https://github.com/blockspacer/flex_reflect_plugin

Where flex_support_headers is header-only library that can simplify usage of Cling C++ interpreter with flextool. flex_support_headers can be found at https://github.com/blockspacer/flex_support_headers

Make sure you installed flex_reflect_plugin, flex_meta_plugin and flex_support_headers using conan. You can find installation instructions in README.md at https://github.com/blockspacer/flex_meta_plugin, https://github.com/blockspacer/flex_support_headers and https://github.com/blockspacer/flex_support_headers

Note that plugins can depend on each other, so order of conan create may be important (or you will get build errors).

If you built flextool using conan create, than flextool must be in output of command conan search flextool. Same must apply for plugins, example: conan search flex_meta_plugin

Run conan install. See https://docs.conan.io/en/latest/using_packages/conanfile_txt.html for details

Now cmake must be able to find path for installed plugins

Add in CMakeLists.txt:

set(flex_reflect_plugin
  ${CONAN_FLEX_REFLECT_PLUGIN_ROOT}/lib/flex_reflect_plugin${CMAKE_SHARED_LIBRARY_SUFFIX}
)
message(STATUS "flex_reflect_plugin=${flex_reflect_plugin}")

Run configure step for your cmake based project. See https://cmake.org/cmake/help/latest/guide/tutorial/index.html for details

Make sure that added message prints valid path to flex_reflect_plugin installed using conan package manager.

You can pass command-line argument --load_plugin to flextool that must conatain path to plugin file. We use ${CMAKE_SHARED_LIBRARY_SUFFIX} that is suffix to use for the end of a shared library filename, .dll on Windows, .so on Linux.

Usage example:

# NOTE: You may want to generate files only if some source files were changed, so prefer `add_custom_command` with `DEPENDS` to `execute_process`
execute_process(
  COMMAND ${flextool}
          --indir=${CMAKE_CURRENT_SOURCE_DIR}
          --outdir=${flextool_outdir}
          --load_plugin ${flex_reflect_plugin}
          --load_plugin ${flex_meta_plugin}
          --extra-arg=-I${cling_includes}
          --extra-arg=-I${clang_includes}
          --extra-arg=-I${chromium_base_headers}
          --extra-arg=-I${chromium_build_util_headers}
          --extra-arg=-I${basis_headers}
          --extra-arg=-I${flexlib_headers}
          --extra-arg=-I${flextool_outdir}
          --extra-arg=-Wno-undefined-inline
          ${flextool_extra_args}
          ${flextool_input_files}
          --cling_scripts=${flex_support_headers}
          #TIMEOUT 7200 # sec
  RESULT_VARIABLE retcode
  ERROR_VARIABLE _ERROR_VARIABLE)
if(NOT "${retcode}" STREQUAL "0")
  message(FATAL_ERROR "Bad exit status ${retcode} ${_ERROR_VARIABLE}")
endif()
message(STATUS "flextool output: ${retcode} ${_ERROR_VARIABLE}")

# Set GENERATED properties of your generated source file.
# So cmake won't complain about missing source file.
set_source_files_properties(
  ${generated_files}
  PROPERTIES GENERATED 1)

Make sure you changed ${flextool_outdir} to directory path where generated files must be stored.

You may want to generate files only if some source files were changed, so prefer add_custom_command with DEPENDS to execute_process:

# you can link with library via --extra-arg=-l
# example: --extra-arg=-l${flexlib_file}
add_custom_command(
    OUTPUT ${generated_typeclasses}
    # code generator COMMAND will only be launched
    # if some of DEPENDS files were changed.
    DEPENDS
      ${flextool_input_files}
    COMMAND
      ${CMAKE_COMMAND} -E echo " Removing ${generated_typeclasses}."
    COMMAND
      ${CMAKE_COMMAND} -E remove ${generated_typeclasses}
    COMMAND
      ${flextool}
        --vmodule=*=200 --enable-logging=stderr --log-level=100
        --indir=${CMAKE_SOURCE_DIR}
        --outdir=${flextool_outdir}
        --load_plugin=${flex_squarets_plugin}
        #--load_plugin=${flex_meta_plugin}
        --load_plugin=${flex_reflect_plugin}
        --load_plugin=${${LIB_NAME}_file}
        --extra-arg=-I${cling_includes}
        --extra-arg=-I${clang_includes}
        --extra-arg=-I${corrade_includes}
        --extra-arg=-I${entt_includes}
        # NOTE: Cling does not support compile_commands.json
        # so we must add headers used by `flex_support_headers` package
        # place into `flex_support_headers` includes that can be used by Cling
        --extra-arg=-I${chromium_base_headers}
        --extra-arg=-I${chromium_build_util_headers}
        --extra-arg=-I${basis_headers}
        --extra-arg=-I${flexlib_headers}
        --extra-arg=-I${flextool_outdir}
        --extra-arg=-DCLING_IS_ON=1
        # path to tests/example_datatypes.hpp
        --extra-arg=-I${CMAKE_CURRENT_SOURCE_DIR}
        --extra-arg=-Wno-undefined-inline
        ${flextool_extra_args}
        ${flextool_input_files}
        # cling_scripts must be ending argument
        --cling_scripts=${flex_support_headers}
        --cling_scripts=${flex_typeclass_plugin_settings}
    DEPENDS ${LIB_NAME} ${ROOT_PROJECT_LIB} ${${LIB_NAME}_file}
    # NOTE: uses COMMAND_EXPAND_LISTS
    # to support generator expressions
    # see https://cmake.org/cmake/help/v3.13/command/add_custom_target.html
    COMMAND_EXPAND_LISTS
    COMMENT "running ${flextool}"
    VERBATIM # to support \t for example
)

Usually you do not need to duplicate target compile definitions (via --extra-arg=-D) or include directories (via --extra-arg=-I). You can use code similar to:

# NOTE: add into conanfile.py:
# self.build_requires("cmake_helper_utils/master@conan/stable")

find_package(cmake_helper_utils REQUIRED)

# from cmake_helper_utils (conan package https://github.com/blockspacer/cmake_helper_utils_conan )
get_all_compile_definitions(collected_defines
  ${LIB_NAME}
)

# from cmake_helper_utils (conan package https://github.com/blockspacer/cmake_helper_utils_conan )
get_all_include_directories(collected_includes
  ${LIB_NAME}
)

And add into flextool arguments:

  # NOTE: generator expression, expands during build time
  # if the ${ITEM} is non-empty, then append it
  $<$<BOOL:${collected_defines}>:--extra-arg=-D$<JOIN:${collected_defines}, --extra-arg=-D>>
  # NOTE: generator expression, expands during build time
  # if the ${ITEM} is non-empty, then append it
  $<$<BOOL:${collected_includes}>:--extra-arg=-I$<JOIN:${collected_includes}, --extra-arg=-I>>

You can find full code of example project at https://github.com/blockspacer/flex_meta_demo/blob/master/CMakeLists.txt

You can also find useful CMakeLists.txt from flex_typeclass_plugin at https://github.com/blockspacer/flex_typeclass_plugin/blob/master/CMakeLists.txt

Running flextool from conanfile

Usually you want to execute flextool as part of build or configure step using CMakeLists.txt

But if you want to test plugin created for flextool, than you can run flextool as part of test_package/conanfile.py

You can find full code at https://github.com/blockspacer/flex_meta_plugin/blob/master/test_package/conanfile.py

    def test(self):
        if not tools.cross_building(self.settings):
            self.output.info('self.source_folder = %s' % (self.source_folder))
            ext = ".so" if os_info.is_linux else ".dll"
            #
            flex_reflect_plugin_ROOT = self.deps_cpp_info["flex_reflect_plugin"].rootpath
            flex_reflect_plugin_file = flex_reflect_plugin_ROOT
            flex_reflect_plugin_file = os.path.join(flex_reflect_plugin_file, "lib")
            flex_reflect_plugin_file = os.path.join(flex_reflect_plugin_file, "flex_reflect_plugin" + ext)
            self.output.info('flex_reflect_plugin_file = %s' % (flex_reflect_plugin_file))
            #
            flex_meta_plugin_ROOT = self.deps_cpp_info["flex_meta_plugin"].rootpath
            flex_meta_plugin_file = flex_meta_plugin_ROOT
            flex_meta_plugin_file = os.path.join(flex_meta_plugin_file, "lib")
            flex_meta_plugin_file = os.path.join(flex_meta_plugin_file, "flex_meta_plugin" + ext)
            self.output.info('flex_meta_plugin_file = %s' % (flex_meta_plugin_file))
            #
            # cling_includes must point to cling/Interpreter/RuntimeUniverse.h
            cling_conan_ROOT = self.deps_cpp_info["cling_conan"].rootpath
            cling_includes = cling_conan_ROOT
            cling_includes = os.path.join(cling_includes, "include")
            self.output.info('cling_includes = %s' % (cling_includes))
            #
            # clang_includes must point to stddef.h from lib/clang/5.0.0/include
            clang_includes = cling_conan_ROOT
            clang_includes = os.path.join(clang_includes, "lib")
            clang_includes = os.path.join(clang_includes, "clang")
            clang_includes = os.path.join(clang_includes, "5.0.0")
            clang_includes = os.path.join(clang_includes, "include")
            self.output.info('clang_includes = %s' % (clang_includes))
            #
            flextool_cmd = "flextool" \
              " --outdir ." \
              " --indir ." \
              " --load_plugin {}" \
              " --load_plugin {}" \
              " --extra-arg=-I{}" \
              " --extra-arg=-I{}" \
              " {}/main.cpp".format(
              flex_reflect_plugin_file, flex_meta_plugin_file, cling_includes, clang_includes, self.source_folder)
            self.output.info('flextool_cmd = %s' % (flextool_cmd))
            self.run(flextool_cmd, run_environment=True)

For contibutors

Fell free to open GitHub Issue if you know how to improve that page