Skip to content

Commit 793c52d

Browse files
committed
Use common JSON schema validation
1 parent 0cb0d96 commit 793c52d

File tree

1 file changed

+51
-26
lines changed

1 file changed

+51
-26
lines changed

substratevm/mx.substratevm/mx_substratevm.py

Lines changed: 51 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import fnmatch
3939
import collections
4040
from io import StringIO
41+
from typing import Iterable, Tuple
4142

4243
import mx
4344
import 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\npython3 -m pip install --user jsonschema \n\n or \n\npip3 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+
25402565
class SubstrateCompilerFlagsBuildTask(mx.ArchivableBuildTask):
25412566
def __init__(self, subject, args):
25422567
mx.ArchivableBuildTask.__init__(self, subject, args, 1)

0 commit comments

Comments
 (0)