3838import fnmatch
3939import collections
4040from io import StringIO
41+ from typing import Iterable , Tuple
4142
4243import mx
4344import mx_compiler
@@ -525,13 +526,6 @@ def help_stdout_check(output):
525526
526527 with Task ('Validate JSON build info' , tasks , tags = [GraalTags .helloworld ]) as t :
527528 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-
535529 json_and_schema_file_pairs = [
536530 ('build-artifacts.json' , 'build-artifacts-schema-v0.9.0.json' ),
537531 ('build-output.json' , 'build-output-schema-v0.9.4.json' ),
@@ -540,19 +534,7 @@ def help_stdout_check(output):
540534 build_output_file = join (svmbuild_dir (), 'build-output.json' )
541535 helloworld (['--output-path' , svmbuild_dir ()] + svm_experimental_options ([f'-H:BuildOutputJSONFile={ build_output_file } ' , '-H:+GenerateBuildArtifactsFile' ]))
542536
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 )
556538
557539 with Task ('java agent tests' , tasks , tags = [GraalTags .java_agent ]) as t :
558540 if t :
@@ -2494,7 +2476,7 @@ def compute_graal_compiler_flags_map(self):
24942476
24952477 return graal_compiler_flags_map
24962478
2497- def get_jsonschema_validator (schema_path ):
2479+ def create_jsonschema_validator (schema_path ):
24982480 """Create and return a jsonschema Validator for the schema at the given file path. Abort on missing jsonschema or invalid schema."""
24992481 import json
25002482 try :
@@ -2510,7 +2492,7 @@ def get_jsonschema_validator(schema_path):
25102492 except ImportError as e :
25112493 mx .abort (
25122494 '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). '
25142496 f'Original error: { e } ' )
25152497 try :
25162498 if hasattr (jsonschema , 'Draft202012Validator' ):
@@ -2520,8 +2502,8 @@ def get_jsonschema_validator(schema_path):
25202502 except (jsonschema .exceptions .SchemaError , TypeError ) as e :
25212503 mx .abort (f'Invalid reachability metadata schema: { e } ' )
25222504
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."""
25252507 import json
25262508 try :
25272509 with open (file_path , 'r' , encoding = 'utf-8' ) as f :
@@ -2531,12 +2513,55 @@ def validate_json_file_against_schema(validator, file_path):
25312513 except OSError as e :
25322514 mx .abort (f'I/O error: { e } ' )
25332515
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>
25342522 errors = []
25352523 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 ))
25382525 return errors
25392526
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+
25402565class SubstrateCompilerFlagsBuildTask (mx .ArchivableBuildTask ):
25412566 def __init__ (self , subject , args ):
25422567 mx .ArchivableBuildTask .__init__ (self , subject , args , 1 )
0 commit comments