36
36
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37
37
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38
38
# SOFTWARE.
39
- from pathlib import Path
39
+ import re
40
40
41
41
import sys
42
+ from pathlib import Path
43
+
44
+ # Approved license identifiers in SPDX "short identifier" format
45
+ ALLOWED_LICENSES = {
46
+ 'MIT' , # https://spdx.org/licenses/MIT.html
47
+ 'BSD-3-Clause' , # https://spdx.org/licenses/BSD-3-Clause.html
48
+ 'BSD-2-Clause' , # https://spdx.org/licenses/BSD-2-Clause.html
49
+ 'Apache-2.0' , # https://spdx.org/licenses/Apache-2.0.html
50
+ 'MPL-2.0' , # https://spdx.org/licenses/MPL-2.0.html
51
+ 'LGPL-2.0-or-later' , # https://spdx.org/licenses/LGPL-2.0-or-later.html
52
+ 'LGPL-3.0-or-later' , # https://spdx.org/licenses/LGPL-3.0-or-later.html
53
+ 'PSF-2.0' , # https://spdx.org/licenses/PSF-2.0.html
54
+ }
55
+ ALLOWED_WITH_CLAUSES = {
56
+ 'openssl-exception' ,
57
+ }
42
58
43
59
SECTIONS = frozenset ({'rules' , 'add-sources' })
44
- RULE_KEYS = frozenset ({'version' , 'patch' , 'subdir' , 'dist-type' , 'install-priority' , 'ignore-rule-on-llvm' })
60
+ RULE_KEYS = frozenset ({'version' , 'patch' , 'license' , ' subdir' , 'dist-type' , 'install-priority' , 'ignore-rule-on-llvm' })
45
61
46
62
if sys .implementation .name == 'graalpy' :
47
63
import ensurepip
@@ -67,6 +83,17 @@ def validate_metadata(package_dir, metadata):
67
83
patch_path = package_dir / patch
68
84
assert patch_path .is_file (), f"Patch file does not exists: { patch_path } "
69
85
patches .add (patch_path )
86
+ license = rule .get ('license' )
87
+ assert license , f"'license' not specified for patch { patch } "
88
+ license = re .sub (r'[()]' , ' ' , license )
89
+ for part in re .split (f'AND|OR' , license ):
90
+ part = part .strip ()
91
+ if ' WITH ' in part :
92
+ part , exception = re .split (r'\s+WITH\s+' , part , 1 )
93
+ assert exception in ALLOWED_WITH_CLAUSES , \
94
+ f"License WITH clause { exception } not in allowed list of clauses: { ', ' .join (ALLOWED_WITH_CLAUSES )} "
95
+ assert part in ALLOWED_LICENSES , \
96
+ f"License { part } not in allowed list of licenses: { ', ' .join (ALLOWED_LICENSES )} "
70
97
if install_priority := rule .get ('install-priority' ):
71
98
assert isinstance (install_priority , int ), "'rules.install_priority' must be an int"
72
99
if dist_type := rule .get ('dist-type' ):
@@ -87,11 +114,17 @@ def validate_metadata(package_dir, metadata):
87
114
88
115
89
116
def test_patch_metadata ():
117
+ errors = []
90
118
for package_dir in patch_dir .iterdir ():
91
119
if package_dir .is_dir ():
92
- if (metadata_path := package_dir / 'metadata.toml' ).is_file ():
93
- with open (metadata_path , 'rb' ) as f :
94
- metadata = tomli .load (f )
95
- validate_metadata (package_dir , metadata )
96
- else :
97
- assert False , f"Patch directory without metadata: { package_dir } "
120
+ try :
121
+ if (metadata_path := package_dir / 'metadata.toml' ).is_file ():
122
+ with open (metadata_path , 'rb' ) as f :
123
+ metadata = tomli .load (f )
124
+ validate_metadata (package_dir , metadata )
125
+ else :
126
+ assert False , f"Patch directory without metadata: { package_dir } "
127
+ except Exception as e :
128
+ errors .append (f"\t { package_dir .name } : { e } " )
129
+ if errors :
130
+ raise AssertionError ("Patch metadata validation failed:\n " + '\n ' .join (errors ))
0 commit comments