summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2025-03-03 10:21:09 +0800
committerStefan Hajnoczi <stefanha@redhat.com>2025-03-03 10:21:09 +0800
commit354925d42252f6f36a9e1e4a6b929aaafb2eaf45 (patch)
treebe35458673102ea06e11e4f5f4fbe410b560af74
parent8d56d0fd2fcb5a99cd2f722d983c9c1199ff19b0 (diff)
parent6b7521818b26134726b3494cd06f04e30659ce2c (diff)
downloadfocaccia-qemu-354925d42252f6f36a9e1e4a6b929aaafb2eaf45.tar.gz
focaccia-qemu-354925d42252f6f36a9e1e4a6b929aaafb2eaf45.zip
Merge tag 'spdx-check-pull-request' of https://gitlab.com/berrange/qemu into staging
SPDX support for checkpatch

* Mandate use of SPDX-License-Identifier in new files
* Validate SPDX license choices
* Forbid other SPDX tags

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEE2vOm/bJrYpEtDo4/vobrtBUQT98FAmfB2bYACgkQvobrtBUQ
# T9/XMhAAmfGQpHmr5cMBFkJwcSO7CnmggK+UM+BYP/2Zv6LdzdT3G1n9PzGuvPuv
# bXA2iYzFzZbD3uFYUi1MDHkQPvCY0skGZTIw6FPFv83a9aFTpM571L2wtpcPpBf/
# F+Fa6Thb//rgzB0eUSY+ZE7BVIyqB6Lpgq6eclePF+Q8hpv9e6/pW5LxwK4eYPev
# ELzIdOUUVkTH4PkBKL/HJGpCG4YUP6ORXSCJylU3s74OudjhLX5CXf3hGRbgXLpK
# t3jyl14CipZPtvNOAxW3AvzjkvqEWb4+ZxHGXJCypCscKOrM/SG0m1HpUOrLx2z3
# O9liRSl3yenSmN8gXghPhS346+jLMihocvD6NFJNzJLYq+JgIq3xPM2U3b3AXIUn
# TUaVE92vp90YusCyu/J+4iyMu7gwir7pLSAzKudmRQJLbA8Uxiq6GeQmCPV+Rj65
# eq6YAcxQeH+sgZud5I3fJ1tiupAsbzha+T+kCyEos3vE7qof+a1W9NhnhGGfZd/y
# lsukf/wT9ukX7aw61geiVdGDrwLtMbcfA7fRyLxI5WZkH6qFW+WMX2bpZaEOv0wb
# z+MH21cr0e56hbRwh7PdxwYrXW/sorvyi4sZgTh79ch8O3Hed9pBa2K2VkDJ4LKU
# joqc0vXwKJXbDZe0KLzD0LnajfiJnduCOsZgP5e93PndjL1fHzg=
# =eGL+
# -----END PGP SIGNATURE-----
# gpg: Signature made Fri 28 Feb 2025 23:43:50 HKT
# gpg:                using RSA key DAF3A6FDB26B62912D0E8E3FBE86EBB415104FDF
# gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" [full]
# gpg:                 aka "Daniel P. Berrange <berrange@redhat.com>" [full]
# Primary key fingerprint: DAF3 A6FD B26B 6291 2D0E  8E3F BE86 EBB4 1510 4FDF

* tag 'spdx-check-pull-request' of https://gitlab.com/berrange/qemu:
  scripts: forbid use of arbitrary SPDX tags besides license identifiers
  scripts: validate SPDX license choices
  scripts: mandate that new files have SPDX-License-Identifier

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-rwxr-xr-xscripts/checkpatch.pl111
1 files changed, 111 insertions, 0 deletions
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 06d07e6c22..83b59fb443 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -1353,6 +1353,70 @@ sub checkfilename {
 	}
 }
 
+sub checkspdx {
+    my ($file, $expr) = @_;
+
+    # Imported Linux headers probably have SPDX tags, but if they
+    # don't we're not requiring contributors to fix this, as these
+    # files are not expected to be modified locally in QEMU.
+    # Also don't accidentally detect own checking code.
+    if ($file =~ m,include/standard-headers, ||
+	$file =~ m,linux-headers, ||
+	$file =~ m,checkpatch.pl,) {
+	return;
+    }
+
+    my $origexpr = $expr;
+
+    # Flatten sub-expressions
+    $expr =~ s/\(|\)/ /g;
+    $expr =~ s/OR|AND/ /g;
+
+    # Merge WITH exceptions to the license
+    $expr =~ s/\s+WITH\s+/-WITH-/g;
+
+    # Cull more leading/trailing whitespace
+    $expr =~ s/^\s*//g;
+    $expr =~ s/\s*$//g;
+
+    my @bits = split / +/, $expr;
+
+    my $prefer = "GPL-2.0-or-later";
+    my @valid = qw(
+	GPL-2.0-only
+	LGPL-2.1-only
+	LGPL-2.1-or-later
+	BSD-2-Clause
+	BSD-3-Clause
+	MIT
+	);
+
+    my $nonpreferred = 0;
+    my @unknown = ();
+    foreach my $bit (@bits) {
+	if ($bit eq $prefer) {
+	    next;
+	}
+	if (grep /^$bit$/, @valid) {
+	    $nonpreferred = 1;
+	} else {
+	    push @unknown, $bit;
+	}
+    }
+    if (@unknown) {
+	ERROR("Saw unacceptable licenses '" . join(',', @unknown) .
+	      "', valid choices for QEMU are:\n" . join("\n", $prefer, @valid));
+    }
+
+    if ($nonpreferred) {
+	WARN("Saw acceptable license '$origexpr' but note '$prefer' is " .
+	     "preferred for new files unless the code is derived from a " .
+	     "source file with an existing declared license that must be " .
+	     "retained. Please explain the license choice in the commit " .
+	     "message.");
+    }
+}
+
 sub process {
 	my $filename = shift;
 
@@ -1378,6 +1442,8 @@ sub process {
 	my $in_imported_file = 0;
 	my $in_no_imported_file = 0;
 	my $non_utf8_charset = 0;
+	my $expect_spdx = 0;
+	my $expect_spdx_file;
 
 	our @report = ();
 	our $cnt_lines = 0;
@@ -1615,6 +1681,51 @@ sub process {
 			WARN("added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr);
 		}
 
+# All new files should have a SPDX-License-Identifier tag
+		if ($line =~ /^new file mode\s*\d+\s*$/) {
+		    if ($expect_spdx) {
+			if ($expect_spdx_file =~
+			    /\.(c|h|py|pl|sh|json|inc|Makefile)$/) {
+			    # source code files MUST have SPDX license declared
+			    ERROR("New file '$expect_spdx_file' requires " .
+				  "'SPDX-License-Identifer'");
+			} else {
+			    # Other files MAY have SPDX license if appropriate
+			    WARNING("Does new file '$expect_spdx_file' need " .
+				    "'SPDX-License-Identifer'?");
+			}
+		    }
+		    $expect_spdx = 1;
+		    $expect_spdx_file = undef;
+		} elsif ($expect_spdx) {
+		    $expect_spdx_file = $realfile unless
+			defined $expect_spdx_file;
+
+		    # SPDX tags may occurr in comments which were
+		    # stripped from '$line', so use '$rawline'
+		    if ($rawline =~ /SPDX-License-Identifier/) {
+			$expect_spdx = 0;
+			$expect_spdx_file = undef;
+		    }
+		}
+
+# Check SPDX-License-Identifier references a permitted license
+		if ($rawline =~ m,SPDX-License-Identifier: (.*?)(\*/)?\s*$,) {
+		    &checkspdx($realfile, $1);
+		}
+
+		if ($rawline =~ m,(SPDX-[a-zA-Z0-9-_]+):,) {
+		    my $tag = $1;
+		    my @permitted = qw(
+			SPDX-License-Identifier
+		    );
+
+		    unless (grep { /^$tag$/ } @permitted) {
+			ERROR("Tag $tag not permitted in QEMU code, valid " .
+			      "choices are: " . join(", ", @permitted));
+		    }
+		}
+
 # Check for wrappage within a valid hunk of the file
 		if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) {
 			ERROR("patch seems to be corrupt (line wrapped?)\n" .