1if(doctest_force_link_static_lib_in_target_included) 2 return() 3endif() 4set(doctest_force_link_static_lib_in_target_included true) 5 6cmake_minimum_required(VERSION 3.0) 7 8# includes the file to the source with compiler flags 9function(doctest_include_file_in_sources header sources) 10 foreach(src ${sources}) 11 if(${src} MATCHES \\.\(cc|cp|cpp|CPP|c\\+\\+|cxx\)$) 12 # get old flags 13 get_source_file_property(old_compile_flags ${src} COMPILE_FLAGS) 14 if(old_compile_flags STREQUAL "NOTFOUND") 15 set(old_compile_flags "") 16 endif() 17 18 # update flags 19 if(MSVC) 20 set_source_files_properties(${src} PROPERTIES COMPILE_FLAGS 21 "${old_compile_flags} /FI\"${header}\"") 22 else() 23 set_source_files_properties(${src} PROPERTIES COMPILE_FLAGS 24 "${old_compile_flags} -include \"${header}\"") 25 endif() 26 endif() 27 endforeach() 28endfunction() 29 30# this is the magic function - forces every object file from the library to be linked into the target (dll or executable) 31# it doesn't work in 2 scenarios: 32# - either the target or the library uses a precompiled header - see the end of this issue for details: https://github.com/onqtam/doctest/issues/21 33# - either the target or the library is an imported target (pre-built) and not built within the current cmake tree 34# Alternatives: 35# - use CMake object libraries instead of static libraries - >> THIS IS ACTUALLY PREFERRED << to all this CMake trickery 36# - checkout these 2 repositories: 37# - https://github.com/pthom/cmake_registertest 38# - https://github.com/pthom/doctest_registerlibrary 39function(doctest_force_link_static_lib_in_target target lib) 40 # check if the library has generated dummy headers 41 get_target_property(DDH ${lib} DOCTEST_DUMMY_HEADER) 42 get_target_property(LIB_NAME ${lib} NAME) 43 if(${DDH} STREQUAL "DDH-NOTFOUND") 44 # figure out the paths and names of the dummy headers - should be in the build folder for the target 45 set(BD ${CMAKE_CURRENT_BINARY_DIR}) 46 if(NOT CMAKE_VERSION VERSION_LESS 3.4) 47 get_target_property(BD ${lib} BINARY_DIR) # 'BINARY_DIR' target property unsupported before CMake 3.4 ... 48 endif() 49 set(dummy_dir ${BD}/${LIB_NAME}_DOCTEST_STATIC_LIB_FORCE_LINK_DUMMIES/) 50 set(dummy_header ${dummy_dir}/all_dummies.h) 51 file(MAKE_DIRECTORY ${dummy_dir}) 52 53 # create a dummy header for each source file, include a dummy function in it and include it in the source file 54 set(curr_dummy "0") 55 set(DLL_PRIVATE "#ifndef _WIN32\n#define DLL_PRIVATE __attribute__ ((visibility (\"hidden\")))\n#else\n#define DLL_PRIVATE\n#endif\n\n") 56 get_target_property(lib_sources ${lib} SOURCES) 57 foreach(src ${lib_sources}) 58 if(${src} MATCHES \\.\(cc|cp|cpp|CPP|c\\+\\+|cxx\)$) 59 math(EXPR curr_dummy "${curr_dummy} + 1") 60 61 set(curr_dummy_header ${dummy_dir}/dummy_${curr_dummy}.h) 62 file(WRITE ${curr_dummy_header} "${DLL_PRIVATE}namespace doctest { namespace detail { DLL_PRIVATE int dummy_for_${LIB_NAME}_${curr_dummy}(); DLL_PRIVATE int dummy_for_${LIB_NAME}_${curr_dummy}() { return ${curr_dummy}; } } }\n") 63 doctest_include_file_in_sources(${curr_dummy_header} ${src}) 64 endif() 65 endforeach() 66 set(total_dummies ${curr_dummy}) 67 68 # create the master dummy header 69 file(WRITE ${dummy_header} "${DLL_PRIVATE}namespace doctest { namespace detail {\n\n") 70 71 # forward declare the dummy functions in the master dummy header 72 foreach(curr_dummy RANGE 1 ${total_dummies}) 73 file(APPEND ${dummy_header} "DLL_PRIVATE int dummy_for_${LIB_NAME}_${curr_dummy}();\n") 74 endforeach() 75 76 # call the dummy functions in the master dummy header 77 file(APPEND ${dummy_header} "\nDLL_PRIVATE int dummies_for_${LIB_NAME}();\nDLL_PRIVATE int dummies_for_${LIB_NAME}() {\n int res = 0;\n") 78 foreach(curr_dummy RANGE 1 ${total_dummies}) 79 file(APPEND ${dummy_header} " res += dummy_for_${LIB_NAME}_${curr_dummy}();\n") 80 endforeach() 81 file(APPEND ${dummy_header} " return res;\n}\n\n} } // namespaces\n") 82 83 # set the dummy header property so we don't recreate the dummy headers the next time this macro is called for this library 84 set_target_properties(${lib} PROPERTIES DOCTEST_DUMMY_HEADER ${dummy_header}) 85 set(DDH ${dummy_header}) 86 endif() 87 88 get_target_property(DFLLTD ${target} DOCTEST_FORCE_LINKED_LIBRARIES_THROUGH_DUMMIES) 89 get_target_property(target_sources ${target} SOURCES) 90 91 if("${DFLLTD}" STREQUAL "DFLLTD-NOTFOUND") 92 # if no library has been force linked to this target 93 foreach(src ${target_sources}) 94 if(${src} MATCHES \\.\(cc|cp|cpp|CPP|c\\+\\+|cxx\)$) 95 doctest_include_file_in_sources(${DDH} ${src}) 96 break() 97 endif() 98 endforeach() 99 100 # add the library as force linked to this target 101 set_target_properties(${target} PROPERTIES DOCTEST_FORCE_LINKED_LIBRARIES_THROUGH_DUMMIES ${LIB_NAME}) 102 else() 103 # if this particular library hasn't been force linked to this target 104 list(FIND DFLLTD ${LIB_NAME} lib_forced_in_target) 105 if(${lib_forced_in_target} EQUAL -1) 106 foreach(src ${target_sources}) 107 if(${src} MATCHES \\.\(cc|cp|cpp|CPP|c\\+\\+|cxx\)$) 108 doctest_include_file_in_sources(${DDH} ${src}) 109 break() 110 endif() 111 endforeach() 112 113 # add this library to the list of force linked libraries for this target 114 list(APPEND DFLLTD ${LIB_NAME}) 115 set_target_properties(${target} PROPERTIES DOCTEST_FORCE_LINKED_LIBRARIES_THROUGH_DUMMIES "${DFLLTD}") 116 else() 117 message(AUTHOR_WARNING "LIBRARY \"${lib}\" ALREADY FORCE-LINKED TO TARGET \"${target}\"!") 118 endif() 119 endif() 120endfunction() 121 122# a utility function to create an executable for a static library with tests - as requested by https://github.com/pthom 123function(doctest_make_exe_for_static_lib exe_name lib_name) 124 set(exe_dir ${CMAKE_CURRENT_BINARY_DIR}/${exe_name}_generated_sources) 125 file(MAKE_DIRECTORY ${exe_dir}) 126 file(WRITE ${exe_dir}/main.cpp "#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN\n#include \"doctest.h\"\n") 127 add_executable(${exe_name} ${exe_dir}/main.cpp) 128 target_link_libraries(${exe_name} ${lib_name}) 129 doctest_force_link_static_lib_in_target(${exe_name} ${lib_name}) 130 add_test(NAME ${exe_name} COMMAND $<TARGET_FILE:${exe_name}>) 131endfunction() 132