Jon Turney via Cygwin-apps wrote (thread "[PATCH cygport] Add
repro-finish command"):
...
PS: I have a local script which checks SPDX Identifiers and
expressions. Any interest to add this to cygport and then check
LICENSE settings?
Oh, yes please. That sounds like a good idea.
Attached.
The new script uses the SPDX webpages to create the license file. I
didn't find a usable single license list at https://github.com/spdx
The data/spdx-licenses file is not included in the patch. It could be
generated from the source dir with:
$ tools/spdx-check -f data/spdx-licenses -m
...
data/spdx-licenses: created
$ sha1sum data/spdx-licenses
80a19d6891d08bf34113464464ee12308374c792 *data/spdx-licenses
The changes to the meson files are guessed. I didn't test the meson
build yet.
--
Regards,
Christian
From 61f75757fa8e9118207cc09cf4a621aac8a4da78 Mon Sep 17 00:00:00 2001
From: Christian Franke <christian.fra...@t-online.de>
Date: Tue, 30 Apr 2024 19:28:01 +0200
Subject: [PATCH] Add check of SPDX expression provided by LICENSE variable
The new script 'tools/spdx-checks' checks a SPDX license expression.
License identifiers are provided by the new file 'spdx-licenses'
which could be created by the script from the related SPDX webpages.
---
bin/cygport.in | 17 ++++
data/meson.build | 1 +
tools/meson.build | 1 +
tools/spdx-check | 198 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 217 insertions(+)
create mode 100644 tools/spdx-check
diff --git a/bin/cygport.in b/bin/cygport.in
index 15bd559e..3166beba 100755
--- a/bin/cygport.in
+++ b/bin/cygport.in
@@ -41,6 +41,7 @@ declare -r _cygport_version=@VERSION@;
declare -r _privdatadir=@pkgdatadir@;
declare -r _privclassdir=@cygclassdir@;
declare -r _privlibdir=@cygpartdir@;
+declare -r _privtoolsdir=@pkgdatadir@/tools;
declare -r _privgnuconfigdir=@gnuconfigdir@;
declare -r _privsysconfdir=@sysconfdir@;
@@ -489,6 +490,22 @@ do
fi
done
+if [ "${LICENSE+y}" = "y" ]
+then
+ if ! _out=$(${_privtoolsdir}/spdx-check -f
${_privdatadir}/spdx-licenses "${LICENSE}" 2>&1)
+ then
+ warning "LICENSE='${LICENSE}' is invalid:"
+ echo "${_out}"
+ elif [ "${_out:+y}" = "y" ]
+ then
+ warning "LICENSE='${LICENSE}' has warnings:"
+ echo "${_out}"
+ else
+ inform "LICENSE='${LICENSE}' is valid"
+ fi
+ unset _out
+fi
+
for restrict in ${RESTRICT//,/ }
do
declare _CYGPORT_RESTRICT_${restrict//-/_}_=1
diff --git a/data/meson.build b/data/meson.build
index 51c6a5fd..e83a90fe 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -2,6 +2,7 @@ datadocs = files('cygport.conf', 'mirrors')
install_data('mirrors',
'sample.cygport',
+ 'spdx-licenses',
install_dir: pkgdatadir)
install_data('gnuconfig/config.guess',
diff --git a/tools/meson.build b/tools/meson.build
index acd83926..96d8d19e 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -1,6 +1,7 @@
tools = files(
'deb2targz',
'pkgrip',
+ 'spdx-check',
'sysrootize'
)
diff --git a/tools/spdx-check b/tools/spdx-check
new file mode 100644
index 00000000..bffcaae0
--- /dev/null
+++ b/tools/spdx-check
@@ -0,0 +1,198 @@
+#! /bin/bash
+###############################################################################
+#
+# spdx-check - check SPDX license expression
+#
+# Copyright (C) 2024 Christian Franke
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+################################################################################
+
+set -e -o pipefail
+myname=$0
+
+# SPDX license list web pages
+spdx_url_lic="https://spdx.org/licenses/index.html"
+spdx_url_exc="https://spdx.org/licenses/exceptions-index.html"
+
+# Default license file
+def_spdx_file="$(dirname "$myname")/spdx-licenses"
+
+usage()
+{
+ cat <<EOF
+Check SPDX license expression.
+
+Usage: $myname [-f FILE] [-mu] 'SPDX_EXPR'
+ $myname [-f FILE] -mu
+
+ -f read license identifiers from FILE
+ [default: $def_spdx_file]
+ -m create missing license file from SPDX webpages
+ -u always update the license file
+
+ SPDX_EXPR check this SPDX license expression
+EOF
+ exit 1
+}
+
+error()
+{
+ echo "Error:" "$@" >&2
+ exit 1
+}
+
+warning()
+{
+ echo "Warning:" "$@" >&2
+}
+
+check_spdx_id()
+{
+ local id=$1
+ local m m_id
+
+ if ! [ -f "$spdx_file" ]; then
+ warning "Missing '$spdx_file' - SPDX identifier '$1' not checked"
+ return 0
+ fi
+
+ # SPDX identifiers are case insensitive but the correct case is recommended
+ m=$(grep -Ei -m 1 "^!?&?${id//+/\\+}\$" "$spdx_file" 2>/dev/null) \
+ || error "Unknown SPDX identifier '$id'"
+
+ # TODO: Distinguish licenses and exceptions
+ m_id=${m#!}; m_id=${m_id#&}
+
+ [ "$m_id" = "$id" ] || warning "It is recommended to use '$m_id' instead of
'$id'"
+ [ "$m" = "${m#!}" ] || warning "SPDX identifier '$m_id' is deprecated"
+}
+
+check_spdx_expr()
+{
+ local x=$1
+ local f s t
+
+ # Insert spaces around tokens to simplify parsing
+ x=" $x "; x=${x//(/ ( }; x=${x//)/ ) }
+
+ # Check tokens
+ f=false
+ for t in $x; do
+ f=true
+ case $t in
+ AND|OR|WITH|[\(\)])
+ ;;
+ [Aa][Nn][Dd]|[Oo][Rr]|[Ww][Ii][Tt][Hh])
+ error "Invalid token '$t' - use '${t@U}' instead" ;;
+ [0-9A-Za-z]*)
+ s=${t%+}; s=${s//[-.0-9A-Za-z]/}
+ [ -z "$s" ] || error "Invalid character(s) '$s' in '$t'" ;;
+ *)
+ error "Invalid token '$t'" ;;
+ esac
+ done
+ $f || error "Expression is empty"
+
+ # Check expression syntax heuristically using these replacements:
+ # - all operators -> '%'
+ # - all operands -> '@'
+ # - remove all spaces
+ # - '(@...%@)' -> '@' -- in a 'loop' to restart from the beginning
+ # - '@...%@' -> '' -- syntax error if nonempty
+ s=$(
+ sed -E \
+ -e 's/ (AND|OR|WITH) / % /g' \
+ -e 's/ [^ %()@]+ / @ /g' \
+ -e 's/ //g' \
+ -e ':loop' \
+ -e 's/\(@(%@)*\)/@/g' \
+ -e 't loop' \
+ -e 's/^@(%@)*$//' \
+ <<<"$x"
+ )
+ [ -z "$s" ] || error "Invalid syntax of SPDX expression"
+
+ # Check license identifiers
+ for t in $x; do
+ case $t in
+ AND|OR|WITH|[\(\)]) ;;
+ *) check_spdx_id "$t"
+ esac
+ done
+}
+
+# Extract identifiers from SPDX webpage and prepend flags
+html2id()
+{
+ sed -n \
+ -e '1,/<h2[^>]*>Deprecated/s/^.*<code
property="spdx:licenseId">\([^>]*\)<.*$/\1/p' \
+ -e '/<h2[^>]*>Deprecated/,$s/^.*<code
property="spdx:licenseId">\([^>]*\)<.*$/!\1/p' \
+ -e '1,/<h2[^>]*>Deprecated/s/^.*<code
property="spdx:licenseExceptionId">\([^>]*\)<.*$/\&\1/p' \
+ -e '/<h2[^>]*>Deprecated/,$s/^.*<code
property="spdx:licenseExceptionId">\([^>]*\)<.*$/!\&\1/p'
+}
+
+get_spdx_file()
+{
+ local f="$spdx_file.new"
+ local n1 n2
+
+ cat <<EOF >>"$f"
+# List of SPDX identifiers
+#
+# Created by '${myname##*/}' from these web pages:
+# $spdx_url_lic
+# $spdx_url_exc
+#
+# Flags: '!' - deprecated, '&' - license exception
+#
+EOF
+
+ # Download and extract identifiers
+ wget -O - "$spdx_url_lic" | html2id >>"$f"
+ n1=$(wc -l <"$f")
+ echo "#" >>"$f"
+ wget -O - "$spdx_url_exc" | html2id >>"$f"
+ n2=$(wc -l <"$f")
+
+ # Check length to detect download problems
+ { [[ n1 -gt 500 ]] && [[ $((n2-n1)) -gt 50 ]]; } || error "$f: File too
small"
+
+ if [ -f "$spdx_file" ]; then
+ # Keep old file if unchanged otherwise keep it as backup
+ if cmp -s "$spdx_file" "$f"; then
+ echo "$spdx_file: unchanged"
+ rm -f "$f"
+ else
+ mv -bf "$f" "$spdx_file"
+ echo "$spdx_file: updated"
+ fi
+ else
+ mv -f "$f" "$spdx_file"
+ echo "$spdx_file: created"
+ fi
+}
+
+# Parse options
+spdx_file=$def_spdx_file
+m_opt=false; u_opt=false
+
+while true; do case $1 in
+ -f) [ $# -gt 1 ] || usage; shift; spdx_file=$1 ;;
+ -m) m_opt=true ;;
+ -u) u_opt=true; m_opt=true ;;
+ -*) usage ;;
+ *) break ;;
+esac; shift; done
+{ [ $# = 0 ] && $m_opt; } || [ $# = 1 ] || usage
+
+# Create or update the license file if requested
+if $u_opt || { $m_opt && ! [ -f "$spdx_file" ]; }; then
+ get_spdx_file
+fi
+
+# Check license expression
+if [ $# = 1 ]; then
+ check_spdx_expr "$1"
+fi
--
2.43.0