+1
CMakeLists.txt
+1
CMakeLists.txt
+77
cmake/MergeJSON.cmake
+77
cmake/MergeJSON.cmake
···
1
+
# Copyright 2025, NVIDIA CORPORATION.
2
+
# SPDX-License-Identifier: BSL-1.0
3
+
4
+
# JSON merge function: merges multiple JSON files into one output file
5
+
#
6
+
# To use this function in your CMakeLists.txt:
7
+
# include(${CMAKE_SOURCE_DIR}/scripts/CMakeLists.txt)
8
+
#
9
+
# Usage:
10
+
# merge_json_files(
11
+
# OUTPUT <output_file>
12
+
# SOURCES <json_file1> <json_file2> ...
13
+
# [ALLOW_OVERWRITE]
14
+
# [IGNORE_SCHEMA]
15
+
# )
16
+
#
17
+
# Arguments:
18
+
# OUTPUT - Output file path (required)
19
+
# SOURCES - List of JSON files to merge (required)
20
+
# ALLOW_OVERWRITE - Optional flag to allow duplicate keys
21
+
# IGNORE_SCHEMA - Optional flag to ignore "$schema" field in output
22
+
#
23
+
# The function automatically:
24
+
# - Sorts input files alphabetically for consistent results
25
+
# - Tracks dependencies so output is regenerated when inputs change
26
+
# - Errors on duplicate keys unless ALLOW_OVERWRITE is specified
27
+
#
28
+
# Example:
29
+
# merge_json_files(
30
+
# OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/merged.json"
31
+
# SOURCES file1.json file2.json file3.json
32
+
# IGNORE_SCHEMA
33
+
# )
34
+
35
+
function(merge_json_files)
36
+
set(options ALLOW_OVERWRITE IGNORE_SCHEMA)
37
+
set(oneValueArgs OUTPUT)
38
+
set(multiValueArgs SOURCES)
39
+
40
+
cmake_parse_arguments(
41
+
MERGE_JSON "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}
42
+
)
43
+
44
+
if(NOT MERGE_JSON_OUTPUT)
45
+
message(FATAL_ERROR "merge_json_files: OUTPUT argument is required")
46
+
endif()
47
+
48
+
if(NOT MERGE_JSON_SOURCES)
49
+
message(FATAL_ERROR "merge_json_files: SOURCES argument is required")
50
+
endif()
51
+
52
+
# Build command arguments
53
+
set(MERGE_COMMAND ${PYTHON_EXECUTABLE}
54
+
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/merge_json.py -o
55
+
"${MERGE_JSON_OUTPUT}"
56
+
)
57
+
58
+
if(MERGE_JSON_ALLOW_OVERWRITE)
59
+
list(APPEND MERGE_COMMAND --allow-overwrite)
60
+
endif()
61
+
62
+
if(MERGE_JSON_IGNORE_SCHEMA)
63
+
list(APPEND MERGE_COMMAND --ignore-schema)
64
+
endif()
65
+
66
+
list(APPEND MERGE_COMMAND ${MERGE_JSON_SOURCES})
67
+
68
+
# Create custom command with proper dependencies
69
+
add_custom_command(
70
+
OUTPUT "${MERGE_JSON_OUTPUT}"
71
+
COMMAND ${MERGE_COMMAND}
72
+
VERBATIM
73
+
DEPENDS ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/merge_json.py
74
+
${MERGE_JSON_SOURCES}
75
+
COMMENT "Merging JSON files into ${MERGE_JSON_OUTPUT}"
76
+
)
77
+
endfunction()
+139
cmake/merge_json.py
+139
cmake/merge_json.py
···
1
+
#!/usr/bin/env python3
2
+
# Copyright 2025, NVIDIA CORPORATION.
3
+
# SPDX-License-Identifier: BSL-1.0
4
+
"""
5
+
Helper script to merge multiple JSON files into one.
6
+
7
+
This script merges the root JSON objects from multiple input files
8
+
while preserving field ordering. Files are processed in sorted order
9
+
to ensure consistent results. Fields are merged in the order they
10
+
appear across the input files.
11
+
"""
12
+
13
+
import argparse
14
+
import json
15
+
import sys
16
+
from pathlib import Path
17
+
18
+
19
+
def merge_json_files(json_files, output_file, allow_overwrite=False,
20
+
ignore_schema=False):
21
+
"""
22
+
Merge multiple JSON files into a single output file.
23
+
24
+
Args:
25
+
json_files: List of paths to JSON files to merge
26
+
output_file: Path to the output file
27
+
allow_overwrite: If True, allow later files to overwrite keys
28
+
from earlier files
29
+
ignore_schema: If True, ignore the "$schema" field in input files
30
+
"""
31
+
merged = {}
32
+
33
+
# Sort files to ensure consistent ordering
34
+
sorted_files = sorted(json_files)
35
+
36
+
for json_file in sorted_files:
37
+
try:
38
+
with open(json_file, 'r', encoding='utf-8') as f:
39
+
data = json.load(f)
40
+
41
+
if not isinstance(data, dict):
42
+
print(f"Error: {json_file} does not contain a JSON "
43
+
f"object at root level",
44
+
file=sys.stderr)
45
+
sys.exit(1)
46
+
47
+
# Merge while preserving order - check for duplicates
48
+
for key, value in data.items():
49
+
if key == "$schema" and ignore_schema:
50
+
continue
51
+
if key in merged:
52
+
if allow_overwrite:
53
+
print(f"Warning: Key '{key}' from {json_file} "
54
+
f"overwrites previous definition",
55
+
file=sys.stderr)
56
+
else:
57
+
print(f"Error: Duplicate key '{key}' found in "
58
+
f"{json_file}. Use --allow-overwrite to "
59
+
f"permit overwriting.",
60
+
file=sys.stderr)
61
+
sys.exit(1)
62
+
merged[key] = value
63
+
64
+
except FileNotFoundError:
65
+
print(f"Error: File not found: {json_file}", file=sys.stderr)
66
+
sys.exit(1)
67
+
except json.JSONDecodeError as e:
68
+
print(f"Error: Failed to parse JSON from {json_file}: {e}",
69
+
file=sys.stderr)
70
+
sys.exit(1)
71
+
except Exception as e:
72
+
print(f"Error: Failed to read {json_file}: {e}",
73
+
file=sys.stderr)
74
+
sys.exit(1)
75
+
76
+
77
+
# Write merged JSON to output file
78
+
try:
79
+
with open(output_file, 'w', encoding='utf-8') as f:
80
+
json.dump(merged, f, indent='\t', ensure_ascii=False)
81
+
f.write('\n') # Add trailing newline
82
+
83
+
# Printing, left in if we want it in the future.
84
+
if False:
85
+
print(f"Successfully merged {len(json_files)} file(s) into "
86
+
f"{output_file}")
87
+
except Exception as e:
88
+
print(f"Error: Failed to write to {output_file}: {e}",
89
+
file=sys.stderr)
90
+
sys.exit(1)
91
+
92
+
93
+
def main():
94
+
parser = argparse.ArgumentParser(
95
+
description='Merge multiple JSON files into one, preserving '
96
+
'field ordering.',
97
+
formatter_class=argparse.RawDescriptionHelpFormatter,
98
+
epilog="""
99
+
Examples:
100
+
%(prog)s -o merged.json file1.json file2.json file3.json
101
+
%(prog)s -o output.json base.json overrides.json
102
+
%(prog)s -o output.json --allow-overwrite base.json overrides.json
103
+
%(prog)s -o output.json --ignore-schema schema.json data.json
104
+
"""
105
+
)
106
+
107
+
parser.add_argument('-o', '--output',
108
+
required=True,
109
+
metavar='OUTPUT',
110
+
help='Output file path for the merged JSON')
111
+
112
+
parser.add_argument('--allow-overwrite',
113
+
action='store_true',
114
+
help='Allow later files to overwrite duplicate '
115
+
'keys from earlier files')
116
+
117
+
parser.add_argument('--ignore-schema',
118
+
action='store_true',
119
+
help='Ignore the "$schema" field in input files')
120
+
121
+
parser.add_argument('json_files',
122
+
nargs='+',
123
+
metavar='JSON_FILE',
124
+
help='JSON files (will be sorted alphabetically)')
125
+
126
+
args = parser.parse_args()
127
+
128
+
# Validate that we have at least one input file
129
+
if not args.json_files:
130
+
print("Error: At least one JSON file must be provided",
131
+
file=sys.stderr)
132
+
sys.exit(1)
133
+
134
+
merge_json_files(args.json_files, args.output, args.allow_overwrite,
135
+
args.ignore_schema)
136
+
137
+
138
+
if __name__ == '__main__':
139
+
main()