building_projects
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