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}/cmake/Modules")
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    OUTPUT_STRIP_TRAILING_WHITESPACE
80    )
81
82  if(checkspdx_output)
83    message(${checkspdx_output})
84  endif()
85
86  # checkspdx failed for this file. Collect no.of errors
87  if(${checkspdx_rc} GREATER 0)
88    checkspdx_get_stats("${checkspdx_output}" errors)
89  endif()
90
91  set(${errors_ret} ${errors} PARENT_SCOPE)
92endfunction()
93
94#
95# Run checkspdx on entire codebase. This verifies all files in this repository
96# except the files listed in "glob_excludes".
97#
98# Exits with FATAL_ERROR upon errors. Warnings are ignored (temporary)
99#
100if(CHECKSPDX_CODEBASE)
101  set(source_files "")
102
103  if (GIT_FOUND AND IS_DIRECTORY .git)
104    Git_Get_All_Files(source_files)
105  else()
106    file(GLOB_RECURSE source_files RELATIVE ${CMAKE_SOURCE_DIR} "*")
107  endif()
108
109  # Filter out 'glob_excludes'
110  foreach(exclude IN LISTS glob_excludes)
111    list(FILTER source_files EXCLUDE REGEX "${exclude}")
112  endforeach()
113
114  if(NOT source_files)
115    message(STATUS "check-spdx-codebase: No files to check")
116    return()
117  endif()
118
119  run_checkspdx("${source_files}" total_errors)
120  print_stats_and_exit("checkspdx-codebase" ${total_errors})
121endif()
122
123#
124# Check SPDX complaiance on pending commits.
125#
126# Exits with FATAL_ERROR upon errors.
127#
128if(CHECKSPDX_PATCH)
129  if(GIT_NOT_FOUND OR NOT IS_DIRECTORY .git)
130    message(FATAL_ERROR "Required dependencies Git not found")
131  endif()
132
133  # Get list of commits to check
134  Git_Get_Pending_Commits(pending_commits)
135
136  # Iterate throuth list of commit ids
137  set(total_errors 0)
138  foreach(commit IN LISTS pending_commits)
139    message(STATUS "Checking commit: ${commit}")
140
141    Git_Get_Files_In_Commit("${commit}" source_files)
142
143    foreach(exclude IN LISTS glob_excludes)
144      list(FILTER source_files EXCLUDE REGEX "${exclude}")
145    endforeach()
146
147    # run check if the list is not empty
148    if(source_files)
149      run_checkspdx("${source_files}" errors)
150      MATH(EXPR total_errors "${total_errors}+${errors}")
151    endif()
152  endforeach()
153
154  print_stats_and_exit("checkspdx-patch" ${total_errors})
155endif()
156