Build: Add output file diffing to config_gen.py

Currently, whenever CMake is reconfigured, config_gen.py will always
regenerate the output config header and json, forcing a timestamp
update and a rebuild. This change adds a --skip-unchanged option which
skips writing to the output file if the write would not change the
file's contents. The default is off to avoid breaking builds that rely
on the existing behavior of always overwriting.

Signed-off-by: James Martin <fennelfoxxo@gmail.com>
This commit is contained in:
Fennelfoxxo
2025-04-08 05:11:01 -07:00
committed by Gerwin Klein
parent 162a38e865
commit e8094340a3

View File

@@ -21,12 +21,19 @@ def main():
default=sys.stdin, default=sys.stdin,
help="Input YAML file.", help="Input YAML file.",
) )
parser.add_argument(
"--skip-unchanged",
dest="skip_unchanged",
action="store_true",
help="Only write output file if new content is different"
)
parser.add_argument( parser.add_argument(
"--write-c", "--write-c",
metavar="OUT_FILE", metavar="OUT_FILE",
dest="out_file_c", dest="out_file_c",
nargs="?", nargs="?",
type=argparse.FileType("w"), # Use a+ mode to open in read/write mode without truncating existing contents
type=argparse.FileType("a+"),
const=sys.stdout, const=sys.stdout,
default=None, default=None,
help="Output C header file.", help="Output C header file.",
@@ -36,25 +43,25 @@ def main():
metavar="OUT_FILE", metavar="OUT_FILE",
dest="out_file_json", dest="out_file_json",
nargs="?", nargs="?",
type=argparse.FileType("w"), type=argparse.FileType("a+"),
const=sys.stdout, const=sys.stdout,
default=None, default=None,
help="Output JSON file.", help="Output JSON file.",
) )
args = parser.parse_args() args = parser.parse_args()
generate(args.in_file_yaml, out_file_c=args.out_file_c, generate(args.in_file_yaml, out_file_c=args.out_file_c,
out_file_json=args.out_file_json) out_file_json=args.out_file_json, skip_unchanged=args.skip_unchanged)
def generate(in_file_yaml, out_file_c=None, out_file_json=None): def generate(in_file_yaml, out_file_c=None, out_file_json=None, skip_unchanged=False):
config = yaml.safe_load(in_file_yaml) config = yaml.safe_load(in_file_yaml)
if out_file_c is not None: if out_file_c is not None:
generate_c(config, out_file_c) generate_c(config, out_file_c, skip_unchanged)
if out_file_json is not None: if out_file_json is not None:
generate_json(config, out_file_json) generate_json(config, out_file_json, skip_unchanged)
def generate_c(config, out_file): def generate_c(config, out_file, skip_unchanged):
header_contents = "#pragma once\n\n" header_contents = "#pragma once\n\n"
for key, value in config.items(): for key, value in config.items():
@@ -76,11 +83,24 @@ def generate_c(config, out_file):
header_contents += f"{entry}\n" header_contents += f"{entry}\n"
out_file.write(header_contents) write_file_lazy(out_file, header_contents, skip_unchanged)
def generate_json(config, out_file): def generate_json(config, out_file, skip_unchanged):
json.dump(config, out_file, indent=4) json_contents = json.dumps(config, indent=4)
write_file_lazy(out_file, json_contents, skip_unchanged)
def write_file_lazy(out_file, content, skip_unchanged):
if out_file == sys.stdout:
out_file.write(content)
return
out_file.seek(0)
if not skip_unchanged or out_file.read() != content:
out_file.seek(0)
out_file.truncate()
out_file.write(content)
if __name__ == "__main__": if __name__ == "__main__":