1#
2# SPDX-License-Identifier: BSD-3-Clause
3# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
4#
5
6#
7# This script is called from main CMakeLists.txt to determine if SPDX headers
8# are in proper format
9#
10find_package(Git REQUIRED)
11find_package(Python3 REQUIRED)
12find_program(CHECKSPDX_EXECUTABLE "checkspdx.py"
13  PATHS ${CMAKE_SOURCE_DIR}
14  PATH_SUFFIXES tools/checkspdx
15  DOC "Path to checkspdx.py"
16  )
17
18list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/tools/common")
19include(GitUtils)
20
21# List of directories and files to exclude from checking for target
22list(APPEND glob_excludes "^.git")
23list(APPEND glob_excludes "^out")
24list(APPEND glob_excludes "^build")
25list(APPEND glob_excludes "^ext")
26list(APPEND glob_excludes "^tools")
27list(APPEND glob_excludes ".patch$")
28list(APPEND glob_excludes ".md$")
29list(APPEND glob_excludes "~$")
30list(APPEND glob_excludes ".swp$")
31list(APPEND glob_excludes "^cscope.")
32list(APPEND glob_excludes ".png$")
33list(APPEND glob_excludes "LICENSE")
34list(APPEND glob_excludes "DCO")
35list(APPEND glob_excludes "docs/global_substitutions.txt")
36
37# checkspdx_get_stats: Parse and returns number of errors and warnings
38function(checkspdx_get_stats stats_arg errors_ret)
39  string(FIND "${stats_arg}" "total:" idx REVERSE)
40  if(NOT ${idx} EQUAL -1)
41    string(LENGTH "${stats_arg}" len)
42    string(SUBSTRING "${stats_arg}" ${idx} ${len} last_line)
43
44    string(REPLACE " " ";" last_line_list ${last_line})
45    list(GET last_line_list 1 errors)
46  else()
47    set(errors 1)
48  endif()
49
50  set(${errors_ret} ${errors} PARENT_SCOPE)
51endfunction()
52
53#
54# print_stats_and_exit: Print summary of all errors and warnings.
55# If there are errors call message(FATAL_ERROR)
56#
57function(print_stats_and_exit check_type total_errors)
58  message(STATUS "${check_type}: total errors: ${total_errors}")
59
60  if(${total_errors} GREATER 0)
61    message(FATAL_ERROR "${check_type}: FAILED")
62  endif()
63
64  message(STATUS "${check_type}: PASSED")
65endfunction()
66
67# Run checkspdx.py on the list of files.
68function(run_checkspdx source_files errors_ret)
69  set(errors 0)
70
71  string(REPLACE ";" " " source_files "${source_files}")
72  separate_arguments(source_files NATIVE_COMMAND "${source_files}")
73
74  execute_process(
75    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
76    COMMAND ${CHECKSPDX_EXECUTABLE} -r docs/readme.rst ${source_files}
77    OUTPUT_VARIABLE checkspdx_output
78    RESULT_VARIABLE checkspdx_rc
79    ECHO_OUTPUT_VARIABLE
80    OUTPUT_STRIP_TRAILING_WHITESPACE
81    )
82
83  # checkspdx failed for this file. Collect no.of errors
84  if(${checkspdx_rc} GREATER 0)
85    checkspdx_get_stats("${checkspdx_output}" errors)
86  endif()
87
88  set(${errors_ret} ${errors} PARENT_SCOPE)
89endfunction()
90
91#
92# Run checkspdx on entire codebase. This verifies all files in this repository
93# except the files listed in "glob_excludes".
94#
95# Exits with FATAL_ERROR upon errors. Warnings are ignored (temporary)
96#
97if(CHECKSPDX_CODEBASE)
98  set(source_files "")
99
100  if (GIT_FOUND AND IS_DIRECTORY .git)
101    Git_Get_All_Files(source_files)
102  else()
103    file(GLOB_RECURSE source_files RELATIVE ${CMAKE_SOURCE_DIR} "*")
104  endif()
105
106  # Filter out 'glob_excludes'
107  foreach(exclude IN LISTS glob_excludes)
108    list(FILTER source_files EXCLUDE REGEX "${exclude}")
109  endforeach()
110
111  if(NOT source_files)
112    message(STATUS "check-spdx-codebase: No files to check")
113    return()
114  endif()
115
116  run_checkspdx("${source_files}" total_errors)
117  print_stats_and_exit("checkspdx-codebase" ${total_errors})
118endif()
119
120#
121# Check SPDX complaiance on pending commits.
122#
123# Exits with FATAL_ERROR upon errors.
124#
125if(CHECKSPDX_PATCH)
126  if(GIT_NOT_FOUND OR NOT IS_DIRECTORY .git)
127    message(FATAL_ERROR "Required dependencies Git not found")
128  endif()
129
130  # Get list of commits to check
131  Git_Get_Pending_Commits(pending_commits)
132
133  # Iterate throuth list of commit ids
134  set(total_errors 0)
135  foreach(commit IN LISTS pending_commits)
136    message(STATUS "Checking commit: ${commit}")
137
138    Git_Get_Files_In_Commit("${commit}" source_files)
139
140    foreach(exclude IN LISTS glob_excludes)
141      list(FILTER source_files EXCLUDE REGEX "${exclude}")
142    endforeach()
143
144    run_checkspdx("${source_files}" errors)
145    MATH(EXPR total_errors "${total_errors}+${errors}")
146  endforeach()
147
148  print_stats_and_exit("checkspdx-patch" ${total_errors})
149endif()
150