1# - Run cppcheck on c++ source files as a custom target and a test
2#
3#  include(CppcheckTargets)
4#  add_cppcheck(<target-name> [UNUSED_FUNCTIONS] [STYLE] [POSSIBLE_ERROR] [FORCE] [FAIL_ON_WARNINGS]) -
5#    Create a target to check a target's sources with cppcheck and the indicated options
6#  add_cppcheck_sources(<target-name> [UNUSED_FUNCTIONS] [STYLE] [POSSIBLE_ERROR] [FORCE] [FAIL_ON_WARNINGS]) -
7#    Create a target to check standalone sources with cppcheck and the indicated options
8#
9# Requires these CMake modules:
10#  Findcppcheck
11#
12# Requires CMake 2.6 or newer (uses the 'function' command)
13#
14# Original Author:
15# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
16# http://academic.cleardefinition.com
17# Iowa State University HCI Graduate Program/VRAC
18#
19# Copyright Iowa State University 2009-2010.
20# Distributed under the Boost Software License, Version 1.0.
21# (See accompanying file LICENSE_1_0.txt or copy at
22# http://www.boost.org/LICENSE_1_0.txt)
23
24if(__add_cppcheck)
25	return()
26endif()
27set(__add_cppcheck YES)
28
29if(NOT CPPCHECK_FOUND)
30	find_package(cppcheck QUIET)
31endif()
32
33if(CPPCHECK_FOUND)
34	if(NOT TARGET all_cppcheck)
35		add_custom_target(all_cppcheck)
36		set_target_properties(all_cppcheck PROPERTIES EXCLUDE_FROM_ALL TRUE)
37	endif()
38endif()
39
40function(add_cppcheck_sources _targetname)
41	if(CPPCHECK_FOUND)
42		set(_cppcheck_args)
43		set(_input ${ARGN})
44		list(FIND _input UNUSED_FUNCTIONS _unused_func)
45		if("${_unused_func}" GREATER "-1")
46			list(APPEND _cppcheck_args ${CPPCHECK_UNUSEDFUNC_ARG})
47			list(REMOVE_AT _input ${_unused_func})
48		endif()
49
50		list(FIND _input STYLE _style)
51		if("${_style}" GREATER "-1")
52			list(APPEND _cppcheck_args ${CPPCHECK_STYLE_ARG})
53			list(REMOVE_AT _input ${_style})
54		endif()
55
56		list(FIND _input POSSIBLE_ERROR _poss_err)
57		if("${_poss_err}" GREATER "-1")
58			list(APPEND _cppcheck_args ${CPPCHECK_POSSIBLEERROR_ARG})
59			list(REMOVE_AT _input ${_poss_err})
60		endif()
61
62		list(FIND _input FORCE _force)
63		if("${_force}" GREATER "-1")
64			list(APPEND _cppcheck_args "--force")
65			list(REMOVE_AT _input ${_force})
66		endif()
67
68		list(FIND _input FAIL_ON_WARNINGS _fail_on_warn)
69		if("${_fail_on_warn}" GREATER "-1")
70			list(APPEND
71				CPPCHECK_FAIL_REGULAR_EXPRESSION
72				${CPPCHECK_WARN_REGULAR_EXPRESSION})
73			list(REMOVE_AT _input ${_fail_on_warn})
74		endif()
75
76		set(_files)
77		foreach(_source ${_input})
78			get_source_file_property(_cppcheck_loc "${_source}" LOCATION)
79			if(_cppcheck_loc)
80				# This file has a source file property, carry on.
81				get_source_file_property(_cppcheck_lang "${_source}" LANGUAGE)
82				if("${_cppcheck_lang}" MATCHES "CXX")
83					list(APPEND _files "${_cppcheck_loc}")
84				endif()
85			else()
86				# This file doesn't have source file properties - figure it out.
87				get_filename_component(_cppcheck_loc "${_source}" ABSOLUTE)
88				if(EXISTS "${_cppcheck_loc}")
89					list(APPEND _files "${_cppcheck_loc}")
90				else()
91					message(FATAL_ERROR
92						"Adding CPPCHECK for file target ${_targetname}: "
93						"File ${_source} does not exist or needs a corrected path location "
94						"since we think its absolute path is ${_cppcheck_loc}")
95				endif()
96			endif()
97		endforeach()
98
99		if("1.${CMAKE_VERSION}" VERSION_LESS "1.2.8.0")
100			# Older than CMake 2.8.0
101			add_test(${_targetname}_cppcheck_test
102				"${CPPCHECK_EXECUTABLE}"
103				${CPPCHECK_TEMPLATE_ARG}
104				${_cppcheck_args}
105				${_files})
106		else()
107			# CMake 2.8.0 and newer
108			add_test(NAME
109				${_targetname}_cppcheck_test
110				COMMAND
111				"${CPPCHECK_EXECUTABLE}"
112				${CPPCHECK_TEMPLATE_ARG}
113				${_cppcheck_args}
114				${_files})
115		endif()
116
117		set_tests_properties(${_targetname}_cppcheck_test
118			PROPERTIES
119			FAIL_REGULAR_EXPRESSION
120			"${CPPCHECK_FAIL_REGULAR_EXPRESSION}")
121
122		add_custom_command(TARGET
123			all_cppcheck
124			PRE_BUILD
125			COMMAND
126			${CPPCHECK_EXECUTABLE}
127			${CPPCHECK_QUIET_ARG}
128			${CPPCHECK_TEMPLATE_ARG}
129			${_cppcheck_args}
130			${_files}
131			WORKING_DIRECTORY
132			"${CMAKE_CURRENT_SOURCE_DIR}"
133			COMMENT
134			"${_targetname}_cppcheck: Running cppcheck on target ${_targetname}..."
135			VERBATIM)
136	endif()
137endfunction()
138
139function(add_cppcheck _name)
140	if(NOT TARGET ${_name})
141		message(FATAL_ERROR
142			"add_cppcheck given a target name that does not exist: '${_name}' !")
143	endif()
144	if(CPPCHECK_FOUND)
145		set(_cppcheck_args)
146
147		list(FIND ARGN UNUSED_FUNCTIONS _unused_func)
148		if("${_unused_func}" GREATER "-1")
149			list(APPEND _cppcheck_args ${CPPCHECK_UNUSEDFUNC_ARG})
150		endif()
151
152		list(FIND ARGN STYLE _style)
153		if("${_style}" GREATER "-1")
154			list(APPEND _cppcheck_args ${CPPCHECK_STYLE_ARG})
155		endif()
156
157		list(FIND ARGN POSSIBLE_ERROR _poss_err)
158		if("${_poss_err}" GREATER "-1")
159			list(APPEND _cppcheck_args ${CPPCHECK_POSSIBLEERROR_ARG})
160		endif()
161
162		list(FIND ARGN FORCE _force)
163		if("${_force}" GREATER "-1")
164			list(APPEND _cppcheck_args "--force")
165		endif()
166
167		list(FIND _input FAIL_ON_WARNINGS _fail_on_warn)
168		if("${_fail_on_warn}" GREATER "-1")
169			list(APPEND
170				CPPCHECK_FAIL_REGULAR_EXPRESSION
171				${CPPCHECK_WARN_REGULAR_EXPRESSION})
172			list(REMOVE_AT _input ${_unused_func})
173		endif()
174
175		get_target_property(_cppcheck_includes "${_name}" INCLUDE_DIRECTORIES)
176		set(_includes)
177		foreach(_include ${_cppcheck_includes})
178			list(APPEND _includes "-I${_include}")
179		endforeach()
180
181		get_target_property(_cppcheck_sources "${_name}" SOURCES)
182		set(_files)
183		foreach(_source ${_cppcheck_sources})
184			get_source_file_property(_cppcheck_lang "${_source}" LANGUAGE)
185			get_source_file_property(_cppcheck_loc "${_source}" LOCATION)
186			if("${_cppcheck_lang}" MATCHES "CXX")
187				list(APPEND _files "${_cppcheck_loc}")
188			endif()
189		endforeach()
190
191		if("1.${CMAKE_VERSION}" VERSION_LESS "1.2.8.0")
192			# Older than CMake 2.8.0
193			add_test(${_name}_cppcheck_test
194				"${CPPCHECK_EXECUTABLE}"
195				${CPPCHECK_TEMPLATE_ARG}
196				${_cppcheck_args}
197				${_files})
198		else()
199			# CMake 2.8.0 and newer
200			add_test(NAME
201				${_name}_cppcheck_test
202				COMMAND
203				"${CPPCHECK_EXECUTABLE}"
204				${CPPCHECK_TEMPLATE_ARG}
205				${_cppcheck_args}
206				${_files})
207		endif()
208
209		set_tests_properties(${_name}_cppcheck_test
210			PROPERTIES
211			FAIL_REGULAR_EXPRESSION
212			"${CPPCHECK_FAIL_REGULAR_EXPRESSION}")
213
214		add_custom_command(TARGET
215			all_cppcheck
216			PRE_BUILD
217			COMMAND
218			${CPPCHECK_EXECUTABLE}
219			${CPPCHECK_QUIET_ARG}
220			${CPPCHECK_TEMPLATE_ARG}
221			${_cppcheck_args}
222			${_includes}
223			${_files}
224			WORKING_DIRECTORY
225			"${CMAKE_CURRENT_SOURCE_DIR}"
226			COMMENT
227			"${_name}_cppcheck: Running cppcheck on target ${_name}..."
228			VERBATIM)
229	endif()
230
231endfunction()
232