diff --git a/.github/workflows/pregenerate.yml b/.github/workflows/pregenerate.yml index 8dc74bc4487..ef63fc78af0 100644 --- a/.github/workflows/pregenerate.yml +++ b/.github/workflows/pregenerate.yml @@ -31,7 +31,7 @@ jobs: - name: Run ntcore run: ./ntcore/generate_topics.py - name: Run wpimath - run: ./wpimath/generate_numbers.py && ./wpimath/generate_quickbuf.py protoc protoc-gen-quickbuf-1.3.3-linux-x86_64.exe + run: ./wpimath/generate_numbers.py && ./wpimath/generate_quickbuf.py --quickbuf_plugin=protoc-gen-quickbuf-1.3.3-linux-x86_64.exe - name: Run HIDs run: ./wpilibj/generate_hids.py && ./wpilibc/generate_hids.py && ./wpilibNewCommands/generate_hids.py - name: Add untracked files to index so they count as changes diff --git a/hal/generate_usage_reporting.py b/hal/generate_usage_reporting.py index 08536a86e2c..95a2357d4f0 100755 --- a/hal/generate_usage_reporting.py +++ b/hal/generate_usage_reporting.py @@ -3,22 +3,23 @@ # Copyright (c) FIRST and other WPILib contributors. # Open Source Software; you can modify and/or share it under the terms of # the WPILib BSD license file in the root directory of this project. -import pathlib +from pathlib import Path +import sys +import argparse -def main(): +def generate_usage_reporting(output_directory: Path, template_directory: Path): # Gets the folder this script is in (the hal/ directory) - HAL_ROOT = pathlib.Path(__file__).parent java_package = "edu/wpi/first/hal" # fmt: off - (HAL_ROOT / "src/generated/main/native/include/hal").mkdir(parents=True, exist_ok=True) - (HAL_ROOT / f"src/generated/main/java/{java_package}").mkdir(parents=True, exist_ok=True) + (output_directory / "main/native/include/hal").mkdir(parents=True, exist_ok=True) + (output_directory / f"main/java/{java_package}").mkdir(parents=True, exist_ok=True) # fmt: on usage_reporting_types_cpp = [] usage_reporting_instances_cpp = [] usage_reporting_types = [] usage_reporting_instances = [] - with open(HAL_ROOT / "src/generate/Instances.txt") as instances: + with (template_directory / "Instances.txt").open(encoding="utf-8") as instances: for instance in instances: usage_reporting_instances_cpp.append(f" {instance.strip()},") usage_reporting_instances.append( @@ -26,7 +27,9 @@ def main(): f" public static final int {instance.strip()};" ) - with open(HAL_ROOT / "src/generate/ResourceType.txt") as resource_types: + with (template_directory / "ResourceType.txt").open( + encoding="utf-8" + ) as resource_types: for resource_type in resource_types: usage_reporting_types_cpp.append(f" {resource_type.strip()},") usage_reporting_types.append( @@ -34,7 +37,9 @@ def main(): f" public static final int {resource_type.strip()};" ) - with open(HAL_ROOT / "src/generate/FRCNetComm.java.in") as java_usage_reporting: + with (template_directory / "FRCNetComm.java.in").open( + encoding="utf-8" + ) as java_usage_reporting: contents = ( # fmt: off java_usage_reporting.read() @@ -43,12 +48,12 @@ def main(): # fmt: on ) - with open( - HAL_ROOT / f"src/generated/main/java/{java_package}/FRCNetComm.java", "w" - ) as java_out: - java_out.write(contents) + frc_net_comm = output_directory / f"main/java/{java_package}/FRCNetComm.java" + frc_net_comm.write_text(contents, encoding="utf-8") - with open(HAL_ROOT / "src/generate/FRCUsageReporting.h.in") as cpp_usage_reporting: + with (template_directory / "FRCUsageReporting.h.in").open( + encoding="utf-8" + ) as cpp_usage_reporting: contents = ( # fmt: off cpp_usage_reporting.read() @@ -57,11 +62,33 @@ def main(): # fmt: on ) - with open( - HAL_ROOT / "src/generated/main/native/include/hal/FRCUsageReporting.h", "w" - ) as cpp_out: - cpp_out.write(contents) + usage_reporting_hdr = ( + output_directory / "main/native/include/hal/FRCUsageReporting.h" + ) + usage_reporting_hdr.write_text(contents, encoding="utf-8") + + +def main(argv): + + dirname = Path(__file__).parent + + parser = argparse.ArgumentParser() + parser.add_argument( + "--output_directory", + help="Optional. If set, will output the generated files to this directory, otherwise it will use a path relative to the script", + default=dirname / "src/generated", + type=Path, + ) + parser.add_argument( + "--template_root", + help="Optional. If set, will use this directory as the root for the jinja templates", + default=dirname / "src/generate", + type=Path, + ) + args = parser.parse_args(argv) + + generate_usage_reporting(args.output_directory, args.template_root) if __name__ == "__main__": - main() + main(sys.argv[1:]) diff --git a/ntcore/generate_topics.py b/ntcore/generate_topics.py index 5afd61a722e..81b1f83a45f 100755 --- a/ntcore/generate_topics.py +++ b/ntcore/generate_topics.py @@ -1,47 +1,39 @@ #!/usr/bin/env python3 - -import glob -import os -import sys -from jinja2 import Environment, FileSystemLoader +import argparse import json +import sys +from pathlib import Path +from typing import Dict, Any +from jinja2 import Environment, FileSystemLoader +from jinja2.environment import Template -def Output(outPath, outfn, contents): - if not os.path.exists(outPath): - os.makedirs(outPath) - - outpathname = f"{outPath}/{outfn}" - - if os.path.exists(outpathname): - with open(outpathname, "r") as f: - if f.read() == contents: - return - - # File either doesn't exist or has different contents - with open(outpathname, "w", newline="\n") as f: - f.write(contents) +def Output(output_dir: Path, controller_name: str, contents: str): + output_dir.mkdir(parents=True, exist_ok=True) + output_file = output_dir / controller_name + output_file.write_text(contents, encoding="utf-8") -def main(): - dirname, _ = os.path.split(os.path.abspath(__file__)) - with open(f"{dirname}/src/generate/types.json") as f: +def generate_topics( + output_directory: Path, template_root: Path, types_schema_file: Path +): + with (types_schema_file).open(encoding="utf-8") as f: types = json.load(f) # Java files + java_template_directory = template_root / "main/java" env = Environment( - loader=FileSystemLoader(f"{dirname}/src/generate/main/java"), autoescape=False + loader=FileSystemLoader(java_template_directory), autoescape=False ) - rootPath = f"{dirname}/src/generated/main/java/edu/wpi/first/networktables" - for fn in glob.glob(f"{dirname}/src/generate/main/java/*.jinja"): - template = env.get_template(os.path.basename(fn)) - outfn = os.path.basename(fn)[:-6] # drop ".jinja" - if os.path.basename(fn).startswith("NetworkTable") or os.path.basename( - fn - ).startswith("Generic"): + + generated_output_dir = output_directory / "main/java/edu/wpi/first/networktables" + for fn in java_template_directory.glob("*.jinja"): + template = env.get_template(fn.name) + outfn = fn.stem + if outfn.startswith("NetworkTable") or outfn.startswith("Generic"): output = template.render(types=types) - Output(rootPath, outfn, output) + Output(generated_output_dir, outfn, output) else: for replacements in types: output = template.render(replacements) @@ -49,79 +41,116 @@ def main(): outfn2 = f"Timestamped{replacements['TypeName']}.java" else: outfn2 = f"{replacements['TypeName']}{outfn}" - Output(rootPath, outfn2, output) + Output(generated_output_dir, outfn2, output) # C++ classes + cpp_subdirectory = "main/native/include/networktables" + cpp_template_directory = template_root / cpp_subdirectory env = Environment( - loader=FileSystemLoader( - f"{dirname}/src/generate/main/native/include/networktables" - ), + loader=FileSystemLoader(cpp_template_directory), autoescape=False, ) - rootPath = f"{dirname}/src/generated/main/native/include/networktables" - for fn in glob.glob( - f"{dirname}/src/generate/main/native/include/networktables/*.jinja" - ): - template = env.get_template(os.path.basename(fn)) - outfn = os.path.basename(fn)[:-6] # drop ".jinja" + + generated_output_dir = output_directory / cpp_subdirectory + for fn in cpp_template_directory.glob("*.jinja"): + template = env.get_template(fn.name) + outfn = fn.stem # drop ".jinja" for replacements in types: output = template.render(replacements) outfn2 = f"{replacements['TypeName']}{outfn}" - Output(rootPath, outfn2, output) + Output(generated_output_dir, outfn2, output) # C++ handle API (header) + hdr_subdirectory = "main/native/include" + hdr_template_directory = template_root / hdr_subdirectory env = Environment( - loader=FileSystemLoader(f"{dirname}/src/generate/main/native/include"), + loader=FileSystemLoader(hdr_template_directory), autoescape=False, ) template = env.get_template("ntcore_cpp_types.h.jinja") output = template.render(types=types) Output( - f"{dirname}/src/generated/main/native/include", + output_directory / hdr_subdirectory, "ntcore_cpp_types.h", output, ) # C++ handle API (source) + cpp_subdirectory = "main/native/cpp" + cpp_template_directory = template_root / cpp_subdirectory env = Environment( - loader=FileSystemLoader(f"{dirname}/src/generate/main/native/cpp"), + loader=FileSystemLoader(cpp_template_directory), autoescape=False, ) template = env.get_template("ntcore_cpp_types.cpp.jinja") output = template.render(types=types) - Output(f"{dirname}/src/generated/main/native/cpp", "ntcore_cpp_types.cpp", output) + Output( + output_directory / cpp_subdirectory, + "ntcore_cpp_types.cpp", + output, + ) # C handle API (header) + hdr_subdirectory = "main/native/include" + hdr_template_directory = template_root / hdr_subdirectory env = Environment( - loader=FileSystemLoader(f"{dirname}/src/generate/main/native/include"), + loader=FileSystemLoader(hdr_template_directory), autoescape=False, ) template = env.get_template("ntcore_c_types.h.jinja") output = template.render(types=types) - Output( - f"{dirname}/src/generated/main/native/include", - "ntcore_c_types.h", - output, - ) + Output(output_directory / hdr_subdirectory, "ntcore_c_types.h", output) # C handle API (source) + c_subdirectory = "main/native/cpp" + c_template_directory = template_root / c_subdirectory env = Environment( - loader=FileSystemLoader(f"{dirname}/src/generate/main/native/cpp"), + loader=FileSystemLoader(c_template_directory), autoescape=False, ) template = env.get_template("ntcore_c_types.cpp.jinja") output = template.render(types=types) - Output(f"{dirname}/src/generated/main/native/cpp", "ntcore_c_types.cpp", output) + Output(output_directory / c_subdirectory, "ntcore_c_types.cpp", output) # JNI + jni_subdirectory = "main/native/cpp/jni" + jni_template_directory = template_root / jni_subdirectory env = Environment( - loader=FileSystemLoader(f"{dirname}/src/generate/main/native/cpp/jni"), + loader=FileSystemLoader(jni_template_directory), autoescape=False, ) template = env.get_template("types_jni.cpp.jinja") output = template.render(types=types) - Output(f"{dirname}/src/generated/main/native/cpp/jni", "types_jni.cpp", output) + Output(output_directory / jni_subdirectory, "types_jni.cpp", output) + + +def main(argv): + script_path = Path(__file__).resolve() + dirname = script_path.parent + + parser = argparse.ArgumentParser() + parser.add_argument( + "--output_directory", + help="Optional. If set, will output the generated files to this directory, otherwise it will use a path relative to the script", + default=dirname / "src/generated", + type=Path, + ) + parser.add_argument( + "--types_schema_file", + help="Optional. If set, this file will be used to load the types schema", + default=dirname / "src/generate/types.json", + type=Path, + ) + parser.add_argument( + "--template_root", + help="Optional. If set, will use this directory as the root for the jinja templates", + default=dirname / "src/generate", + type=Path, + ) + args = parser.parse_args(argv) + + generate_topics(args.output_directory, args.template_root, args.types_schema_file) if __name__ == "__main__": - main() + main(sys.argv[1:]) diff --git a/wpilibNewCommands/generate_hids.py b/wpilibNewCommands/generate_hids.py index 8836253e5b7..5fc027d5a8d 100755 --- a/wpilibNewCommands/generate_hids.py +++ b/wpilibNewCommands/generate_hids.py @@ -4,85 +4,92 @@ # Open Source Software; you can modify and/or share it under the terms of # the WPILib BSD license file in the root directory of this project. import json -import os +import sys +import argparse +from pathlib import Path from jinja2 import Environment, FileSystemLoader -def write_controller_file(outPath, controllerName, contents): - if not os.path.exists(outPath): - os.makedirs(outPath) +def write_controller_file(output_dir: Path, controller_name: str, contents: str): + output_dir.mkdir(parents=True, exist_ok=True) + output_file = output_dir / controller_name + output_file.write_text(contents, encoding="utf-8") - outpathname = f"{outPath}/{controllerName}" - if os.path.exists(outpathname): - with open(outpathname, "r") as f: - if f.read() == contents: - return - - # File either doesn't exist or has different contents - with open(outpathname, "w", newline="\n") as f: - f.write(contents) - - -def main(): - dirname, _ = os.path.split(os.path.abspath(__file__)) - - with open("wpilibj/src/generate/hids.json") as f: +def generate_hids(output_directory: Path, template_directory: Path, schema_file: Path): + with schema_file.open(encoding="utf-8") as f: controllers = json.load(f) # Java files + java_subdirectory = "main/java/edu/wpi/first/wpilibj2/command/button" env = Environment( - loader=FileSystemLoader( - f"{dirname}/src/generate/main/java/edu/wpi/first/wpilibj2/command/button" - ), + loader=FileSystemLoader(template_directory / java_subdirectory), autoescape=False, keep_trailing_newline=True, ) - rootPath = ( - f"{dirname}/src/generated/main/java/edu/wpi/first/wpilibj2/command/button" - ) + root_path = output_directory / java_subdirectory template = env.get_template("commandhid.java.jinja") for controller in controllers: - controllerName = os.path.basename( - f"Command{controller['ConsoleName']}Controller.java" - ) + controllerName = f"Command{controller['ConsoleName']}Controller.java" output = template.render(controller) - write_controller_file(rootPath, controllerName, output) + write_controller_file(root_path, controllerName, output) # C++ headers + hdr_subdirectory = "main/native/include/frc2/command/button" env = Environment( - loader=FileSystemLoader( - f"{dirname}/src/generate/main/native/include/frc2/command/button" - ), + loader=FileSystemLoader(template_directory / hdr_subdirectory), autoescape=False, keep_trailing_newline=True, ) - rootPath = f"{dirname}/src/generated/main/native/include/frc2/command/button" + root_path = output_directory / hdr_subdirectory template = env.get_template("commandhid.h.jinja") for controller in controllers: - controllerName = os.path.basename( - f"Command{controller['ConsoleName']}Controller.h" - ) + controllerName = f"Command{controller['ConsoleName']}Controller.h" output = template.render(controller) - write_controller_file(rootPath, controllerName, output) + write_controller_file(root_path, controllerName, output) # C++ files + cpp_subdirectory = "main/native/cpp/frc2/command/button" env = Environment( - loader=FileSystemLoader( - f"{dirname}/src/generate/main/native/cpp/frc2/command/button" - ), + loader=FileSystemLoader(template_directory / cpp_subdirectory), autoescape=False, ) - rootPath = f"{dirname}/src/generated/main/native/cpp/frc2/command/button" + root_path = output_directory / cpp_subdirectory template = env.get_template("commandhid.cpp.jinja") for controller in controllers: - controllerName = os.path.basename( - f"Command{controller['ConsoleName']}Controller.cpp" - ) + controllerName = f"Command{controller['ConsoleName']}Controller.cpp" output = template.render(controller) - write_controller_file(rootPath, controllerName, output) + write_controller_file(root_path, controllerName, output) + + +def main(argv): + script_path = Path(__file__).resolve() + dirname = script_path.parent + + parser = argparse.ArgumentParser() + parser.add_argument( + "--output_directory", + help="Optional. If set, will output the generated files to this directory, otherwise it will use a path relative to the script", + default=dirname / "src/generated", + type=Path, + ) + parser.add_argument( + "--template_root", + help="Optional. If set, will use this directory as the root for the jinja templates", + default=dirname / "src/generate", + type=Path, + ) + parser.add_argument( + "--schema_file", + help="Optional. If set, will use this file for the joystick schema", + default="wpilibj/src/generate/hids.json", + type=Path, + ) + args = parser.parse_args(argv) + + generate_hids(args.output_directory, args.template_root, args.schema_file) if __name__ == "__main__": - main() + main(sys.argv[1:]) diff --git a/wpilibc/generate_hids.py b/wpilibc/generate_hids.py index cfe54f7dee6..7c0689fc348 100755 --- a/wpilibc/generate_hids.py +++ b/wpilibc/generate_hids.py @@ -4,87 +4,105 @@ # Open Source Software; you can modify and/or share it under the terms of # the WPILib BSD license file in the root directory of this project. import json -import os +import sys +import argparse +from pathlib import Path from jinja2 import Environment, FileSystemLoader -def write_controller_file(outPath, controllerName, contents): - if not os.path.exists(outPath): - os.makedirs(outPath) +def write_controller_file(output_dir: Path, controller_name: str, contents: str): + output_dir.mkdir(parents=True, exist_ok=True) + output_file = output_dir / controller_name + output_file.write_text(contents, encoding="utf-8") - outpathname = f"{outPath}/{controllerName}" - if os.path.exists(outpathname): - with open(outpathname, "r") as f: - if f.read() == contents: - return - - # File either doesn't exist or has different contents - with open(outpathname, "w", newline="\n") as f: - f.write(contents) - - -def main(): - dirname, _ = os.path.split(os.path.abspath(__file__)) - - with open("wpilibj/src/generate/hids.json") as f: +def generate_hids(output_directory: Path, template_directory: Path, schema_file: Path): + with schema_file.open(encoding="utf-8") as f: controllers = json.load(f) # C++ headers + hdr_subdirectory = "main/native/include/frc" env = Environment( - loader=FileSystemLoader(f"{dirname}/src/generate/main/native/include/frc"), + loader=FileSystemLoader(template_directory / hdr_subdirectory), autoescape=False, keep_trailing_newline=True, ) - rootPath = f"{dirname}/src/generated/main/native/include/frc" + root_path = output_directory / hdr_subdirectory template = env.get_template("hid.h.jinja") for controller in controllers: - controllerName = os.path.basename(f"{controller['ConsoleName']}Controller.h") + controllerName = f"{controller['ConsoleName']}Controller.h" output = template.render(controller) - write_controller_file(rootPath, controllerName, output) + write_controller_file(root_path, controllerName, output) # C++ files + cpp_subdirectory = "main/native/cpp" env = Environment( - loader=FileSystemLoader(f"{dirname}/src/generate/main/native/cpp"), + loader=FileSystemLoader(template_directory / cpp_subdirectory), autoescape=False, ) - rootPath = f"{dirname}/src/generated/main/native/cpp" + root_path = output_directory / cpp_subdirectory template = env.get_template("hid.cpp.jinja") for controller in controllers: - controllerName = os.path.basename(f"{controller['ConsoleName']}Controller.cpp") + controllerName = f"{controller['ConsoleName']}Controller.cpp" output = template.render(controller) - write_controller_file(rootPath, controllerName, output) + write_controller_file(root_path, controllerName, output) # C++ simulation headers + sim_hdr_subdirectory = "main/native/include/frc/simulation" env = Environment( - loader=FileSystemLoader( - f"{dirname}/src/generate/main/native/include/frc/simulation" - ), + loader=FileSystemLoader(template_directory / sim_hdr_subdirectory), autoescape=False, keep_trailing_newline=True, ) - rootPath = f"{dirname}/src/generated/main/native/include/frc/simulation" + root_path = output_directory / sim_hdr_subdirectory template = env.get_template("hidsim.h.jinja") for controller in controllers: - controllerName = os.path.basename(f"{controller['ConsoleName']}ControllerSim.h") + controllerName = f"{controller['ConsoleName']}ControllerSim.h" output = template.render(controller) - write_controller_file(rootPath, controllerName, output) + write_controller_file(root_path, controllerName, output) # C++ simulation files + sim_cpp_subdirectory = "main/native/cpp/simulation" env = Environment( - loader=FileSystemLoader(f"{dirname}/src/generate/main/native/cpp/simulation"), + loader=FileSystemLoader(template_directory / sim_cpp_subdirectory), autoescape=False, ) - rootPath = f"{dirname}/src/generated/main/native/cpp/simulation" + root_path = output_directory / sim_cpp_subdirectory template = env.get_template("hidsim.cpp.jinja") for controller in controllers: - controllerName = os.path.basename( - f"{controller['ConsoleName']}ControllerSim.cpp" - ) + controllerName = f"{controller['ConsoleName']}ControllerSim.cpp" output = template.render(controller) - write_controller_file(rootPath, controllerName, output) + write_controller_file(root_path, controllerName, output) + + +def main(argv): + script_path = Path(__file__).resolve() + dirname = script_path.parent + + parser = argparse.ArgumentParser() + parser.add_argument( + "--output_directory", + help="Optional. If set, will output the generated files to this directory, otherwise it will use a path relative to the script", + default=dirname / "src/generated", + type=Path, + ) + parser.add_argument( + "--template_root", + help="Optional. If set, will use this directory as the root for the jinja templates", + default=dirname / "src/generate", + type=Path, + ) + parser.add_argument( + "--schema_file", + help="Optional. If set, will use this file for the joystick schema", + default="wpilibj/src/generate/hids.json", + type=Path, + ) + args = parser.parse_args(argv) + + generate_hids(args.output_directory, args.template_root, args.schema_file) if __name__ == "__main__": - main() + main(sys.argv[1:]) diff --git a/wpilibj/generate_hids.py b/wpilibj/generate_hids.py index 98f97dd6c0e..56d47af2668 100755 --- a/wpilibj/generate_hids.py +++ b/wpilibj/generate_hids.py @@ -4,56 +4,66 @@ # Open Source Software; you can modify and/or share it under the terms of # the WPILib BSD license file in the root directory of this project. import json -import os +import sys +import argparse +from pathlib import Path from jinja2 import Environment, FileSystemLoader -def write_controller_file(outPath, controllerName, contents): - if not os.path.exists(outPath): - os.makedirs(outPath) +def write_controller_file(output_dir: Path, controller_name: str, contents: str): + output_dir.mkdir(parents=True, exist_ok=True) + output_file = output_dir / controller_name + output_file.write_text(contents, encoding="utf-8") - outpathname = f"{outPath}/{controllerName}" - if os.path.exists(outpathname): - with open(outpathname, "r") as f: - if f.read() == contents: - return - - # File either doesn't exist or has different contents - with open(outpathname, "w", newline="\n") as f: - f.write(contents) - - -def main(): - dirname, _ = os.path.split(os.path.abspath(__file__)) - - with open(f"{dirname}/src/generate/hids.json") as f: +def generate_hids(output_directory: Path, template_directory: Path): + with (template_directory / "hids.json").open(encoding="utf-8") as f: controllers = json.load(f) # Java files env = Environment( - loader=FileSystemLoader(f"{dirname}/src/generate/"), + loader=FileSystemLoader(template_directory), autoescape=False, keep_trailing_newline=True, ) - rootPath = f"{dirname}/src/generated/main/java/edu/wpi/first/wpilibj" + rootPath = output_directory / "main/java/edu/wpi/first/wpilibj" template = env.get_template("hid.java.jinja") for controller in controllers: - controllerName = os.path.basename(f"{controller['ConsoleName']}Controller.java") + controllerName = f"{controller['ConsoleName']}Controller.java" output = template.render(controller) write_controller_file(rootPath, controllerName, output) # Java simulation files - rootPath = f"{dirname}/src/generated/main/java/edu/wpi/first/wpilibj/simulation" + rootPath = output_directory / "main/java/edu/wpi/first/wpilibj/simulation" template = env.get_template("hidsim.java.jinja") for controller in controllers: - controllerName = os.path.basename( - f"{controller['ConsoleName']}ControllerSim.java" - ) + controllerName = f"{controller['ConsoleName']}ControllerSim.java" output = template.render(controller) write_controller_file(rootPath, controllerName, output) +def main(argv): + script_path = Path(__file__).resolve() + dirname = script_path.parent + + parser = argparse.ArgumentParser() + parser.add_argument( + "--output_directory", + help="Optional. If set, will output the generated files to this directory, otherwise it will use a path relative to the script", + default=dirname / "src/generated", + type=Path, + ) + parser.add_argument( + "--template_root", + help="Optional. If set, will use this directory as the root for the jinja templates", + default=dirname / "src/generate", + type=Path, + ) + args = parser.parse_args(argv) + + generate_hids(args.output_directory, args.template_root) + + if __name__ == "__main__": - main() + main(sys.argv[1:]) diff --git a/wpimath/generate_numbers.py b/wpimath/generate_numbers.py index 2aeb45a56d8..ec55ff63cc5 100755 --- a/wpimath/generate_numbers.py +++ b/wpimath/generate_numbers.py @@ -4,50 +4,61 @@ # Open Source Software; you can modify and/or share it under the terms of # the WPILib BSD license file in the root directory of this project. -import os import sys +import argparse from jinja2 import Environment, FileSystemLoader +from pathlib import Path -def output(outPath, outfn, contents): - if not os.path.exists(outPath): - os.makedirs(outPath) +def output(output_dir: Path, outfn: str, contents: str): + output_dir.mkdir(parents=True, exist_ok=True) + output_file = output_dir / outfn + output_file.write_text(contents, encoding="utf-8") - outpathname = f"{outPath}/{outfn}" - if os.path.exists(outpathname): - with open(outpathname, "r") as f: - if f.read() == contents: - return - - # File either doesn't exist or has different contents - with open(outpathname, "w", newline="\n") as f: - f.write(contents) - - -def main(): +def generate_numbers(output_directory: Path, template_root: Path): MAX_NUM = 20 - dirname, _ = os.path.split(os.path.abspath(__file__)) - env = Environment( - loader=FileSystemLoader(f"{dirname}/src/generate/main/java"), + loader=FileSystemLoader(template_root / "main/java"), autoescape=False, keep_trailing_newline=True, ) template = env.get_template("GenericNumber.java.jinja") - rootPath = f"{dirname}/src/generated/main/java/edu/wpi/first/math/numbers" + rootPath = output_directory / "main/java/edu/wpi/first/math/numbers" for i in range(MAX_NUM + 1): contents = template.render(num=i) output(rootPath, f"N{i}.java", contents) template = env.get_template("Nat.java.jinja") - rootPath = f"{dirname}/src/generated/main/java/edu/wpi/first/math" + rootPath = output_directory / "main/java/edu/wpi/first/math" contents = template.render(nums=range(MAX_NUM + 1)) output(rootPath, "Nat.java", contents) +def main(argv): + script_path = Path(__file__).resolve() + dirname = script_path.parent + + parser = argparse.ArgumentParser() + parser.add_argument( + "--output_directory", + help="Optional. If set, will output the generated files to this directory, otherwise it will use a path relative to the script", + default=dirname / "src/generated", + type=Path, + ) + parser.add_argument( + "--template_root", + help="Optional. If set, will use this directory as the root for the jinja templates", + default=dirname / "src/generate", + type=Path, + ) + args = parser.parse_args(argv) + + generate_numbers(args.output_directory, args.template_root) + + if __name__ == "__main__": - main() + main(sys.argv[1:]) diff --git a/wpimath/generate_quickbuf.py b/wpimath/generate_quickbuf.py index 506c5bc92d2..9faef53d08f 100755 --- a/wpimath/generate_quickbuf.py +++ b/wpimath/generate_quickbuf.py @@ -3,31 +3,73 @@ # Copyright (c) FIRST and other WPILib contributors. # Open Source Software; you can modify and/or share it under the terms of # the WPILib BSD license file in the root directory of this project. -import os.path import subprocess import sys -from glob import glob +import argparse +import sys +from pathlib import Path -if __name__ == "__main__": - proto_files = glob("wpimath/src/main/proto/*.proto") + +def generate_quickbuf( + protoc, quickbuf_plugin: Path, output_directory: Path, proto_dir: Path +): + proto_files = proto_dir.glob("*.proto") for path in proto_files: - absolute_filename = os.path.abspath(path) - absolute_dir, filename = os.path.split(absolute_filename) + absolute_filename = path.absolute() subprocess.run( [ - sys.argv[1], - f"--plugin=protoc-gen-quickbuf={sys.argv[2]}", - f"--quickbuf_out=gen_descriptors=true:{os.path.abspath('./wpimath/src/generated/main/java')}", - f"-I{absolute_dir}", + protoc, + f"--plugin=protoc-gen-quickbuf={quickbuf_plugin}", + f"--quickbuf_out=gen_descriptors=true:{output_directory.absolute()}", + f"-I{absolute_filename.parent}", absolute_filename, ] ) - java_files = glob("wpimath/src/generated/main/java/edu/wpi/first/math/proto/*.java") + java_files = (output_directory / "edu/wpi/first/math/proto").glob("*.java") for java_file in java_files: - with open(java_file) as file: - content = file.read() - with open(java_file, "tw") as file: - file.write( - "// Copyright (c) FIRST and other WPILib contributors.\n// Open Source Software; you can modify and/or share it under the terms of\n// the WPILib BSD license file in the root directory of this project.\n" - + content - ) + with (java_file).open(encoding="utf-8") as f: + content = f.read() + + java_file.write_text( + "// Copyright (c) FIRST and other WPILib contributors.\n// Open Source Software; you can modify and/or share it under the terms of\n// the WPILib BSD license file in the root directory of this project.\n" + + content, + encoding="utf-8", + ) + + +def main(argv): + script_path = Path(__file__).resolve() + dirname = script_path.parent + + parser = argparse.ArgumentParser() + parser.add_argument( + "--protoc", + help="Protoc executable command", + default="protoc", + ) + parser.add_argument( + "--quickbuf_plugin", + help="Path to the quickbuf protoc plugin", + required=True, + ) + parser.add_argument( + "--output_directory", + help="Optional. If set, will output the generated files to this directory, otherwise it will use a path relative to the script", + default=dirname / "src/generated/main/java", + type=Path, + ) + parser.add_argument( + "--proto_directory", + help="Optional. If set, will use this directory to glob for protobuf files", + default=dirname / "src/main/proto", + type=Path, + ) + args = parser.parse_args(argv) + + generate_quickbuf( + args.protoc, args.quickbuf_plugin, args.output_directory, args.proto_directory + ) + + +if __name__ == "__main__": + main(sys.argv[1:])