|
54 | 54 | import datetime
|
55 | 55 | import shutil
|
56 | 56 | import tempfile
|
| 57 | +from typing import Iterable, Tuple |
57 | 58 | from mx_bisect import define_bisect_default_build_steps
|
58 | 59 | from mx_bisect_strategy import BuildStepsGraalVMStrategy
|
59 | 60 |
|
@@ -262,6 +263,117 @@ def jlink_new_jdk(jdk, dst_jdk_dir, module_dists, ignore_dists,
|
262 | 263 | use_upgrade_module_path=use_upgrade_module_path,
|
263 | 264 | default_to_jvmci=default_to_jvmci)
|
264 | 265 |
|
| 266 | + |
| 267 | +def create_jsonschema_validator(schema_path): |
| 268 | + """Create and return a jsonschema Validator for the schema at the given file path. Abort on missing jsonschema or invalid schema.""" |
| 269 | + import json |
| 270 | + try: |
| 271 | + with open(schema_path, "r", encoding="utf-8") as f: |
| 272 | + schema = json.load(f) |
| 273 | + except json.JSONDecodeError as e: |
| 274 | + mx.abort(f'Failed to parse JSON in schema file "{schema_path}" at line {e.lineno}, column {e.colno}: {e.msg}') |
| 275 | + except OSError as e: |
| 276 | + mx.abort(f'I/O error when opening schema file "{schema_path}": {e}') |
| 277 | + |
| 278 | + try: |
| 279 | + import jsonschema # type: ignore |
| 280 | + except ImportError as e: |
| 281 | + mx.abort( |
| 282 | + 'Python module "jsonschema" is required to validate reachability metadata but was not found. ' |
| 283 | + 'Install it with: \n\npython3 -m pip install --user jsonschema \n\n or \n\npip3 install jsonschema\n\n' |
| 284 | + f'Original error: {e}') |
| 285 | + try: |
| 286 | + if hasattr(jsonschema, 'Draft202012Validator'): |
| 287 | + return jsonschema.Draft202012Validator(schema) # type: ignore[attr-defined] |
| 288 | + else: |
| 289 | + return jsonschema.Draft7Validator(schema) # type: ignore |
| 290 | + except (jsonschema.exceptions.SchemaError, TypeError) as e: |
| 291 | + mx.abort(f'Invalid reachability metadata schema: {e}') |
| 292 | + |
| 293 | + |
| 294 | +def validate_json_file_with_validator(validator, file_path): |
| 295 | + """Validates a JSON file against the provided Validator. Returns a list of detailed error strings; empty if valid.""" |
| 296 | + import json |
| 297 | + try: |
| 298 | + with open(file_path, 'r', encoding='utf-8') as f: |
| 299 | + data = json.load(f) |
| 300 | + except json.JSONDecodeError as e: |
| 301 | + mx.abort(f'Invalid JSON syntax at line {e.lineno}, column {e.colno}: {e.msg}') |
| 302 | + except OSError as e: |
| 303 | + mx.abort(f'I/O error: {e}') |
| 304 | + |
| 305 | + # Use jsonschema's ValidationError string representation to produce messages in the form: |
| 306 | + # Failed validating '<validator>' in schema[...] : |
| 307 | + # { ... failing subschema ... } |
| 308 | + # |
| 309 | + # On instance[...] : |
| 310 | + # <offending instance> |
| 311 | + errors = [] |
| 312 | + for err in validator.iter_errors(data): |
| 313 | + errors.append(str(err)) |
| 314 | + return errors |
| 315 | + |
| 316 | + |
| 317 | +def validate_dir_files_with_file_schema_pairs(dir_with_json: str, json_and_schema_file_pairs: Iterable[Tuple[str, str]]) -> None: |
| 318 | + """ |
| 319 | + Validate JSON files in a directory against corresponding JSON Schemas. |
| 320 | +
|
| 321 | + This scans the given directory for each JSON filename listed in json_and_schema_file_pairs. |
| 322 | + For every file that exists, it validates the file against the associated schema located in |
| 323 | + the Native Image docs assets directory. Validation uses a pre-built jsonschema.Validator |
| 324 | + per schema to ensure consistent behavior and error reporting. |
| 325 | +
|
| 326 | + Parameters: |
| 327 | + - dir_with_json: Directory path containing JSON files to validate. |
| 328 | + - json_and_schema_file_pairs: Iterable of (json_filename, schema_filename) pairs. |
| 329 | +
|
| 330 | + Behavior: |
| 331 | + - Logs each validation attempt. |
| 332 | + - Accumulates all validation errors across files. |
| 333 | + - Calls mx.abort with a detailed message if any error is found; otherwise returns None. |
| 334 | + """ |
| 335 | + assets_dir = os.path.join(_suite.dir, '..', 'docs', 'reference-manual', 'native-image', 'assets') |
| 336 | + |
| 337 | + # Build validators for all known schema files using CE helper to ensure uniform behavior and error reporting. |
| 338 | + validators = {} |
| 339 | + for _, schema_file in json_and_schema_file_pairs: |
| 340 | + schema_path = os.path.join(assets_dir, schema_file) |
| 341 | + validators[schema_file] = create_jsonschema_validator(schema_path) |
| 342 | + |
| 343 | + validation_errors_by_file = {} |
| 344 | + for json_filename, schema_file in json_and_schema_file_pairs: |
| 345 | + json_path = os.path.join(dir_with_json, json_filename) |
| 346 | + if os.path.exists(json_path): |
| 347 | + mx.log(f'Validating {json_path} against {schema_file}...') |
| 348 | + errs = validate_json_file_with_validator(validators[schema_file], json_path) |
| 349 | + if errs: |
| 350 | + validation_errors_by_file[(json_path, schema_file)] = errs |
| 351 | + |
| 352 | + if validation_errors_by_file: |
| 353 | + sections = [] |
| 354 | + for (json_path, schema_file), errs in validation_errors_by_file.items(): |
| 355 | + header = ( |
| 356 | + "-------------------------------------------------------------------------------\n" |
| 357 | + f"File: {json_path}\n" |
| 358 | + f"Schema: {schema_file}\n" |
| 359 | + "-------------------------------------------------------------------------------" |
| 360 | + ) |
| 361 | + sections.append(header + "\n\n" + "\n\n".join(errs)) |
| 362 | + msg = "Unable to validate JSON file(s) against the schema:\n\n" + "\n\n".join(sections) |
| 363 | + mx.abort(msg) |
| 364 | + # Validate any present config files |
| 365 | + validation_errors = [] |
| 366 | + for json_filename, schema_file in json_and_schema_file_pairs: |
| 367 | + json_path = os.path.join(dir_with_json, json_filename) |
| 368 | + if os.path.exists(json_path): |
| 369 | + mx.log(f'Validating {json_path} against {schema_file}...') |
| 370 | + errs = validate_json_file_with_validator(validators[schema_file], json_path) |
| 371 | + validation_errors.extend(errs) |
| 372 | + |
| 373 | + if validation_errors: |
| 374 | + mx.abort('Unable to validate JSON file against the schema:\n\n' + '\n\n'.join(validation_errors)) |
| 375 | + |
| 376 | + |
265 | 377 | class GraalVMJDKConfig(mx.JDKConfig):
|
266 | 378 |
|
267 | 379 | # Oracle JDK includes the libjvmci compiler, allowing it to function as GraalVM.
|
|
0 commit comments