diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
commit | 72231250ed81e10d66bfe70701e64fa5fe50f712 (patch) | |
tree | 2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /functions.cmake |
Publish
Diffstat (limited to 'functions.cmake')
-rw-r--r-- | functions.cmake | 465 |
1 files changed, 465 insertions, 0 deletions
diff --git a/functions.cmake b/functions.cmake new file mode 100644 index 00000000000..c75a454923b --- /dev/null +++ b/functions.cmake @@ -0,0 +1,465 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# @author Vegard Sjonfjell + +function(vespa_add_module_dependency OTHER_TARGET_OR_LIB) + if (TARGET ${OTHER_TARGET_OR_LIB}) + get_target_property(OTHER_TARGET_TYPE ${OTHER_TARGET_OR_LIB} TYPE) + if(OTHER_TARGET_TYPE STREQUAL OBJECT_LIBRARY) + include_directories($<TARGET_PROPERTY:${OTHER_TARGET_OR_LIB},INTERFACE_INCLUDE_DIRECTORIES>) + return() + endif() + endif() + + link_libraries(${OTHER_TARGET_OR_LIB}) +endfunction() + +function(vespa_add_target_dependency TARGET OTHER_TARGET) + get_target_property(TARGET_TYPE ${TARGET} TYPE) + + # (Weak) dependency between object library and other target + if(TARGET_TYPE STREQUAL OBJECT_LIBRARY) + add_dependencies(${TARGET} ${OTHER_TARGET}) + target_include_directories(${TARGET} PRIVATE $<TARGET_PROPERTY:${OTHER_TARGET},INTERFACE_INCLUDE_DIRECTORIES>) + return() + endif() + + if(TARGET_TYPE STREQUAL INTERFACE_LIBRARY) + set(VISIBILITY INTERFACE) + else() + set(VISIBILITY PUBLIC) + endif() + + target_link_libraries(${TARGET} ${VISIBILITY} ${OTHER_TARGET}) +endfunction() + +function(vespa_add_target_external_dependency TARGET LIB) + get_target_property(TARGET_TYPE ${TARGET} TYPE) + + # TODO: Use generator expressions in target_link_libraries + if(TARGET_TYPE STREQUAL OBJECT_LIBRARY) + return() + elseif(TARGET_TYPE STREQUAL INTERFACE_LIBRARY) + set(VISIBILITY INTERFACE) + else() + set(VISIBILITY PUBLIC) + endif() + + target_link_libraries(${TARGET} ${VISIBILITY} ${LIB}) +endfunction() + +function(vespa_add_package_dependency PACKAGE_NAME) + find_package(${PACKAGE_NAME} REQUIRED) + string(TOUPPER ${PACKAGE_NAME} PACKAGE_NAME) + set(PACKAGE_INCLUDE_DIR ${${PACKAGE_NAME}_INCLUDE_DIR}) + set(PACKAGE_LIBRARIES ${${PACKAGE_NAME}_LIBRARIES}) + link_libraries(${PACKAGE_LIBRARIES}) + include_directories(SYSTEM ${PACKAGE_INCLUDE_DIR}) +endfunction() + +# TODO: Merge this function into add_target_system_dependency +function(vespa_add_target_package_dependency TARGET PACKAGE_NAME) + find_package(${PACKAGE_NAME} REQUIRED) + string(TOUPPER ${PACKAGE_NAME} PACKAGE_NAME) + set(PACKAGE_INCLUDE_DIR ${${PACKAGE_NAME}_INCLUDE_DIR}) + set(PACKAGE_LIBRARIES ${${PACKAGE_NAME}_LIBRARIES}) + target_link_libraries(${TARGET} PUBLIC ${PACKAGE_LIBRARIES}) + target_include_directories(${TARGET} SYSTEM PUBLIC ${PACKAGE_INCLUDE_DIR}) +endfunction() + +function(vespa_add_target_system_dependency TARGET PACKAGE_NAME) + get_target_property(TARGET_TYPE ${TARGET} TYPE) + + if(TARGET_TYPE STREQUAL INTERFACE_LIBRARY) + set(VISIBILITY INTERFACE) + else() + set(VISIBILITY PUBLIC) + endif() + + if(TARGET_TYPE STREQUAL OBJECT_LIBRARY) + return() + endif() + + if(TARGET_TYPE STREQUAL INTERFACE_LIBRARY) + return() + endif() + + # Hacks <3 + if(${PACKAGE_NAME} STREQUAL llvm) + set(PACKAGE_NAME LLVM) + endif() + + # If third (optional) parameter is STATIC, link with static library. + # Library name specified by last optional parameter + if (ARGV2 STREQUAL "STATIC") + target_link_libraries(${TARGET} PUBLIC ${PACKAGE_DIR}/lib64/lib${ARGV3}.a) + # If third (optional) parameter is something else, link with shared library with that name + elseif (ARGV2) + target_link_libraries(${TARGET} PUBLIC ${ARGV2}) + else() + target_link_libraries(${TARGET} PUBLIC ${PACKAGE_NAME}) + endif() +endfunction() + +function(vespa_generate_config TARGET RELATIVE_CONFIG_DEF_PATH) + # Generate config-<name>.cpp and config-<name>.h from <name>.def + # Destination directory is always where generate_config is called (or, in the case of out-of-source builds, in the build-tree parallel) + # This may not be the same directory as where the .def file is located. + + # Third parameter lets the user change <name> for the generated files + if (ARGC GREATER 2) + set(CONFIG_NAME ${ARGV2}) + else() + get_filename_component(CONFIG_NAME ${RELATIVE_CONFIG_DEF_PATH} NAME_WE) + endif() + + # configgen.jar takes the parent dir of the destination dir and the destination dirname as separate parameters + # so it can produce the correct include statements within the generated .cpp-file (silent cry) + # Make config path an absolute_path + set(CONFIG_DEF_PATH ${CMAKE_CURRENT_LIST_DIR}/${RELATIVE_CONFIG_DEF_PATH}) + + # Config destination is the + set(CONFIG_DEST_DIR ${CMAKE_CURRENT_BINARY_DIR}) + + # Get parent of destination directory + set(CONFIG_DEST_PARENT_DIR ${CONFIG_DEST_DIR}/..) + + # Get destination dirname + get_filename_component(CONFIG_DEST_DIRNAME ${CMAKE_CURRENT_BINARY_DIR} NAME) + + set(CONFIG_H_PATH ${CONFIG_DEST_DIR}/config-${CONFIG_NAME}.h) + set(CONFIG_CPP_PATH ${CONFIG_DEST_DIR}/config-${CONFIG_NAME}.cpp) + + add_custom_command( + OUTPUT ${CONFIG_H_PATH} ${CONFIG_CPP_PATH} + COMMAND java -Dconfig.spec=${CONFIG_DEF_PATH} -Dconfig.dest=${CONFIG_DEST_PARENT_DIR} -Dconfig.lang=cppng -Dconfig.requireNamespace=false -Dconfig.subdir=${CONFIG_DEST_DIRNAME} -Dconfig.dumpTree=false -Xms64m -Xmx64m -jar ${PROJECT_SOURCE_DIR}/configgen/target/configgen.jar + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/.. + MAIN_DEPENDENCY ${CONFIG_DEF_PATH} + ) + + # Add generated to sources for target + target_sources(${TARGET} PRIVATE ${CONFIG_H_PATH} ${CONFIG_CPP_PATH}) + + # Needed to be able to do a #include <CONFIG_DEST_DIRNAME/config-<name>.h> for this target + # This is used within the generated config-<name>.cpp + # TODO: Should modify configgen to use #include <vespa/<modulename>/config-<name>.h> instead + target_include_directories(${TARGET} PRIVATE ${CONFIG_DEST_PARENT_DIR}) + + # Needed to be able to do a #include <config-<name>.h> for this target + # This is used within some unit tests + target_include_directories(${TARGET} PRIVATE ${CONFIG_DEST_DIR}) +endfunction() + +function(vespa_add_library TARGET) + cmake_parse_arguments(ARG + "STATIC;OBJECT;INTERFACE" + "INSTALL;OUTPUT_NAME" + "DEPENDS;AFTER;SOURCES" + ${ARGN}) + + __check_target_parameters() + + if(ARG_SOURCES) + set(SOURCE_FILES ${ARG_SOURCES}) + else() + # In the case where no source files are given, we include an empty source file to suppress a warning from CMake + # This way, config-only libraries will not generate lots of build warnings + set(SOURCE_FILES "${CMAKE_SOURCE_DIR}/empty.cpp") + endif() + + if(ARG_OBJECT) + set(LIBRARY_TYPE OBJECT) + elseif(ARG_STATIC) + set(LINKAGE STATIC) + elseif(ARG_INTERFACE) + set(LIBRARY_TYPE INTERFACE) + set(SOURCE_FILES) + endif() + + add_library(${TARGET} ${LINKAGE} ${LIBRARY_TYPE} ${SOURCE_FILES}) + __add_dependencies_to_target() + + if(ARG_INSTALL) + install(TARGETS ${TARGET} DESTINATION ${ARG_INSTALL}) + endif() + + if(ARG_OUTPUT_NAME) + set_target_properties(${TARGET} PROPERTIES OUTPUT_NAME ${ARG_OUTPUT_NAME}) + endif() + + __add_target_to_module(${TARGET}) + __export_include_directories(${TARGET}) +endfunction() + +function(vespa_add_executable TARGET) + cmake_parse_arguments(ARG + "" + "INSTALL;OUTPUT_NAME" + "DEPENDS;AFTER;SOURCES" + ${ARGN}) + + __check_target_parameters() + add_executable(${TARGET} ${ARG_SOURCES}) + __add_dependencies_to_target() + + if(ARG_INSTALL) + install(TARGETS ${TARGET} DESTINATION ${ARG_INSTALL}) + endif() + + if(ARG_OUTPUT_NAME) + set_target_properties(${TARGET} PROPERTIES OUTPUT_NAME ${ARG_OUTPUT_NAME}) + endif() + + __add_target_to_module(${TARGET}) + __export_include_directories(${TARGET}) +endfunction() + +macro(vespa_define_module) + cmake_parse_arguments(ARG + "" + "" + "DEPENDS;EXTERNAL_DEPENDS;LIBS;LIB_DEPENDS;LIB_EXTERNAL_DEPENDS;APPS;APP_DEPENDS;APP_EXTERNAL_DEPENDS;TESTS;TEST_DEPENDS;TEST_EXTERNAL_DEPENDS" + ${ARGN}) + + __initialize_module() + + # Base target dependencies for whole module + foreach(DEPENDEE IN LISTS ARG_DEPENDS) + set(BASE_DEPENDS ${BASE_DEPENDS} ${DEPENDEE}) + endforeach() + + # External library dependencies for whole module + foreach(DEPENDEE IN LISTS ARG_EXTERNAL_DEPENDS) + set(BASE_EXTERNAL_DEPENDS ${BASE_EXTERNAL_DEPENDS} ${DEPENDEE}) + endforeach() + + # Dependencies for libraries + set(MODULE_DEPENDS ${BASE_DEPENDS}) + foreach(DEPENDEE IN LISTS ARG_LIB_DEPENDS) + set(MODULE_DEPENDS ${MODULE_DEPENDS} ${DEPENDEE}) + endforeach() + + # External library dependencies for libraries + set(MODULE_EXTERNAL_DEPENDS ${BASE_EXTERNAL_DEPENDS}) + foreach(DEPENDEE IN LISTS ARG_LIB_EXTERNAL_DEPENDS) + set(MODULE_EXTERNAL_DEPENDS ${MODULE_EXTERNAL_DEPENDS} ${DEPENDEE}) + endforeach() + + # Add libraries + foreach(DIR IN LISTS ARG_LIBS) + add_subdirectory(${DIR}) + endforeach() + + # Dependencies for apps + set(MODULE_DEPENDS ${BASE_DEPENDS}) + foreach(DEPENDEE IN LISTS ARG_APP_DEPENDS) + set(MODULE_DEPENDS ${MODULE_DEPENDS} ${DEPENDEE}) + endforeach() + + # External library dependencies for apps + set(MODULE_EXTERNAL_DEPENDS ${BASE_EXTERNAL_DEPENDS}) + foreach(DEPENDEE IN LISTS ARG_APP_EXTERNAL_DEPENDS) + set(MODULE_EXTERNAL_DEPENDS ${MODULE_EXTERNAL_DEPENDS} ${DEPENDEE}) + endforeach() + + # Add apps + foreach(DIR IN LISTS ARG_APPS) + add_subdirectory(${DIR}) + endforeach() + + # Dependencies for tests + set(MODULE_DEPENDS ${BASE_DEPENDS}) + foreach(DEPENDEE IN LISTS ARG_TEST_DEPENDS) + set(MODULE_DEPENDS ${MODULE_DEPENDS} ${DEPENDEE}) + endforeach() + + # External library dependencies for tests + set(MODULE_EXTERNAL_DEPENDS ${BASE_EXTERNAL_DEPENDS}) + foreach(DEPENDEE IN LISTS ARG_TEST_EXTERNAL_DEPENDS) + set(MODULE_EXTERNAL_DEPENDS ${MODULE_EXTERNAL_DEPENDS} ${DEPENDEE}) + endforeach() + + # Add tests + foreach(DIR IN LISTS ARG_TESTS) + add_subdirectory(${DIR}) + endforeach() +endmacro() + +function(__is_script_extension COMMAND RESULT_VAR) + set(SCRIPT_EXTENSIONS ".sh" ".bash" ".py" ".pl" ".rb") + + get_filename_component(COMMAND_EXT ${COMMAND} EXT) + if(COMMAND_EXT) + list(FIND SCRIPT_EXTENSIONS ${COMMAND_EXT} RESULT) + if(NOT RESULT EQUAL -1) + set(${RESULT_VAR} TRUE PARENT_SCOPE) + return() + endif() + endif() + + set(${RESULT_VAR} FALSE PARENT_SCOPE) +endfunction() + +function(__is_command_a_script COMMAND RESULT_VAR) + list(GET COMMAND 0 FIRST) + __is_script_extension(${FIRST} IS_SCRIPT_EXT) + + list(LENGTH COMMAND COMMAND_LENGTH) + if(COMMAND_LENGTH GREATER 1 AND NOT IS_SCRIPT_EXT) + list(GET COMMAND 1 SECOND) + __is_script_extension(${SECOND} IS_SCRIPT_EXT) + endif() + + set(${RESULT_VAR} ${IS_SCRIPT_EXT} PARENT_SCOPE) +endfunction() + +function(vespa_add_test) + cmake_parse_arguments(ARG "NO_VALGRIND;RUN_SERIAL;BENCHMARK" "NAME;WORKING_DIRECTORY;ENVIRONMENT" "COMMAND" ${ARGN}) + + if(NOT RUN_BENCHMARKS AND ARG_BENCHMARK) + return() + endif() + + if(VALGRIND_UNIT_TESTS AND NOT ARG_NO_VALGRIND) + if(NOT VALGRIND_EXECUTABLE) + message(FATAL_ERROR "Requested valgrind tests, but could not find valgrind executable.") + endif() + + __is_command_a_script("${ARG_COMMAND}" IS_SCRIPT) + if(IS_SCRIPT) + # For shell scripts, export a VALGRIND environment variable + list(APPEND ARG_ENVIRONMENT "VALGRIND=${VALGRIND_COMMAND}") + else() + # For targets or other executables, prepend valgrind to the command. + # Extract first part of command and expand the executable path if it refers to a executable target + set(COMMAND_REST ${ARG_COMMAND}) + list(GET COMMAND_REST 0 COMMAND_FIRST) + list(REMOVE_AT COMMAND_REST 0) + + if (TARGET ${COMMAND_FIRST}) + set(COMMAND_FIRST $<TARGET_FILE:${COMMAND_FIRST}>) + endif() + set(ARG_COMMAND "${VALGRIND_COMMAND} ${COMMAND_FIRST} ${COMMAND_REST}") + endif() + + separate_arguments(ARG_COMMAND) + endif() + + # If there exists a target with the same name as the test, exclude the target from the "all" target + # Instead, add it to the test target for this module + if (TARGET ${ARG_NAME}) + set_target_properties(${ARG_NAME} PROPERTIES EXCLUDE_FROM_ALL TRUE) + __add_test_target_to_module(${ARG_NAME}) + endif() + + add_test(NAME ${ARG_NAME} COMMAND ${ARG_COMMAND} WORKING_DIRECTORY ${ARG_WORKING_DIRECTORY}) + + if(ARG_ENVIRONMENT) + set_tests_properties(${ARG_NAME} PROPERTIES ENVIRONMENT "${ARG_ENVIRONMENT}") + endif() + + if(ARG_RUN_SERIAL) + set_tests_properties(${TEST_NAME} PROPERTIES RUN_SERIAL TRUE) + endif() +endfunction() + +function(vespa_install_script) + if(ARGC GREATER 2) + install(FILES ${ARGV0} RENAME ${ARGV1} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE DESTINATION ${ARGV2}) + else() + install(FILES ${ARGV0} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE DESTINATION ${ARGV1}) + endif() +endfunction() + +function(vespa_workaround_gcc_bug_67055 SOURCE_FILE) + if(CMAKE_COMPILER_IS_GNUCC) + execute_process(COMMAND ${CMAKE_CPP_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) + if (GCC_VERSION VERSION_LESS "5.3.0") + set_source_files_properties(${SOURCE_FILE} PROPERTIES COMPILE_FLAGS -fno-ipa-icf) + endif() + endif() +endfunction() + +macro(__initialize_module) + # Set a couple of useful variables for this module + set(MODULE_ROOT ${CMAKE_CURRENT_BINARY_DIR}) + get_filename_component(MODULE_NAME ${MODULE_ROOT} NAME) + + get_property(VESPA_MODULES GLOBAL PROPERTY VESPA_MODULES) + set(VESPA_MODULES ${VESPA_MODULES} ${MODULE_NAME}) + set_property(GLOBAL PROPERTY VESPA_MODULES ${VESPA_MODULES}) + + # Add "src" to the module's include directories so it's possible to use nonrelative includes + # This include path is later exported to other targets that depend on targets within this module + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) + + # For generated files that are placed in the build tree (e.g. config-<name>.h) + include_directories(${CMAKE_CURRENT_BINARY_DIR}/src) +endmacro() + +macro(__check_target_parameters) + if(ARG_UNPARSED_ARGUMENTS) + if(NOT ARG_SOURCES) + set(AUX_MESSAGE "\nMaybe you forgot to add the SOURCES parameter?") + endif() + message(FATAL_ERROR "Unrecognized parameters: ${ARG_UNPARSED_ARGUMENTS}.${AUX_MESSAGE}") + endif() +endmacro() + +macro(__add_dependencies_to_target) + # Link with other targets or libraries + foreach(DEPENDEE IN LISTS ARG_DEPENDS) + vespa_add_target_dependency(${TARGET} ${DEPENDEE}) + endforeach() + + # Link with other targets defined as module dependencies + foreach(DEPENDEE IN LISTS MODULE_DEPENDS) + vespa_add_target_dependency(${TARGET} ${DEPENDEE}) + endforeach() + + # Link with other external libraries defined as module external dependencies + foreach(DEPENDEE IN LISTS MODULE_EXTERNAL_DEPENDS) + vespa_add_target_external_dependency(${TARGET} ${DEPENDEE}) + endforeach() + + # Build after these targets + foreach(OTHER_TARGET IN LISTS ARG_AFTER) + add_dependencies(${TARGET} ${OTHER_TARGET}) + target_include_directories(${TARGET} PRIVATE $<TARGET_PROPERTY:${OTHER_TARGET},INTERFACE_INCLUDE_DIRECTORIES>) + endforeach() +endmacro() + +function(__add_target_to_module TARGET) + set_property(GLOBAL APPEND PROPERTY MODULE_${MODULE_NAME}_TARGETS ${TARGET}) +endfunction() + +function(__add_test_target_to_module TARGET) + set_property(GLOBAL APPEND PROPERTY MODULE_${MODULE_NAME}_TEST_TARGETS ${TARGET}) +endfunction() + +function(__create_module_targets PROPERTY_POSTFIX TARGET_POSTFIX) + get_property(VESPA_MODULES GLOBAL PROPERTY VESPA_MODULES) + set(OUTPUT_ALL_TARGET "all_${TARGET_POSTFIX}s") + add_custom_target(${OUTPUT_ALL_TARGET}) + + foreach(MODULE IN LISTS VESPA_MODULES) + get_property(TARGETS GLOBAL PROPERTY MODULE_${MODULE}_${PROPERTY_POSTFIX}) + set(OUTPUT_TARGET "${MODULE}+${TARGET_POSTFIX}") + add_custom_target(${OUTPUT_TARGET}) + + foreach(TARGET IN LISTS TARGETS) + add_dependencies(${OUTPUT_TARGET} ${TARGET}) + endforeach() + + add_dependencies(${OUTPUT_ALL_TARGET} ${OUTPUT_TARGET}) + endforeach() +endfunction() + +function(__export_include_directories TARGET) + get_directory_property(LOCAL_INCLUDE_DIRS INCLUDE_DIRECTORIES) + get_target_property(TARGET_TYPE ${TARGET} TYPE) + if(TARGET_TYPE STREQUAL INTERFACE_LIBRARY) + target_include_directories(${TARGET} SYSTEM INTERFACE ${LOCAL_INCLUDE_DIRS}) + else() + target_include_directories(${TARGET} SYSTEM PUBLIC ${LOCAL_INCLUDE_DIRS}) + endif() +endfunction() |