38
38
import fnmatch
39
39
import collections
40
40
from io import StringIO
41
+ from typing import Iterable , Tuple
41
42
42
43
import mx
43
44
import mx_compiler
@@ -525,13 +526,6 @@ def help_stdout_check(output):
525
526
526
527
with Task ('Validate JSON build info' , tasks , tags = [GraalTags .helloworld ]) as t :
527
528
if t :
528
- import json
529
- try :
530
- from jsonschema import validate as json_validate
531
- from jsonschema .exceptions import ValidationError , SchemaError
532
- except ImportError :
533
- mx .abort ('Unable to import jsonschema' )
534
-
535
529
json_and_schema_file_pairs = [
536
530
('build-artifacts.json' , 'build-artifacts-schema-v0.9.0.json' ),
537
531
('build-output.json' , 'build-output-schema-v0.9.4.json' ),
@@ -540,19 +534,7 @@ def help_stdout_check(output):
540
534
build_output_file = join (svmbuild_dir (), 'build-output.json' )
541
535
helloworld (['--output-path' , svmbuild_dir ()] + svm_experimental_options ([f'-H:BuildOutputJSONFile={ build_output_file } ' , '-H:+GenerateBuildArtifactsFile' ]))
542
536
543
- try :
544
- for json_file , schema_file in json_and_schema_file_pairs :
545
- with open (join (svmbuild_dir (), json_file )) as f :
546
- json_contents = json .load (f )
547
- with open (join (suite .dir , '..' , 'docs' , 'reference-manual' , 'native-image' , 'assets' , schema_file )) as f :
548
- schema_contents = json .load (f )
549
- json_validate (json_contents , schema_contents )
550
- except IOError as e :
551
- mx .abort (f'Unable to load JSON build info: { e } ' )
552
- except ValidationError as e :
553
- mx .abort (f'Unable to validate JSON build info against the schema: { e } ' )
554
- except SchemaError as e :
555
- mx .abort (f'JSON schema not valid: { e } ' )
537
+ validate_dir_files_with_file_schema_pairs (svmbuild_dir (), json_and_schema_file_pairs )
556
538
557
539
with Task ('java agent tests' , tasks , tags = [GraalTags .java_agent ]) as t :
558
540
if t :
@@ -2494,7 +2476,7 @@ def compute_graal_compiler_flags_map(self):
2494
2476
2495
2477
return graal_compiler_flags_map
2496
2478
2497
- def get_jsonschema_validator (schema_path ):
2479
+ def create_jsonschema_validator (schema_path ):
2498
2480
"""Create and return a jsonschema Validator for the schema at the given file path. Abort on missing jsonschema or invalid schema."""
2499
2481
import json
2500
2482
try :
@@ -2510,7 +2492,7 @@ def get_jsonschema_validator(schema_path):
2510
2492
except ImportError as e :
2511
2493
mx .abort (
2512
2494
'Python module "jsonschema" is required to validate reachability metadata but was not found. '
2513
- 'Install it with: "python3 -m pip install --user jsonschema" ( or "pip3 install jsonschema" ). '
2495
+ 'Install it with: \n \n python3 -m pip install --user jsonschema \n \n or \n \n pip3 install jsonschema). '
2514
2496
f'Original error: { e } ' )
2515
2497
try :
2516
2498
if hasattr (jsonschema , 'Draft202012Validator' ):
@@ -2520,8 +2502,8 @@ def get_jsonschema_validator(schema_path):
2520
2502
except (jsonschema .exceptions .SchemaError , TypeError ) as e :
2521
2503
mx .abort (f'Invalid reachability metadata schema: { e } ' )
2522
2504
2523
- def validate_json_file_against_schema (validator , file_path ):
2524
- """Validates a JSON file against the provided Validator. Returns a list of error strings; empty if valid."""
2505
+ def validate_json_file_with_validator (validator , file_path ):
2506
+ """Validates a JSON file against the provided Validator. Returns a list of detailed error strings; empty if valid."""
2525
2507
import json
2526
2508
try :
2527
2509
with open (file_path , 'r' , encoding = 'utf-8' ) as f :
@@ -2531,12 +2513,55 @@ def validate_json_file_against_schema(validator, file_path):
2531
2513
except OSError as e :
2532
2514
mx .abort (f'I/O error: { e } ' )
2533
2515
2516
+ # Use jsonschema's ValidationError string representation to produce messages in the form:
2517
+ # Failed validating '<validator>' in schema[...] :
2518
+ # { ... failing subschema ... }
2519
+ #
2520
+ # On instance[...] :
2521
+ # <offending instance>
2534
2522
errors = []
2535
2523
for err in validator .iter_errors (data ):
2536
- path = '/' .join ([str (p ) for p in err .path ]) if err .path else '#'
2537
- errors .append (f'{ path } : { err .message } ' )
2524
+ errors .append (str (err ))
2538
2525
return errors
2539
2526
2527
+ def validate_dir_files_with_file_schema_pairs (dir_with_json : str , json_and_schema_file_pairs : Iterable [Tuple [str , str ]]) -> None :
2528
+ """
2529
+ Validate JSON files in a directory against corresponding JSON Schemas.
2530
+
2531
+ This scans the given directory for each JSON filename listed in json_and_schema_file_pairs.
2532
+ For every file that exists, it validates the file against the associated schema located in
2533
+ the Native Image docs assets directory. Validation uses a pre-built jsonschema.Validator
2534
+ per schema to ensure consistent behavior and error reporting.
2535
+
2536
+ Parameters:
2537
+ - dir_with_json: Directory path containing JSON files to validate.
2538
+ - json_and_schema_file_pairs: Iterable of (json_filename, schema_filename) pairs.
2539
+
2540
+ Behavior:
2541
+ - Logs each validation attempt.
2542
+ - Accumulates all validation errors across files.
2543
+ - Calls mx.abort with a detailed message if any error is found; otherwise returns None.
2544
+ """
2545
+ assets_dir = join (suite .dir , '..' , 'docs' , 'reference-manual' , 'native-image' , 'assets' )
2546
+
2547
+ # Build validators for all known schema files using CE helper to ensure uniform behavior and error reporting.
2548
+ validators = {}
2549
+ for _ , schema_file in json_and_schema_file_pairs :
2550
+ schema_path = join (assets_dir , schema_file )
2551
+ validators [schema_file ] = create_jsonschema_validator (schema_path )
2552
+
2553
+ # Validate any present config files
2554
+ validation_errors = []
2555
+ for json_filename , schema_file in json_and_schema_file_pairs :
2556
+ json_path = join (dir_with_json , json_filename )
2557
+ if exists (json_path ):
2558
+ mx .log (f'Validating { json_path } against { schema_file } ...' )
2559
+ errs = validate_json_file_with_validator (validators [schema_file ], json_path )
2560
+ validation_errors .extend (errs )
2561
+
2562
+ if validation_errors :
2563
+ mx .abort ('Unable to validate JSON file against the schema:\n \n ' + '\n \n ' .join (validation_errors ))
2564
+
2540
2565
class SubstrateCompilerFlagsBuildTask (mx .ArchivableBuildTask ):
2541
2566
def __init__ (self , subject , args ):
2542
2567
mx .ArchivableBuildTask .__init__ (self , subject , args , 1 )
0 commit comments