diff options
author | Joerg Bornemann <joerg.bornemann@qt.io> | 2022-04-26 14:31:19 +0200 |
---|---|---|
committer | Joerg Bornemann <joerg.bornemann@qt.io> | 2022-04-29 10:34:08 +0200 |
commit | 72f0fb3c95c8eb0a023ef9ba93f1962acd71b96b (patch) | |
tree | 75b6465c982bd3550aa40f56b77094024746eaf4 /tests/prebuild | |
parent | 4ea55db95052e14e21116c33d34c3866f1eb952e (diff) |
Teach tst_licenses.pl to check SPDX licenses
The license test now performs a test against the SPDX specification
if a LICENSES directory is found in the repository root.
The test is roughly doing the following:
- check that at least one copyright tag is in the file
- check that at least one license identifier is in the file
- check for each license identifier whether we have a corresponding
license file
Task-number: QTBUG-102752
Change-Id: Ifb2cc03caa0efc8147c133189278d5312b20112a
Reviewed-by: Kai Koehne <kai.koehne@qt.io>
Diffstat (limited to 'tests/prebuild')
-rwxr-xr-x | tests/prebuild/license/tst_licenses.pl | 150 |
1 files changed, 135 insertions, 15 deletions
diff --git a/tests/prebuild/license/tst_licenses.pl b/tests/prebuild/license/tst_licenses.pl index 5a26739a..3b5bac94 100755 --- a/tests/prebuild/license/tst_licenses.pl +++ b/tests/prebuild/license/tst_licenses.pl @@ -243,6 +243,7 @@ my @mandatoryFiles=( my $QT_MODULE_TO_TEST; my $moduleName; +my $repositoryLicenseType = 'legacy'; # # These regexes define the expected patterns for the various parts of a @@ -362,7 +363,7 @@ sub msgMismatch # Check whether the nominated file has a valid license header with legal text # that matches one of the reference licenses. # -sub checkLicense +sub checkLicense_legacy { my $filename = shift; @@ -535,6 +536,96 @@ sub checkLicense return 1; } +sub checkSPDXLicenseIdentifier +{ + my $expression = shift; + my $shortfilename = shift; + $expression =~ s/[^:]+:\s*//; # remove the "SPDX-License-Identifier: " prefix + foreach (split(/\s+/, $expression)) { + # Skip operators in the expression. + if (/OR|AND|WITH/) { + next; + } + + # Check whether we know this license. + if (!exists($licenseFiles{$_})) { + fail("error: $shortfilename uses unknown license " . $_ . "\n"); + return 0; + } + } + return 1; +} + +# +# Check whether the nominated file has a valid license header with legal text +# that matches one of the reference licenses. +# +sub checkLicense_SPDX +{ + my $filename = shift; + + # Use short filename for reporting purposes (remove useless noise from failure message) + my $shortfilename = $filename; + $shortfilename =~ s/^\Q$QT_MODULE_TO_TEST\E\///; + + # Read in the whole file + my $fileHandle; + if (!open($fileHandle, '<', $filename)) { + fail("error: Cannot open $filename"); + return 0; + } + my @lines = <$fileHandle>; + close $fileHandle; + + my $currentLine = 0; + my $yearRegEx = qr/2[0-9][0-9][0-9]/; + my $copyrightRegEx = qr/\s\b((?:Copyright \(C\) $yearRegEx.*)|(?:SPDX-FileCopyrightText: $yearRegEx.*))/; + my $licenseIdRegEx = qr/\s\b((?:SPDX-License-Identifier:\s*[a-zA-Z0-9.\- ]+))/; + + my @copyrightTags = (); + my @licenseIdentifiers = (); + + while ($currentLine <= $#lines) { + $_ = $lines[$currentLine]; + $currentLine++; + + chomp; + s/\r$//; # Strip DOS carriage return if present + + if (/$copyrightRegEx/) { + push @copyrightTags, $1; + } elsif (/$licenseIdRegEx/) { + push @licenseIdentifiers, $1; + } + } + + # Be more lenient towards empty or very small files. + if ($#lines > 2) { + # Did we find any copyright tags? + if (!@copyrightTags) { + fail("error: $shortfilename lacks an SPDX copyright tag"); + return; + } + + # Did we find any licenses? + if (!@licenseIdentifiers) { + fail("error: $shortfilename does not appear to contain a license header"); + return; + } + } + + # Check the license identifiers we've found. + foreach (@licenseIdentifiers) { + if (!checkSPDXLicenseIdentifier($_, $shortfilename)) { + return 0; + } + } + + pass($shortfilename); + return 1; +} + + # # Decide whether the nominated file needs to be scanned for a license header. # We will scan the file if it is obliged to have a license header (i.e. it is @@ -573,7 +664,7 @@ sub shouldScan my @lines = <$fileHandle>; close $fileHandle; - return grep(/QT_BEGIN_LICENSE/, @lines); + return grep(/QT_BEGIN_LICENSE|SPDX-License-Identifier:/, @lines); } # This function reads line based regular expressions into a list. @@ -642,24 +733,49 @@ sub run } } + # Check if we're dealing with a repository that has been ported to use SPDX. + if (-d "$QT_MODULE_TO_TEST/LICENSES") { + print "SPDX compliant repository detected.\n"; + $repositoryLicenseType = 'SPDX'; + + # Store what's in the LICENSES directory. + foreach (glob "$QT_MODULE_TO_TEST/LICENSES/*.txt") { + my $id = basename($_); + $id =~ s/\.txt$//; + $licenseFiles{$id} = $_; + } + } + # # Phase 2: Read the reference license texts # # Load reference license headers from qtqa/tests/prebuild/license/templates/ - my $current_dir = dirname(__FILE__); - foreach (glob "$current_dir/templates/header.*") { - loadLicense($_) || return; - } + if ($repositoryLicenseType eq 'legacy') { + my $current_dir = dirname(__FILE__); + foreach (glob "$current_dir/templates/header.*") { + loadLicense($_) || return; + } - # Also load all header.* files in the module's root, in case the module has special requirements - foreach (glob "$QT_MODULE_TO_TEST/header.*") { - loadLicense($_) || return; - } + # Also load all header.* files in the module's root, in case the module has special + # requirements + foreach (glob "$QT_MODULE_TO_TEST/header.*") { + loadLicense($_) || return; + } - my $numLicenses = keys %licenseTexts; - if ($numLicenses == 0) { - fail("No reference licenses were found."); - return; + my $numLicenses = keys %licenseTexts; + if ($numLicenses == 0) { + fail("No reference licenses were found."); + return; + } + } elsif ($repositoryLicenseType eq 'SPDX') { + # Make sure that the repo does not contain extra license header files. + my @repoLicenseHeaders = glob "$QT_MODULE_TO_TEST/header.*"; + if (@repoLicenseHeaders) { + my $message = sprintf("A Qt repository ported to SPDX should not contain license header" + . " templates:\n %s", join("\n ", @repoLicenseHeaders)); + fail($message); + return; + } } # @@ -707,8 +823,12 @@ sub run plan skip_all => "Module $moduleName appears to have no files that must be scanned"; } else { plan tests => $#filesToScan + 1; + my $checkLicense = \&checkLicense_legacy; + if ($repositoryLicenseType eq 'SPDX') { + $checkLicense = \&checkLicense_SPDX; + } foreach ( @filesToScan ) { - checkLicense($_); + &$checkLicense($_); } } } |