code-server-wpilib/project-creator.py

220 lines
8.4 KiB
Python

#!/usr/bin/env python
import os
import json
import platform
import sys
import shutil
import glob
import re
def padded_print(to_print, end, minimum_length):
pad_length = minimum_length - len(to_print) + 2
print(to_print + ' ' * pad_length, end=end)
def show_options(option_list, columns):
longest_option = max([len(option) + 4 for option in option_list])
for x in range(0, len(option_list), columns):
for y in range(x, min(len(option_list), x + columns), 1):
padded_print("[{opnum}] {option}".format(opnum=y, option=option_list[y]),'', longest_option)
print("")
def get_selection(option_list, columns, prompt):
selection = ""
while True:
show_options(option_list, columns)
selection = input(prompt)
if (selection == ""):
print("You need to actually specify a number")
else:
try:
int_form = int(selection)
if (int_form >= 0 and int_form < len(option_list)):
return option_list[int_form]
else:
print("You need to specify your selection as one of the numbers listed")
except:
print("You need to specify your selection as one of the numbers listed")
### BEGIN Ask the user where there WPILIB folder is
if (platform.system() == "Windows"):
wpilib_folder = input("Path to WPILIB Folder (or leave blank for C:\\Users\\Public\\wpilib): ")
else:
wpilib_folder = input("Path to WPILIB Folder (or leave blank for ~/wpilib): ")
print() # For spacing
wpilib_folder = wpilib_folder if wpilib_folder != "" else "C:\\Users\\Public\\wpilib" if platform.system() == "Windows" else os.path.expanduser("~/wpilib")
### END Ask the user where there WPILIB folder is
### BEGIN Determine the most current WPILIB folder field that is available
year = "0"
for folder in os.listdir(wpilib_folder):
joined = os.path.join(wpilib_folder, folder)
if (os.path.isdir(joined)):
try:
if (max(int(year), int(folder)) != int(year)):
year = folder
except:
pass # Don't care about this, just means it's not a year folder, and I don't want the script to die
if (year == "0"):
print("It doesn't look like WPILIB is installed in the appropriate location")
sys.exit(1)
wpilib_folder = os.path.join(wpilib_folder, year)
### END Determine the most current WPILIB folder field that is available
### BEGIN Determine project language and type
project_language = get_selection(["Java", "C++"], 1, "Select your language: ").lower().replace('+', 'p')
print("") # For spacing
project_type = get_selection(["Template", "Example"], 1, "Select your project type: ").lower() + "s"
print("") # For spacing
### END Determine project language and type
### BEGIN Set common directories and variables for source project files
project_app = os.path.join(wpilib_folder, "utility", "resources", "app")
project_resources = os.path.join(project_app, "resources")
gradle_shared = os.path.join(project_resources, "gradle", "shared")
vendordeps = os.path.join(project_resources, "vendordeps")
language_source = os.path.join(project_resources, project_language, "src", project_type)
# Is this the right way to get this value? From the electron apps package.json?
with open(os.path.join(project_app, "package.json")) as json_file:
wpilib_version = json.loads(json_file.read())["version"]
with open(os.path.join(language_source, project_type + ".json")) as json_file:
json_data = json.loads(json_file.read())
### END Set common directories and variables for source project files
### BEGIN Identify which example/template to use and set relevant variables
all_named_options = [element["name"] for element in json_data]
name_selected = get_selection(all_named_options, 3, "Select from the above to build your project from: ")
print("") # For spacing
project_json = [element for element in json_data if element["name"] == name_selected][0]
project_main_folder = os.path.join(language_source, project_json["foldername"])
project_gradle_folder = os.path.join(project_resources, "gradle", project_json["gradlebase"])
### END Identify which example/template to use and set relevant variables
### BEGIN Determine user project directory and name and set relevant variables
project_directory = os.path.expanduser(input("Enter the project directory: "))
project_name = input("Enter the name of your project folder (or leave blank to put project in the project directory): ")
if project_name == "":
new_project_path = project_directory
else:
new_project_path = os.path.join(project_directory, project_name)
new_project_path_src = os.path.join(new_project_path, "src", "main")
### END Determine user project directory and name and set relevant variables
### BEGIN Generate project directories and fill them with the appropriate files
os.makedirs(os.path.join(new_project_path_src, "deploy"), exist_ok=True)
if project_language == "java":
os.makedirs(os.path.join(new_project_path_src, "java", "frc", "robot"), exist_ok=True)
if project_language == "java":
shutil.copytree(project_main_folder, os.path.join(new_project_path_src, "java", "frc", "robot"), dirs_exist_ok=True)
else:
shutil.copytree(project_main_folder, new_project_path_src, dirs_exist_ok=True)
shutil.copytree(project_gradle_folder, new_project_path, dirs_exist_ok=True)
shutil.copytree(gradle_shared, new_project_path, dirs_exist_ok=True)
shutil.copytree(vendordeps, os.path.join(new_project_path, "vendordeps"), dirs_exist_ok=True)
### END Generate project directories and fill them with the appropriate files
### BEGIN Do find and replace work for java and cpp projects and all projects team number config
### TODO Opening and closing build.gradle is unnecessarily repetitive,
### Might be better to lose the with calls and manually open/close the files?
with open(os.path.join(new_project_path, "build.gradle"), "r") as gradle_build_file:
gradle_build_content = gradle_build_file.read()
gradle_build_content = gradle_build_content.replace("###GRADLERIOREPLACE###", wpilib_version)
with open(os.path.join(new_project_path, "build.gradle"), "w") as gradle_build_file:
gradle_build_file.write(gradle_build_content)
if project_language == "java":
### BEGIN Gradle build file changes for Java
with open(os.path.join(new_project_path, "build.gradle"), "r") as gradle_build_file:
gradle_build_content = gradle_build_file.read()
gradle_build_content = gradle_build_content.replace("###ROBOTCLASSREPLACE###", "frc.robot.Main")
with open(os.path.join(new_project_path, "build.gradle"), "w") as gradle_build_file:
gradle_build_file.write(gradle_build_content)
### END Gradle build file changes for Java
### BEGIN *.java find and replace template/example package in all files
# This regular expression trickery needs to be HEAVILY tested.
# I don't think it's going to hold up everywhere...
# This is NOT a good regex for general use, but because we aren't
# validating package names, just trying to nuke the generic
# template packages, it's probably fine.
# Note that because the last [option]* list doesn't include period, this
# SHOULD ignore any continuing package structures after what we want to replace,
# this is necessary as to not break command based templates
template_regex = re.compile(r'edu\.wpi\.first\.wpilibj\.(templates|examples)\.[a-zA-Z0-9]*')
for file in glob.glob(os.path.join(new_project_path_src, "**", "*.java"), recursive=True):
with open(file, "r") as java_file:
java_file_content = java_file.read()
java_file_content = template_regex.sub('frc.robot', java_file_content)
with open(file, "w") as java_file:
java_file.write(java_file_content)
### END *.java find and replace template package in all files
with open(os.path.join(new_project_path, ".wpilib", "wpilib_preferences.json"), "r") as wpilib_pref_file:
wpilib_pref_json = json.loads(wpilib_pref_file.read())
while wpilib_pref_json["teamNumber"] == -1:
new_number = input("Enter your team number: ")
try:
wpilib_pref_json["teamNumber"] = int(new_number)
except:
print("Not a valid team number")
wpilib_pref_json["teamNumber"] = -1
with open(os.path.join(new_project_path, ".wpilib", "wpilib_preferences.json"), "w") as wpilib_pref_file:
wpilib_pref_file.write(json.dumps(wpilib_pref_json, indent=4))
### END Do find and replace work for java projects and all projects team number config