Home | History | Annotate | Download | only in CMake
      1 # This file is part of CMake-codecov.
      2 #
      3 # Copyright (c)
      4 #   2015-2017 RWTH Aachen University, Federal Republic of Germany
      5 #
      6 # See the LICENSE file in the package base directory for details
      7 #
      8 # Written by Alexander Haase, alexander.haase (a] rwth-aachen.de
      9 #
     10 
     11 
     12 # configuration
     13 set(LCOV_DATA_PATH "${CMAKE_BINARY_DIR}/lcov/data")
     14 set(LCOV_DATA_PATH_INIT "${LCOV_DATA_PATH}/init")
     15 set(LCOV_DATA_PATH_CAPTURE "${LCOV_DATA_PATH}/capture")
     16 set(LCOV_HTML_PATH "${CMAKE_BINARY_DIR}/lcov/html")
     17 
     18 
     19 
     20 
     21 # Search for Gcov which is used by Lcov.
     22 find_package(Gcov)
     23 
     24 
     25 
     26 
     27 # This function will add lcov evaluation for target <TNAME>. Only sources of
     28 # this target will be evaluated and no dependencies will be added. It will call
     29 # geninfo on any source file of <TNAME> once and store the info file in the same
     30 # directory.
     31 #
     32 # Note: This function is only a wrapper to define this function always, even if
     33 #   coverage is not supported by the compiler or disabled. This function must
     34 #   be defined here, because the module will be exited, if there is no coverage
     35 #   support by the compiler or it is disabled by the user.
     36 function (add_lcov_target TNAME)
     37 	if (LCOV_FOUND)
     38 		# capture initial coverage data
     39 		lcov_capture_initial_tgt(${TNAME})
     40 
     41 		# capture coverage data after execution
     42 		lcov_capture_tgt(${TNAME})
     43 	endif ()
     44 endfunction (add_lcov_target)
     45 
     46 
     47 
     48 
     49 # include required Modules
     50 include(FindPackageHandleStandardArgs)
     51 
     52 # Search for required lcov binaries.
     53 find_program(LCOV_BIN lcov)
     54 find_program(GENINFO_BIN geninfo)
     55 find_program(GENHTML_BIN genhtml)
     56 find_package_handle_standard_args(lcov
     57 	REQUIRED_VARS LCOV_BIN GENINFO_BIN GENHTML_BIN
     58 )
     59 
     60 # enable genhtml C++ demangeling, if c++filt is found.
     61 set(GENHTML_CPPFILT_FLAG "")
     62 find_program(CPPFILT_BIN c++filt)
     63 if (NOT CPPFILT_BIN STREQUAL "")
     64 	set(GENHTML_CPPFILT_FLAG "--demangle-cpp")
     65 endif (NOT CPPFILT_BIN STREQUAL "")
     66 
     67 # enable no-external flag for lcov, if available.
     68 if (GENINFO_BIN AND NOT DEFINED GENINFO_EXTERN_FLAG)
     69 	set(FLAG "")
     70 	execute_process(COMMAND ${GENINFO_BIN} --help OUTPUT_VARIABLE GENINFO_HELP)
     71 	string(REGEX MATCH "external" GENINFO_RES "${GENINFO_HELP}")
     72 	if (GENINFO_RES)
     73 		set(FLAG "--no-external")
     74 	endif ()
     75 
     76 	set(GENINFO_EXTERN_FLAG "${FLAG}"
     77 		CACHE STRING "Geninfo flag to exclude system sources.")
     78 endif ()
     79 
     80 # If Lcov was not found, exit module now.
     81 if (NOT LCOV_FOUND)
     82 	return()
     83 endif (NOT LCOV_FOUND)
     84 
     85 
     86 
     87 
     88 # Create directories to be used.
     89 file(MAKE_DIRECTORY ${LCOV_DATA_PATH_INIT})
     90 file(MAKE_DIRECTORY ${LCOV_DATA_PATH_CAPTURE})
     91 
     92 set(LCOV_REMOVE_PATTERNS "")
     93 
     94 # This function will merge lcov files to a single target file. Additional lcov
     95 # flags may be set with setting LCOV_EXTRA_FLAGS before calling this function.
     96 function (lcov_merge_files OUTFILE ...)
     97 	# Remove ${OUTFILE} from ${ARGV} and generate lcov parameters with files.
     98 	list(REMOVE_AT ARGV 0)
     99 
    100 	# Generate merged file.
    101 	string(REPLACE "${CMAKE_BINARY_DIR}/" "" FILE_REL "${OUTFILE}")
    102 	add_custom_command(OUTPUT "${OUTFILE}.raw"
    103 		COMMAND cat ${ARGV} > ${OUTFILE}.raw
    104 		DEPENDS ${ARGV}
    105 		COMMENT "Generating ${FILE_REL}"
    106 	)
    107 
    108 	add_custom_command(OUTPUT "${OUTFILE}"
    109 		COMMAND ${LCOV_BIN} --quiet -a ${OUTFILE}.raw --output-file ${OUTFILE}
    110 			--base-directory ${PROJECT_SOURCE_DIR} ${LCOV_EXTRA_FLAGS}
    111 		COMMAND ${LCOV_BIN} --quiet -r ${OUTFILE} ${LCOV_REMOVE_PATTERNS}
    112 			--output-file ${OUTFILE} ${LCOV_EXTRA_FLAGS}
    113 		DEPENDS ${OUTFILE}.raw
    114 		COMMENT "Post-processing ${FILE_REL}"
    115 	)
    116 endfunction ()
    117 
    118 
    119 
    120 
    121 # Add a new global target to generate initial coverage reports for all targets.
    122 # This target will be used to generate the global initial info file, which is
    123 # used to gather even empty report data.
    124 if (NOT TARGET lcov-capture-init)
    125 	add_custom_target(lcov-capture-init)
    126 	set(LCOV_CAPTURE_INIT_FILES "" CACHE INTERNAL "")
    127 endif (NOT TARGET lcov-capture-init)
    128 
    129 
    130 # This function will add initial capture of coverage data for target <TNAME>,
    131 # which is needed to get also data for objects, which were not loaded at
    132 # execution time. It will call geninfo for every source file of <TNAME> once and
    133 # store the info file in the same directory.
    134 function (lcov_capture_initial_tgt TNAME)
    135 	# We don't have to check, if the target has support for coverage, thus this
    136 	# will be checked by add_coverage_target in Findcoverage.cmake. Instead we
    137 	# have to determine which gcov binary to use.
    138 	get_target_property(TSOURCES ${TNAME} SOURCES)
    139 	set(SOURCES "")
    140 	set(TCOMPILER "")
    141 	foreach (FILE ${TSOURCES})
    142 		codecov_path_of_source(${FILE} FILE)
    143 		if (NOT "${FILE}" STREQUAL "")
    144 			codecov_lang_of_source(${FILE} LANG)
    145 			if (NOT "${LANG}" STREQUAL "")
    146 				list(APPEND SOURCES "${FILE}")
    147 				set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID})
    148 			endif ()
    149 		endif ()
    150 	endforeach ()
    151 
    152 	# If no gcov binary was found, coverage data can't be evaluated.
    153 	if (NOT GCOV_${TCOMPILER}_BIN)
    154 		message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.")
    155 		return()
    156 	endif ()
    157 
    158 	set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}")
    159 	set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}")
    160 
    161 
    162 	set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir)
    163 	set(GENINFO_FILES "")
    164 	foreach(FILE ${SOURCES})
    165 		# generate empty coverage files
    166 		set(OUTFILE "${TDIR}/${FILE}.info.init")
    167 		list(APPEND GENINFO_FILES ${OUTFILE})
    168 
    169 		add_custom_command(OUTPUT ${OUTFILE} COMMAND ${GCOV_ENV} ${GENINFO_BIN}
    170 				--quiet --base-directory ${PROJECT_SOURCE_DIR} --initial
    171 				--gcov-tool ${GCOV_BIN} --output-filename ${OUTFILE}
    172 				${GENINFO_EXTERN_FLAG} ${TDIR}/${FILE}.gcno
    173 			DEPENDS ${TNAME}
    174 			COMMENT "Capturing initial coverage data for ${FILE}"
    175 		)
    176 	endforeach()
    177 
    178 	# Concatenate all files generated by geninfo to a single file per target.
    179 	set(OUTFILE "${LCOV_DATA_PATH_INIT}/${TNAME}.info")
    180 	set(LCOV_EXTRA_FLAGS "--initial")
    181 	lcov_merge_files("${OUTFILE}" ${GENINFO_FILES})
    182 	add_custom_target(${TNAME}-capture-init ALL DEPENDS ${OUTFILE})
    183 
    184 	# add geninfo file generation to global lcov-geninfo target
    185 	add_dependencies(lcov-capture-init ${TNAME}-capture-init)
    186 	set(LCOV_CAPTURE_INIT_FILES "${LCOV_CAPTURE_INIT_FILES}"
    187 		"${OUTFILE}" CACHE INTERNAL ""
    188 	)
    189 endfunction (lcov_capture_initial_tgt)
    190 
    191 
    192 # This function will generate the global info file for all targets. It has to be
    193 # called after all other CMake functions in the root CMakeLists.txt file, to get
    194 # a full list of all targets that generate coverage data.
    195 function (lcov_capture_initial)
    196 	# Skip this function (and do not create the following targets), if there are
    197 	# no input files.
    198 	if ("${LCOV_CAPTURE_INIT_FILES}" STREQUAL "")
    199 		return()
    200 	endif ()
    201 
    202 	# Add a new target to merge the files of all targets.
    203 	set(OUTFILE "${LCOV_DATA_PATH_INIT}/all_targets.info")
    204 	lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_INIT_FILES})
    205 	add_custom_target(lcov-geninfo-init ALL	DEPENDS ${OUTFILE}
    206 		lcov-capture-init
    207 	)
    208 endfunction (lcov_capture_initial)
    209 
    210 
    211 
    212 
    213 # Add a new global target to generate coverage reports for all targets. This
    214 # target will be used to generate the global info file.
    215 if (NOT TARGET lcov-capture)
    216 	add_custom_target(lcov-capture)
    217 	set(LCOV_CAPTURE_FILES "" CACHE INTERNAL "")
    218 endif (NOT TARGET lcov-capture)
    219 
    220 
    221 # This function will add capture of coverage data for target <TNAME>, which is
    222 # needed to get also data for objects, which were not loaded at execution time.
    223 # It will call geninfo for every source file of <TNAME> once and store the info
    224 # file in the same directory.
    225 function (lcov_capture_tgt TNAME)
    226 	# We don't have to check, if the target has support for coverage, thus this
    227 	# will be checked by add_coverage_target in Findcoverage.cmake. Instead we
    228 	# have to determine which gcov binary to use.
    229 	get_target_property(TSOURCES ${TNAME} SOURCES)
    230 	set(SOURCES "")
    231 	set(TCOMPILER "")
    232 	foreach (FILE ${TSOURCES})
    233 		codecov_path_of_source(${FILE} FILE)
    234 		if (NOT "${FILE}" STREQUAL "")
    235 			codecov_lang_of_source(${FILE} LANG)
    236 			if (NOT "${LANG}" STREQUAL "")
    237 				list(APPEND SOURCES "${FILE}")
    238 				set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID})
    239 			endif ()
    240 		endif ()
    241 	endforeach ()
    242 
    243 	# If no gcov binary was found, coverage data can't be evaluated.
    244 	if (NOT GCOV_${TCOMPILER}_BIN)
    245 		message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.")
    246 		return()
    247 	endif ()
    248 
    249 	set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}")
    250 	set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}")
    251 
    252 
    253 	set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir)
    254 	set(GENINFO_FILES "")
    255 	foreach(FILE ${SOURCES})
    256 		# Generate coverage files. If no .gcda file was generated during
    257 		# execution, the empty coverage file will be used instead.
    258 		set(OUTFILE "${TDIR}/${FILE}.info")
    259 		list(APPEND GENINFO_FILES ${OUTFILE})
    260 
    261 		add_custom_command(OUTPUT ${OUTFILE}
    262 			COMMAND test -f "${TDIR}/${FILE}.gcda"
    263 				&& ${GCOV_ENV} ${GENINFO_BIN} --quiet --base-directory
    264 					${PROJECT_SOURCE_DIR} --gcov-tool ${GCOV_BIN}
    265 					--output-filename ${OUTFILE} ${GENINFO_EXTERN_FLAG}
    266 					${TDIR}/${FILE}.gcda
    267 				|| cp ${OUTFILE}.init ${OUTFILE}
    268 			DEPENDS ${TNAME} ${TNAME}-capture-init
    269 			COMMENT "Capturing coverage data for ${FILE}"
    270 		)
    271 	endforeach()
    272 
    273 	# Concatenate all files generated by geninfo to a single file per target.
    274 	set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/${TNAME}.info")
    275 	lcov_merge_files("${OUTFILE}" ${GENINFO_FILES})
    276 	add_custom_target(${TNAME}-geninfo DEPENDS ${OUTFILE})
    277 
    278 	# add geninfo file generation to global lcov-capture target
    279 	add_dependencies(lcov-capture ${TNAME}-geninfo)
    280 	set(LCOV_CAPTURE_FILES "${LCOV_CAPTURE_FILES}" "${OUTFILE}" CACHE INTERNAL
    281 		""
    282 	)
    283 
    284 	# Add target for generating html output for this target only.
    285 	file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/${TNAME})
    286 	add_custom_target(${TNAME}-genhtml
    287 		COMMAND ${GENHTML_BIN} --quiet --sort --prefix ${PROJECT_SOURCE_DIR}
    288 			--baseline-file ${LCOV_DATA_PATH_INIT}/${TNAME}.info
    289 			--output-directory ${LCOV_HTML_PATH}/${TNAME}
    290 			--title "${CMAKE_PROJECT_NAME} - target ${TNAME}"
    291 			${GENHTML_CPPFILT_FLAG} ${OUTFILE}
    292 		DEPENDS ${TNAME}-geninfo ${TNAME}-capture-init
    293 	)
    294 endfunction (lcov_capture_tgt)
    295 
    296 
    297 # This function will generate the global info file for all targets. It has to be
    298 # called after all other CMake functions in the root CMakeLists.txt file, to get
    299 # a full list of all targets that generate coverage data.
    300 function (lcov_capture)
    301 	# Skip this function (and do not create the following targets), if there are
    302 	# no input files.
    303 	if ("${LCOV_CAPTURE_FILES}" STREQUAL "")
    304 		return()
    305 	endif ()
    306 
    307 	# Add a new target to merge the files of all targets.
    308 	set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/all_targets.info")
    309 	lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_FILES})
    310 	add_custom_target(lcov-geninfo DEPENDS ${OUTFILE} lcov-capture)
    311 
    312 	# Add a new global target for all lcov targets. This target could be used to
    313 	# generate the lcov html output for the whole project instead of calling
    314 	# <TARGET>-geninfo and <TARGET>-genhtml for each target. It will also be
    315 	# used to generate a html site for all project data together instead of one
    316 	# for each target.
    317 	if (NOT TARGET lcov)
    318 		file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/all_targets)
    319 		add_custom_target(lcov
    320 			COMMAND ${GENHTML_BIN} --quiet --sort
    321 				--baseline-file ${LCOV_DATA_PATH_INIT}/all_targets.info
    322 				--output-directory ${LCOV_HTML_PATH}/all_targets
    323 				--title "${CMAKE_PROJECT_NAME}" --prefix "${PROJECT_SOURCE_DIR}"
    324 				${GENHTML_CPPFILT_FLAG} ${OUTFILE}
    325 			DEPENDS lcov-geninfo-init lcov-geninfo
    326 		)
    327 	endif ()
    328 endfunction (lcov_capture)
    329 
    330 
    331 
    332 
    333 # Add a new global target to generate the lcov html report for the whole project
    334 # instead of calling <TARGET>-genhtml for each target (to create an own report
    335 # for each target). Instead of the lcov target it does not require geninfo for
    336 # all targets, so you have to call <TARGET>-geninfo to generate the info files
    337 # the targets you'd like to have in your report or lcov-geninfo for generating
    338 # info files for all targets before calling lcov-genhtml.
    339 file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/selected_targets)
    340 if (NOT TARGET lcov-genhtml)
    341 	add_custom_target(lcov-genhtml
    342 		COMMAND ${GENHTML_BIN}
    343 			--quiet
    344 			--output-directory ${LCOV_HTML_PATH}/selected_targets
    345 			--title \"${CMAKE_PROJECT_NAME} - targets  `find
    346 				${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name
    347 				\"all_targets.info\" -exec basename {} .info \\\;`\"
    348 			--prefix ${PROJECT_SOURCE_DIR}
    349 			--sort
    350 			${GENHTML_CPPFILT_FLAG}
    351 			`find ${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name
    352 				\"all_targets.info\"`
    353 	)
    354 endif (NOT TARGET lcov-genhtml)
    355