1##============================================================================
2##  Copyright (c) Kitware, Inc.
3##  All rights reserved.
4##  See LICENSE.txt for details.
5##
6##  This software is distributed WITHOUT ANY WARRANTY; without even
7##  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8##  PURPOSE.  See the above copyright notice for more information.
9##============================================================================
10
11## This CMake script checks source files for the appropriate VTKM copyright
12## statement, which is stored in VTKm_SOURCE_DIR/CMake/VTKmCopyrightStatement.txt.
13## To run this script, execute CMake as follows:
14##
15## cmake -DVTKm_SOURCE_DIR=<VTKm_SOURCE_DIR> -P <VTKm_SOURCE_DIR>/CMake/VTKMCheckCopyright.cmake
16##
17
18set(FILES_TO_CHECK
19  *.txt
20  *.cmake
21  *.h
22  *.h.in
23  *.cxx
24  *.cu
25  )
26
27set(EXCEPTIONS
28  LICENSE.txt
29  README.txt
30  )
31
32if (NOT VTKm_SOURCE_DIR)
33  message(SEND_ERROR "VTKm_SOURCE_DIR not defined.")
34endif (NOT VTKm_SOURCE_DIR)
35
36set(copyright_file ${VTKm_SOURCE_DIR}/CMake/VTKmCopyrightStatement.txt)
37
38if (NOT EXISTS ${copyright_file})
39  message(SEND_ERROR "Cannot find VTKMCopyrightStatement.txt.")
40endif (NOT EXISTS ${copyright_file})
41
42set(license_file ${VTKm_SOURCE_DIR}/LICENSE.txt)
43
44if (NOT EXISTS ${license_file})
45  message(SEND_ERROR "Cannot find LICENSE.txt.")
46endif (NOT EXISTS ${license_file})
47
48# Get a list of third party files (with different copyrights) from the
49# license file.
50file(STRINGS ${license_file} license_lines)
51list(FIND
52  license_lines
53  "- - - - - - - - - - - - - - - - - - - - - - - - do not remove this line"
54  separator_index
55  )
56math(EXPR begin_index "${separator_index} + 1")
57list(LENGTH license_lines license_file_length)
58math(EXPR end_index "${license_file_length} - 1")
59foreach (index RANGE ${begin_index} ${end_index})
60  list(GET license_lines ${index} tpl_file)
61  set(EXCEPTIONS ${EXCEPTIONS} ${tpl_file})
62endforeach(index)
63# if the build directory is in the source directory, exclude generated build
64# files
65find_path(BUILD_DIR CMakeCache.txt .)
66get_filename_component(abs_build_dir ${BUILD_DIR} ABSOLUTE)
67get_filename_component(build_dir_name ${abs_build_dir} NAME)
68set(EXCEPTIONS ${EXCEPTIONS} ${build_dir_name}/*)
69message("Copyright Check Exceptions: ${EXCEPTIONS}")
70
71# Gets the current year (if possible).
72function (get_year var)
73  string(TIMESTAMP result "%Y")
74  set(${var} "${result}" PARENT_SCOPE)
75endfunction (get_year)
76
77set(copyright_file_year 2014)
78get_year(current_year)
79
80# Escapes ';' characters (list delimiters) and splits the given string into
81# a list of its lines without newlines.
82function (list_of_lines var string)
83  string(REGEX REPLACE ";" "\\\\;" conditioned_string "${string}")
84  string(REGEX REPLACE "\n" ";" conditioned_string "${conditioned_string}")
85  set(${var} "${conditioned_string}" PARENT_SCOPE)
86endfunction (list_of_lines)
87
88# Read in copyright statement file.
89file(READ ${copyright_file} COPYRIGHT_STATEMENT)
90
91# Remove trailing whitespace and ending lines.  They are sometimes hard to
92# see or remove in editors.
93string(REGEX REPLACE "[ \t]*\n" "\n" COPYRIGHT_STATEMENT "${COPYRIGHT_STATEMENT}")
94string(REGEX REPLACE "\n+$" "" COPYRIGHT_STATEMENT "${COPYRIGHT_STATEMENT}")
95
96# Get a list of lines in the copyright statement.
97list_of_lines(COPYRIGHT_LINE_LIST "${COPYRIGHT_STATEMENT}")
98
99# Comment regular expression characters that we want to match literally.
100string(REPLACE "." "\\." COPYRIGHT_LINE_LIST "${COPYRIGHT_LINE_LIST}")
101string(REPLACE "(" "\\(" COPYRIGHT_LINE_LIST "${COPYRIGHT_LINE_LIST}")
102string(REPLACE ")" "\\)" COPYRIGHT_LINE_LIST "${COPYRIGHT_LINE_LIST}")
103
104# Introduce regular expression for years we want to be generic.
105string(REPLACE
106  "${copyright_file_year}"
107  "20[0-9][0-9]"
108  COPYRIGHT_LINE_LIST
109  "${COPYRIGHT_LINE_LIST}"
110  )
111
112# Replace year in COPYRIGHT_STATEMENT with current year.
113string(REPLACE
114  "${copyright_file_year}"
115  "${current_year}"
116  COPYRIGHT_STATEMENT
117  "${COPYRIGHT_STATEMENT}"
118  )
119
120# Print an error concerning the missing copyright in the given file.
121function(missing_copyright filename comment_prefix)
122  message("${filename} does not have the appropriate copyright statement:\n")
123
124  # Condition the copyright statement
125  string(REPLACE
126    "\n"
127    "\n${comment_prefix}  "
128    comment_copyright
129    "${COPYRIGHT_STATEMENT}"
130    )
131  set(comment_copyright "${comment_prefix}  ${comment_copyright}")
132  string(REPLACE
133    "\n${comment_prefix}  \n"
134    "\n${comment_prefix}\n"
135    comment_copyright
136    "${comment_copyright}"
137    )
138
139  message("${comment_prefix}=============================================================================")
140  message("${comment_prefix}")
141  message("${comment_copyright}")
142  message("${comment_prefix}")
143  message("${comment_prefix}=============================================================================\n")
144  message(SEND_ERROR
145    "Please add the previous statement to the beginning of ${filename}"
146    )
147endfunction(missing_copyright)
148
149# Get an appropriate beginning line comment for the given filename.
150function(get_comment_prefix var filename)
151  get_filename_component(base "${filename}" NAME_WE)
152  get_filename_component(extension "${filename}" EXT)
153  if (extension STREQUAL ".cmake")
154    set(${var} "##" PARENT_SCOPE)
155  elseif (base STREQUAL "CMakeLists" AND extension STREQUAL ".txt")
156    set(${var} "##" PARENT_SCOPE)
157  elseif (extension STREQUAL ".txt")
158    set(${var} "" PARENT_SCOPE)
159  elseif (extension STREQUAL ".h" OR extension STREQUAL ".h.in" OR extension STREQUAL ".cxx" OR extension STREQUAL ".cu")
160    set(${var} "//" PARENT_SCOPE)
161  elseif (extension STREQUAL ".worklet")
162    set(${var} "//" PARENT_SCOPE)
163  else (extension STREQUAL ".cmake")
164    message(SEND_ERROR "Could not identify file type of ${filename}.")
165  endif (extension STREQUAL ".cmake")
166endfunction(get_comment_prefix)
167
168# Check the given file for the appropriate copyright statement.
169function(check_copyright filename)
170
171  get_comment_prefix(comment_prefix "${filename}")
172
173  # Read in the first 2000 characters of the file and split into lines.
174  # This is roughly equivalent to the file STRINGS command except that we
175  # also escape semicolons (list separators) in the input, which the file
176  # STRINGS command does not currently do.
177  file(READ "${filename}" header_contents LIMIT 2000)
178  list_of_lines(header_lines "${header_contents}")
179
180  set(printed)
181  # Check each copyright line.
182  foreach (copyright_line IN LISTS COPYRIGHT_LINE_LIST)
183    set(match)
184    # My original algorithm tried to check the order by removing items from
185    # header_lines as they were encountered.  Unfortunately, CMake 2.8's
186    # list REMOVE_AT command removed the escaping on the ; in one of the
187    # header_line's items and cause the compare to fail.
188    foreach (header_line IN LISTS header_lines)
189      if (copyright_line)
190        string(REGEX MATCH
191          "^${comment_prefix}[ \t]*${copyright_line}[ \t]*$"
192          match
193          "${header_line}"
194          )
195      else (copyright_line)
196        if (NOT header_line)
197          set(match TRUE)
198        endif (NOT header_line)
199      endif (copyright_line)
200      if (match)
201        break()
202      endif (match)
203    endforeach (header_line)
204    if (NOT match AND NOT printed)
205      message(STATUS "Could not find match for `${copyright_line}'")
206      missing_copyright("${filename}" "${comment_prefix}")
207      set(printed TRUE)
208    endif (NOT match AND NOT printed)
209  endforeach (copyright_line)
210endfunction(check_copyright)
211
212foreach (glob_expression ${FILES_TO_CHECK})
213  file(GLOB_RECURSE file_list
214    RELATIVE "${VTKm_SOURCE_DIR}"
215    "${VTKm_SOURCE_DIR}/${glob_expression}"
216    )
217
218  foreach (file ${file_list})
219    set(skip)
220    foreach(exception ${EXCEPTIONS})
221      if(file MATCHES "^${exception}(/.*)?$")
222        # This file is an exception
223        set(skip TRUE)
224      endif(file MATCHES "^${exception}(/.*)?$")
225    endforeach(exception)
226
227    if (NOT skip)
228      check_copyright("${VTKm_SOURCE_DIR}/${file}")
229    endif (NOT skip)
230  endforeach (file)
231endforeach (glob_expression)
232