diff --git a/.gitignore b/.gitignore index 6ad18a8..a803a46 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build buildlogs* -__pycache__ \ No newline at end of file +__pycache__ +MaximSDK/Template/.vscode/readme.md \ No newline at end of file diff --git a/MaximSDK/Inject/.vscode/flash.gdb b/MaximSDK/Inject/.vscode/flash.gdb index 2d86f86..fc627ae 100644 --- a/MaximSDK/Inject/.vscode/flash.gdb +++ b/MaximSDK/Inject/.vscode/flash.gdb @@ -5,3 +5,11 @@ define flash_m4 compare-sections monitor reset halt end + +define flash_m4_run + set architecture armv7e-m + target remote | openocd -c "gdb_port pipe;log_output flash.log" -s $arg0/scripts -f interface/$arg1 -f target/$arg2 -c "init; reset halt" + load + compare-sections + monitor resume +end \ No newline at end of file diff --git a/MaximSDK/Inject/.vscode/tasks.json b/MaximSDK/Inject/.vscode/tasks.json index 5eef671..3951f35 100644 --- a/MaximSDK/Inject/.vscode/tasks.json +++ b/MaximSDK/Inject/.vscode/tasks.json @@ -26,25 +26,56 @@ "label": "flash", "type": "shell", "command": "arm-none-eabi-gdb", + "args": [ + "--cd=\"${workspaceFolder}\"", + "--se=\"build/${config:program_file}\"", + "--symbols=build/${config:symbol_file}", + "-x=\"${workspaceFolder}/.vscode/flash.gdb\"", + "--ex=\"flash_m4 ${config:OCD_path} ${config:M4_OCD_interface_file} ${config:M4_OCD_target_file}\"", + "--batch" + ], + "group": "build", + "problemMatcher": [], + "dependsOn":["build"] + }, + { + "label": "flash & run", + "type": "shell", + "command": "arm-none-eabi-gdb", "args": [ "--cd=\"${workspaceFolder}\"", "--se=\"build/${config:program_file}\"", "--symbols=build/${config:symbol_file}", "-x=\"${workspaceFolder}/.vscode/flash.gdb\"", - "--ex=\"flash_m4 ${config:OCD_path} ${config:M4_OCD_interface_file} ${config:M4_OCD_target_file}\"", + "--ex=\"flash_m4_run ${config:OCD_path} ${config:M4_OCD_interface_file} ${config:M4_OCD_target_file}\"", "--batch" ], "group": "build", "problemMatcher": [], "dependsOn":["build"] }, + { + "label": "erase flash", + "type": "shell", + "command": "openocd", + "args": [ + "-s", "${config:OCD_path}/scripts", + "-f", "interface/${config:M4_OCD_interface_file}", + "-f", "target/${config:M4_OCD_target_file}", + "-c", "\"init; reset halt; max32xxx mass_erase 0;\"", + "-c", "exit" + ], + "group":"build", + "problemMatcher": [], + "dependsOn":[] + }, { "label": "openocd (m4)", "type": "shell", "command": "openocd", "args": [ "-s", - "${config:MAXIM_PATH}/Tools/OpenOCD/scripts", + "${config:OCD_path}/scripts", "-f", "interface/${config:M4_OCD_interface_file}", "-f", diff --git a/MaximSDK/New_Project/.vscode/flash.gdb b/MaximSDK/New_Project/.vscode/flash.gdb index 2d86f86..fc627ae 100644 --- a/MaximSDK/New_Project/.vscode/flash.gdb +++ b/MaximSDK/New_Project/.vscode/flash.gdb @@ -5,3 +5,11 @@ define flash_m4 compare-sections monitor reset halt end + +define flash_m4_run + set architecture armv7e-m + target remote | openocd -c "gdb_port pipe;log_output flash.log" -s $arg0/scripts -f interface/$arg1 -f target/$arg2 -c "init; reset halt" + load + compare-sections + monitor resume +end \ No newline at end of file diff --git a/MaximSDK/New_Project/.vscode/tasks.json b/MaximSDK/New_Project/.vscode/tasks.json index 5eef671..3951f35 100644 --- a/MaximSDK/New_Project/.vscode/tasks.json +++ b/MaximSDK/New_Project/.vscode/tasks.json @@ -26,25 +26,56 @@ "label": "flash", "type": "shell", "command": "arm-none-eabi-gdb", + "args": [ + "--cd=\"${workspaceFolder}\"", + "--se=\"build/${config:program_file}\"", + "--symbols=build/${config:symbol_file}", + "-x=\"${workspaceFolder}/.vscode/flash.gdb\"", + "--ex=\"flash_m4 ${config:OCD_path} ${config:M4_OCD_interface_file} ${config:M4_OCD_target_file}\"", + "--batch" + ], + "group": "build", + "problemMatcher": [], + "dependsOn":["build"] + }, + { + "label": "flash & run", + "type": "shell", + "command": "arm-none-eabi-gdb", "args": [ "--cd=\"${workspaceFolder}\"", "--se=\"build/${config:program_file}\"", "--symbols=build/${config:symbol_file}", "-x=\"${workspaceFolder}/.vscode/flash.gdb\"", - "--ex=\"flash_m4 ${config:OCD_path} ${config:M4_OCD_interface_file} ${config:M4_OCD_target_file}\"", + "--ex=\"flash_m4_run ${config:OCD_path} ${config:M4_OCD_interface_file} ${config:M4_OCD_target_file}\"", "--batch" ], "group": "build", "problemMatcher": [], "dependsOn":["build"] }, + { + "label": "erase flash", + "type": "shell", + "command": "openocd", + "args": [ + "-s", "${config:OCD_path}/scripts", + "-f", "interface/${config:M4_OCD_interface_file}", + "-f", "target/${config:M4_OCD_target_file}", + "-c", "\"init; reset halt; max32xxx mass_erase 0;\"", + "-c", "exit" + ], + "group":"build", + "problemMatcher": [], + "dependsOn":[] + }, { "label": "openocd (m4)", "type": "shell", "command": "openocd", "args": [ "-s", - "${config:MAXIM_PATH}/Tools/OpenOCD/scripts", + "${config:OCD_path}/scripts", "-f", "interface/${config:M4_OCD_interface_file}", "-f", diff --git a/MaximSDK/Template/.vscode/flash.gdb b/MaximSDK/Template/.vscode/flash.gdb index 2d86f86..fc627ae 100644 --- a/MaximSDK/Template/.vscode/flash.gdb +++ b/MaximSDK/Template/.vscode/flash.gdb @@ -5,3 +5,11 @@ define flash_m4 compare-sections monitor reset halt end + +define flash_m4_run + set architecture armv7e-m + target remote | openocd -c "gdb_port pipe;log_output flash.log" -s $arg0/scripts -f interface/$arg1 -f target/$arg2 -c "init; reset halt" + load + compare-sections + monitor resume +end \ No newline at end of file diff --git a/MaximSDK/Template/.vscode/tasks.json b/MaximSDK/Template/.vscode/tasks.json index 5eef671..3951f35 100644 --- a/MaximSDK/Template/.vscode/tasks.json +++ b/MaximSDK/Template/.vscode/tasks.json @@ -26,25 +26,56 @@ "label": "flash", "type": "shell", "command": "arm-none-eabi-gdb", + "args": [ + "--cd=\"${workspaceFolder}\"", + "--se=\"build/${config:program_file}\"", + "--symbols=build/${config:symbol_file}", + "-x=\"${workspaceFolder}/.vscode/flash.gdb\"", + "--ex=\"flash_m4 ${config:OCD_path} ${config:M4_OCD_interface_file} ${config:M4_OCD_target_file}\"", + "--batch" + ], + "group": "build", + "problemMatcher": [], + "dependsOn":["build"] + }, + { + "label": "flash & run", + "type": "shell", + "command": "arm-none-eabi-gdb", "args": [ "--cd=\"${workspaceFolder}\"", "--se=\"build/${config:program_file}\"", "--symbols=build/${config:symbol_file}", "-x=\"${workspaceFolder}/.vscode/flash.gdb\"", - "--ex=\"flash_m4 ${config:OCD_path} ${config:M4_OCD_interface_file} ${config:M4_OCD_target_file}\"", + "--ex=\"flash_m4_run ${config:OCD_path} ${config:M4_OCD_interface_file} ${config:M4_OCD_target_file}\"", "--batch" ], "group": "build", "problemMatcher": [], "dependsOn":["build"] }, + { + "label": "erase flash", + "type": "shell", + "command": "openocd", + "args": [ + "-s", "${config:OCD_path}/scripts", + "-f", "interface/${config:M4_OCD_interface_file}", + "-f", "target/${config:M4_OCD_target_file}", + "-c", "\"init; reset halt; max32xxx mass_erase 0;\"", + "-c", "exit" + ], + "group":"build", + "problemMatcher": [], + "dependsOn":[] + }, { "label": "openocd (m4)", "type": "shell", "command": "openocd", "args": [ "-s", - "${config:MAXIM_PATH}/Tools/OpenOCD/scripts", + "${config:OCD_path}/scripts", "-f", "interface/${config:M4_OCD_interface_file}", "-f", diff --git a/generate.py b/generate.py index 5a1db70..57c3e62 100644 --- a/generate.py +++ b/generate.py @@ -34,19 +34,22 @@ import os import shutil -import argparse -import platform -from subprocess import run -from utils import parse_json -from sdk import * +import stat +from utils import * +from pathlib import Path + +# Get location of this file. +# Need to use this so that template look-ups are decoupled from the caller's working directory +here = Path(__file__).parent # Load default values for template from master "inject" folder so that we don't have to maintain multiple copies of the settings -defaults = parse_json("MaximSDK/Inject/.vscode/settings.json") +defaults = parse_json(here.joinpath("MaximSDK/Inject/.vscode/settings.json")) whitelist = [ "MAX32650", "MAX32655", "MAX32660", + "MAX32662", "MAX32665", "MAX32670", "MAX32672", @@ -61,6 +64,7 @@ def create_project( out_path: str, target: str, board: str, + overwrite = False, program_file: str = defaults["PROGRAM_FILE"], symbol_file: str = defaults["SYMBOL_FILE"], m4_ocd_interface_file: str = defaults["M4_OCD_INTERFACE_FILE"], @@ -75,176 +79,122 @@ def create_project( ocd_path: str = defaults["OCD_PATH"], arm_gcc_path: str = defaults["ARM_GCC_PATH"], xpack_gcc_path: str = defaults["XPACK_GCC_PATH"], - make_path: str = defaults["MAKE_PATH"] + make_path: str = defaults["MAKE_PATH"], ): + """ + Generates Visual Studio Code project files from the VSCode-Maxim project. + """ + + out_path = Path(out_path) + + template_dir = here.joinpath("MaximSDK/Template").resolve() + # Where to find the VS Code template directory relative to this script - template_dir = os.path.abspath(os.path.join("MaximSDK", "Template")) # Where to find the VS Code template directory relative to this script - template_prefix = "template" # Filenames beginning with this will have substitution + template_prefix = "template" + # Filenames beginning with this will have substitution - if not os.path.exists(template_dir): - raise(Exception(f"Failed to find project template folder '{template_dir}'. Check the location and existence of these files.")) + if not template_dir.exists(): + raise Exception(f"Failed to find project template folder '{template_dir}'.") tmp = [] # Work-horse list, linter be nice + # Parse compiler definitions... if defines != []: - # Parse defines... - # --- tmp = defines tmp = list(map(lambda s: s.strip("-D"), tmp)) # VS Code doesn't want -D - tmp = list(map("\"{0}\"".format, tmp)) # Surround with quotes + tmp = list(map(lambda s: f"\"{s}\"", tmp)) # Surround with quotes defines_parsed = ",\n ".join(tmp) # csv, newline, and tab (w/ spaces) alignment - # --- else: - defines_parsed = ",\n ".join(defines) + defines_parsed = "" # Parse include paths... tmp = i_paths - tmp = list(map("\"{0}\"".format, tmp)) # Surround with quotes + tmp = list(map(lambda s: f"\"{s}\"", tmp)) # Surround with quotes i_paths_parsed = ",\n ".join(tmp).replace(target, "${config:target}").replace("\\", "/") - # Parse browse paths... tmp = v_paths - tmp = list(map("\"{0}\"".format, tmp)) # Surround with quotes - v_paths_parsed = ",\n ".join(tmp).replace(target, "${config:target}").replace("\\", "/") # csv, newline, and tab alignment + tmp = list(map(lambda s: f"\"{s}\"", tmp)) # Surround with quotes + v_paths_parsed = ",\n ".join(tmp).replace(target, "${config:target}").replace("\\", "/") + updated = [] # Create template... for directory, _, files in sorted(os.walk(template_dir)): # ^ For each directory in the directory tree rooted at top (including top itself, # but excluding '.' and '..'), yields a 3-tuple (dirpath, dirnames, filenames) # Get current directory relative to root - rel_dir = os.path.relpath(directory, template_dir) + rel_dir = Path(directory).relative_to(Path(template_dir)) # Figure out whether we're in a subfolder of the template directory, # and form output path accordingly. - if rel_dir != '.': + if rel_dir != Path('.'): # We're in a sub-folder. Replicate this folder in the output directory - out_path = os.path.join(out_path, rel_dir) + out_path = Path(out_path).joinpath(rel_dir) os.makedirs(out_path, exist_ok=True) else: # We're in the root template folder, no need to create a directory. pass + # Any files to copy? for file in sorted(files): if file.startswith(template_prefix): # There is a template file to copy. Perform string substitution in output file. - out_loc = os.path.join(out_path, file[len(template_prefix):]) - with open(os.path.join(directory, file)) as in_file, \ - open(out_loc, "w+") as out_file: - for line in in_file.readlines(): - out_file.write( - line.replace("##__TARGET__##", target.upper()). - replace("##__BOARD__##", board). - replace("##__PROGRAM_FILE__##", program_file). - replace("##__SYMBOL_FILE__##", symbol_file). - replace("##__M4_OCD_INTERFACE_FILE__##", m4_ocd_interface_file). - replace("##__M4_OCD_TARGET_FILE__##", m4_ocd_target_file). - replace("##__RV_OCD_INTERFACE_FILE__##", rv_ocd_interface_file). - replace("##__RV_OCD_TARGET_FILE__##", rv_ocd_target_file). - replace("\"##__I_PATHS__##\"", i_paths_parsed). # Next 3 are surrounded in quotes in the template because of the linter - replace("\"##__DEFINES__##\"", defines_parsed). - replace("\"##__V_PATHS__##\"", v_paths_parsed). - replace("##__V_ARM_GCC__##", v_arm_gcc). - replace("##__V_XPACK_GCC__##", v_xpack_gcc). - replace("##__OCD_PATH__##", ocd_path). - replace("##__ARM_GCC_PATH__##", arm_gcc_path). - replace("##__XPACK_GCC_PATH__##", xpack_gcc_path). - replace("##__MAKE_PATH__##", make_path) - ) - - os.chmod(out_loc, 0o764) - # print(f"Wrote {os.path.basename(out_loc)}") # Uncomment to debug + out_file = Path(out_path).joinpath(file[len(template_prefix):]) # Remove prefix + template = Path(directory).joinpath(file) + + content = None + with open(template, 'r', encoding="UTF-8") as f: + content = f.read() + content = content.replace("##__TARGET__##", target.upper()). \ + replace("##__BOARD__##", board). \ + replace("##__PROGRAM_FILE__##", program_file). \ + replace("##__SYMBOL_FILE__##", symbol_file). \ + replace("##__M4_OCD_INTERFACE_FILE__##", m4_ocd_interface_file). \ + replace("##__M4_OCD_TARGET_FILE__##", m4_ocd_target_file). \ + replace("##__RV_OCD_INTERFACE_FILE__##", rv_ocd_interface_file). \ + replace("##__RV_OCD_TARGET_FILE__##", rv_ocd_target_file). \ + replace("\"##__I_PATHS__##\"", i_paths_parsed). \ + replace("\"##__DEFINES__##\"", defines_parsed). \ + replace("\"##__V_PATHS__##\"", v_paths_parsed). \ + replace("##__V_ARM_GCC__##", v_arm_gcc). \ + replace("##__V_XPACK_GCC__##", v_xpack_gcc). \ + replace("##__OCD_PATH__##", ocd_path). \ + replace("##__ARM_GCC_PATH__##", arm_gcc_path). \ + replace("##__XPACK_GCC_PATH__##", xpack_gcc_path). \ + replace("##__MAKE_PATH__##", make_path) + + write = True + if out_file.exists(): + if not overwrite or compare_content(content, out_file): + write = False + + if write: + with open(out_file, "w+", encoding="UTF-8") as f: + f.write(content) + os.chmod(out_file, stat.S_IRWXU | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH) + if out_file not in updated: + updated.append(out_file) + + # print(f"Wrote {os.path.basename(out_loc)}") # Uncomment to debug else: # There is a non-template file to copy - shutil.copy(os.path.join(directory, file), out_path) - os.chmod(out_path, 0o764) - #print(f"Wrote {os.path.basename(file)}") # Uncomment to debug - -@time_me -def populate_maximsdk(target_os, maxim_path, overwrite=True): - # Copy readme into template directory - shutil.copy("readme.md", str(Path("MaximSDK/Template/.vscode/"))) - - print(f"Scanning {maxim_path}...") - - # Check for cache file - cachefile = Path(maxim_path).joinpath(".cache").joinpath("msdk") - - if (cachefile.exists()): - print("Loading from cache file...") - sdk = SDK.thaw(cachefile) - else: - sdk = SDK.from_search(maxim_path) - sdk.freeze(cachefile) - - count = 0 - for example in sdk.examples: - #print(f"Generating VSCode-Maxim project for {example.path} ...") - - # Common options - _path = example.path - _target = example.target.name - _board = example.target.boards[0].name # Default to first board in list - for b in example.target.boards: - # Use EvKit_V1 if possible. - # Some boards modify EvKit_V1 (ex: QN_EvKit_V1) - if "EvKit_V1" in b.name: _board = b.name - - _program_file="${config:project_name}-combined.elf" if example.riscv else defaults["PROGRAM_FILE"] - _symbol_file="${config:project_name}.elf" if example.riscv else defaults["SYMBOL_FILE"] - _ipaths = [] + defaults["C_CPP.DEFAULT.INCLUDEPATH"] - _vpaths = [] + defaults["C_CPP.DEFAULT.BROWSE.PATH"] - - # Add include and browse paths for the libraries that this example uses - for l in example.libs: - for ipath in l.get_ipaths(example.target.name): - _ipaths.append( - str(ipath.as_posix()). - replace(sdk.maxim_path.as_posix(), "${config:MAXIM_PATH}"). - replace(example.target.name, "${config:target}") - ) - - for vpath in l.get_vpaths(example.target.name): - _vpaths.append( - str(vpath.as_posix()). - replace(sdk.maxim_path.as_posix(), "${config:MAXIM_PATH}"). - replace(example.target.name, "${config:target}") - ) - - # Linux OpenOCD .cfg files are case senstive. Need to hard-code a lowercase value. - _m4_ocd_target_file = f"{str.lower(example.target.name)}.cfg" if target_os == "Linux" else defaults["M4_OCD_TARGET_FILE"] - - # RPi Tools - if target_os == "RPi": - _arm_gcc_path = "${config:MAXIM_PATH}/Tools/GNUTools/gcc-arm-none-eabi/${config:v_Arm_GCC}" - _xpack_gcc_path = "${config:MAXIM_PATH}/Tools/xPack/riscv-none-embed-gcc/${config:v_xPack_GCC}" - _v_arm_gcc = "10.3.1" - _v_xpack_gcc = "10.2.0-1.2" - else: - _arm_gcc_path = defaults["ARM_GCC_PATH"] - _xpack_gcc_path = defaults["XPACK_GCC_PATH"] - _v_arm_gcc = defaults["V_ARM_GCC"] - _v_xpack_gcc = defaults["V_XPACK_GCC"] - - create_project( - _path, - _target, - _board, - program_file=_program_file, - symbol_file=_symbol_file, - i_paths=_ipaths, - v_paths=_vpaths, - m4_ocd_target_file=_m4_ocd_target_file, - arm_gcc_path=_arm_gcc_path, - xpack_gcc_path=_xpack_gcc_path, - v_arm_gcc=_v_arm_gcc, - v_xpack_gcc=_v_xpack_gcc - ) - - count += 1 - - print(f"Done! Created {count} projects.") + in_file = Path(directory).joinpath(file) + out_file = Path(out_path).joinpath(file) + + write = True + if out_file.exists(): + if not overwrite or (hash_file(in_file) == hash_file(out_file)): + write = False + + if write: + shutil.copy(in_file, out_path) + os.chmod(out_file, stat.S_IRWXU | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH) + if out_file not in updated: + updated.append(out_file) + # print(f"Wrote {os.path.basename(file)}") # Uncomment to debug + + return (len(updated) > 0) \ No newline at end of file diff --git a/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/Inject/.vscode/flash.gdb b/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/Inject/.vscode/flash.gdb index 2d86f86..fc627ae 100644 --- a/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/Inject/.vscode/flash.gdb +++ b/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/Inject/.vscode/flash.gdb @@ -5,3 +5,11 @@ define flash_m4 compare-sections monitor reset halt end + +define flash_m4_run + set architecture armv7e-m + target remote | openocd -c "gdb_port pipe;log_output flash.log" -s $arg0/scripts -f interface/$arg1 -f target/$arg2 -c "init; reset halt" + load + compare-sections + monitor resume +end \ No newline at end of file diff --git a/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/Inject/.vscode/launch.json b/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/Inject/.vscode/launch.json old mode 100644 new mode 100755 diff --git a/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/Inject/.vscode/tasks.json b/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/Inject/.vscode/tasks.json index 5eef671..3951f35 100644 --- a/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/Inject/.vscode/tasks.json +++ b/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/Inject/.vscode/tasks.json @@ -26,25 +26,56 @@ "label": "flash", "type": "shell", "command": "arm-none-eabi-gdb", + "args": [ + "--cd=\"${workspaceFolder}\"", + "--se=\"build/${config:program_file}\"", + "--symbols=build/${config:symbol_file}", + "-x=\"${workspaceFolder}/.vscode/flash.gdb\"", + "--ex=\"flash_m4 ${config:OCD_path} ${config:M4_OCD_interface_file} ${config:M4_OCD_target_file}\"", + "--batch" + ], + "group": "build", + "problemMatcher": [], + "dependsOn":["build"] + }, + { + "label": "flash & run", + "type": "shell", + "command": "arm-none-eabi-gdb", "args": [ "--cd=\"${workspaceFolder}\"", "--se=\"build/${config:program_file}\"", "--symbols=build/${config:symbol_file}", "-x=\"${workspaceFolder}/.vscode/flash.gdb\"", - "--ex=\"flash_m4 ${config:OCD_path} ${config:M4_OCD_interface_file} ${config:M4_OCD_target_file}\"", + "--ex=\"flash_m4_run ${config:OCD_path} ${config:M4_OCD_interface_file} ${config:M4_OCD_target_file}\"", "--batch" ], "group": "build", "problemMatcher": [], "dependsOn":["build"] }, + { + "label": "erase flash", + "type": "shell", + "command": "openocd", + "args": [ + "-s", "${config:OCD_path}/scripts", + "-f", "interface/${config:M4_OCD_interface_file}", + "-f", "target/${config:M4_OCD_target_file}", + "-c", "\"init; reset halt; max32xxx mass_erase 0;\"", + "-c", "exit" + ], + "group":"build", + "problemMatcher": [], + "dependsOn":[] + }, { "label": "openocd (m4)", "type": "shell", "command": "openocd", "args": [ "-s", - "${config:MAXIM_PATH}/Tools/OpenOCD/scripts", + "${config:OCD_path}/scripts", "-f", "interface/${config:M4_OCD_interface_file}", "-f", diff --git a/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/New_Project/.vscode/flash.gdb b/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/New_Project/.vscode/flash.gdb index 2d86f86..fc627ae 100644 --- a/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/New_Project/.vscode/flash.gdb +++ b/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/New_Project/.vscode/flash.gdb @@ -5,3 +5,11 @@ define flash_m4 compare-sections monitor reset halt end + +define flash_m4_run + set architecture armv7e-m + target remote | openocd -c "gdb_port pipe;log_output flash.log" -s $arg0/scripts -f interface/$arg1 -f target/$arg2 -c "init; reset halt" + load + compare-sections + monitor resume +end \ No newline at end of file diff --git a/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/New_Project/.vscode/launch.json b/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/New_Project/.vscode/launch.json old mode 100644 new mode 100755 diff --git a/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/New_Project/.vscode/tasks.json b/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/New_Project/.vscode/tasks.json index 5eef671..3951f35 100644 --- a/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/New_Project/.vscode/tasks.json +++ b/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/New_Project/.vscode/tasks.json @@ -26,25 +26,56 @@ "label": "flash", "type": "shell", "command": "arm-none-eabi-gdb", + "args": [ + "--cd=\"${workspaceFolder}\"", + "--se=\"build/${config:program_file}\"", + "--symbols=build/${config:symbol_file}", + "-x=\"${workspaceFolder}/.vscode/flash.gdb\"", + "--ex=\"flash_m4 ${config:OCD_path} ${config:M4_OCD_interface_file} ${config:M4_OCD_target_file}\"", + "--batch" + ], + "group": "build", + "problemMatcher": [], + "dependsOn":["build"] + }, + { + "label": "flash & run", + "type": "shell", + "command": "arm-none-eabi-gdb", "args": [ "--cd=\"${workspaceFolder}\"", "--se=\"build/${config:program_file}\"", "--symbols=build/${config:symbol_file}", "-x=\"${workspaceFolder}/.vscode/flash.gdb\"", - "--ex=\"flash_m4 ${config:OCD_path} ${config:M4_OCD_interface_file} ${config:M4_OCD_target_file}\"", + "--ex=\"flash_m4_run ${config:OCD_path} ${config:M4_OCD_interface_file} ${config:M4_OCD_target_file}\"", "--batch" ], "group": "build", "problemMatcher": [], "dependsOn":["build"] }, + { + "label": "erase flash", + "type": "shell", + "command": "openocd", + "args": [ + "-s", "${config:OCD_path}/scripts", + "-f", "interface/${config:M4_OCD_interface_file}", + "-f", "target/${config:M4_OCD_target_file}", + "-c", "\"init; reset halt; max32xxx mass_erase 0;\"", + "-c", "exit" + ], + "group":"build", + "problemMatcher": [], + "dependsOn":[] + }, { "label": "openocd (m4)", "type": "shell", "command": "openocd", "args": [ "-s", - "${config:MAXIM_PATH}/Tools/OpenOCD/scripts", + "${config:OCD_path}/scripts", "-f", "interface/${config:M4_OCD_interface_file}", "-f", diff --git a/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/readme.md b/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/readme.md index 22f1558..4cce59b 100644 --- a/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/readme.md +++ b/installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim/readme.md @@ -56,7 +56,7 @@ The project folders in this repo have the following dependencies: { // There may be other settings up here... - "MAXIM_PATH":"C:/MaximSDK", + "MAXIM_PATH":"C:/MaximSDK", // Only use forward slahes '/' when setting this path! "update.mode": "manual", "extensions.autoUpdate": false, diff --git a/installer/com.maximintegrated.dist.vscodemaxim/meta/installscript.js b/installer/com.maximintegrated.dist.vscodemaxim/meta/installscript.js index 8fd3678..0fa69b6 100644 --- a/installer/com.maximintegrated.dist.vscodemaxim/meta/installscript.js +++ b/installer/com.maximintegrated.dist.vscodemaxim/meta/installscript.js @@ -23,7 +23,7 @@ Component.prototype.createOperations = function() Component.prototype.installationFinished = function() { - var tag = "v1.4.2"; + var tag = "v1.4.3"; var tag_url = "https://github.com/MaximIntegratedTechSupport/VSCode-Maxim/tree/" + tag; var release_url = "https://github.com/MaximIntegratedTechSupport/VSCode-Maxim/releases/tag/" + tag; diff --git a/installer/com.maximintegrated.dist.vscodemaxim/meta/package.xml b/installer/com.maximintegrated.dist.vscodemaxim/meta/package.xml index 36129e6..afc42dc 100644 --- a/installer/com.maximintegrated.dist.vscodemaxim/meta/package.xml +++ b/installer/com.maximintegrated.dist.vscodemaxim/meta/package.xml @@ -1,9 +1,9 @@ Visual Studio Code Support Project files, templates, and documentation for integrating Visual Studio Code and the MaximSDK. Example projects come pre-populated with .vscode project folders, and this package contains information on how to use them. It installs to "Tools/VSCode-Maxim". - 2022-05-16 + 2022-08-01 net.sourceforge.openocd, net.launchpad.gcc.arm.embedded, net.launchpad.gcc.riscv.embedded, com.maximintegrated.libraries.periphdrivers - 1.4.2 + 1.4.3 1 \ No newline at end of file diff --git a/maintain.py b/maintain.py index 006f1f7..c696bdd 100644 --- a/maintain.py +++ b/maintain.py @@ -39,17 +39,15 @@ import shutil import argparse from pathlib import Path -from dataclasses import dataclass -from utils import time_me -from generate import populate_maximsdk from datetime import date curplatform = platform.system() # Get OS def log(string, file): - print(string) - file.write(f"{string}\n") - file.flush() + with open(file, "a") as f: + print(string) + f.write(f"{string}\n") + f.flush() def timestamp(): now = time.localtime() @@ -70,17 +68,9 @@ def wrapper(*args, **kwargs): return wrapper -# Run powershell command. On linux, run a shell command @time_me -def ps(cmd, env=None): - if curplatform == 'Linux': - if env is None: result = run(cmd, capture_output=True, shell=True) - else: result = run(cmd, env=env, capture_output=True, shell=True) - elif curplatform == 'Windows': - if env is None: result = run(["powershell", cmd], capture_output=True) - else: result = run(["powershell", cmd], env=env, capture_output=True) - - return result +def run_cmd(*args, **kwargs): + return run(*args, **kwargs) def sync(): # Inject .vscode folder into example projects @@ -118,7 +108,7 @@ def release(version): # Copy in to installer package print("Updating installer package...") - shutil.copytree(Path("./Releases/VSCode-Maxim-v140"), Path("./installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim"), dirs_exist_ok=True) + shutil.copytree(r_dir, Path("./installer/com.maximintegrated.dist.vscodemaxim/data/Tools/VSCode-Maxim"), dirs_exist_ok=True) # Update version # and release date in package.xml # --- @@ -159,21 +149,21 @@ def release(version): # Tests cleaning and compiling example projects for target platforms. If no targets, boards, projects, etc. are specified then it will auto-detect def test(maxim_path, targets=None, boards=None, projects=None): - maxim_path = Path(maxim_path).resolve().as_posix() + maxim_path = Path(maxim_path).resolve() env = os.environ.copy() # Simulate the VS Code terminal by appending to the Path - if curplatform == 'Linux': - env["PATH"] = f"{maxim_path}/Tools/GNUTools/10.3/bin:{maxim_path}/Tools/xPack/riscv-none-embed-gcc/10.2.0-1.2/bin:" + env["PATH"] - elif curplatform == 'Windows': - env["PATH"] = f"{maxim_path}/Tools/GNUTools/10.3/bin;{maxim_path}/Tools/xPack/riscv-none-embed-gcc/10.2.0-1.2/bin;" + env["PATH"] + # if curplatform == 'Linux': + # env["PATH"] = f"{maxim_path.as_posix()}/Tools/GNUTools/10.3/bin:{maxim_path.as_posix()}/Tools/xPack/riscv-none-embed-gcc/10.2.0-1.2/bin:" + env["PATH"] + # elif curplatform == 'Windows': + # env["PATH"] = f"{maxim_path.as_posix()}/Tools/GNUTools/10.3/bin;{maxim_path.as_posix()}/Tools/xPack/riscv-none-embed-gcc/10.2.0-1.2/bin;{maxim_path.as_posix()}/Tools/MSYS2/usr/bin;" + env["PATH"] - LOG_DIR = os.getcwd() + log_dir = Path(os.getcwd()).joinpath("buildlogs") # Create log file - try: os.mkdir(f"{LOG_DIR}/buildlogs") - except FileExistsError: pass - logfile = open(f"{LOG_DIR}/test.log", 'w') + if not log_dir.exists(): + os.mkdir(log_dir) + logfile = log_dir.joinpath("test.log") # Log system info log(timestamp(), logfile) @@ -197,9 +187,10 @@ def test(maxim_path, targets=None, boards=None, projects=None): targets = sorted(targets) # Create subfolders for target-specific logfiles - for t in targets: - try: os.mkdir(f"{LOG_DIR}/buildlogs/{t}") - except FileExistsError: pass + for t in targets: + sub_dir = log_dir.joinpath(t) + if not sub_dir.exists(): + os.mkdir(sub_dir) # Track failed projects for end summary failed = [] @@ -212,9 +203,9 @@ def test(maxim_path, targets=None, boards=None, projects=None): # Get list of supported boards for this target. if boards is None: boards = [] - for dirpath, subdirs, items in os.walk(f"{maxim_path}/Libraries/Boards/{target}"): - if "board.mk" in items and curplatform == 'Linux': boards.append(dirpath.split('/')[-1]) # Linux - elif "board.mk" in items and curplatform == 'Windows': boards.append(dirpath.split('\\')[-1]) # Board string will be the last folder in the directory path # Windows + for dirpath, subdirs, items in os.walk(maxim_path.joinpath("Libraries", "Boards", target)): + if "board.mk" in items: + boards.append(Path(dirpath).name) log(f"[BOARDS] Detected {boards}", logfile) @@ -227,9 +218,9 @@ def test(maxim_path, targets=None, boards=None, projects=None): # Get list of examples for this target. If a Makefile is in the root directory it's an example. if projects is None: projects = [] - for dirpath, subdirs, items in os.walk(f"{maxim_path}/Examples/{target}"): - if 'Makefile' in items: - projects.append(dirpath) + for dirpath, subdirs, items in os.walk(maxim_path.joinpath("Examples", target)): + if 'Makefile' in items and ("main.c" in items or "project.mk" in items): + projects.append(Path(dirpath)) log(f"[PROJECTS] Detected {projects}", logfile) @@ -241,20 +232,19 @@ def test(maxim_path, targets=None, boards=None, projects=None): # Test each project for project in projects: - if curplatform == 'Linux': project_stripped = project.split('/')[-1] # Linux - elif curplatform == 'Windows': project_stripped = project.split('\\')[-1] # Windows + project_name = project.name + print(project_name) log("---------------------", logfile) - log(f"[{target}]\t[{project_stripped}]", logfile) - os.chdir(project) # Need to us os.chdir to set working directory of subprocesses + log(f"[{target}]\t[{project_name}]", logfile) for board in boards: - buildlog = f"{target}_{board}_{project_stripped}.log" + buildlog = f"{target}_{board}_{project_name}.log" success = True # Test build (make all) - build_cmd = f"make all TARGET={target} MAXIM_PATH={maxim_path} BOARD={board} MAKE=make" - res = ps(build_cmd, env=env) # Run build command + build_cmd = f"make -r -j 8 all TARGET={target} MAXIM_PATH={maxim_path.as_posix()} BOARD={board} MAKE=make" + res = run_cmd(build_cmd, env=env, cwd=project, shell=True, capture_output=True, encoding="utf-8") # Run build command # Error check build command if res.returncode != 0: @@ -263,32 +253,31 @@ def test(maxim_path, targets=None, boards=None, projects=None): log(f"{timestamp()}[{board}] --- [BUILD]\t[FAILED] Return code {res.returncode}. See buildlogs/{buildlog}", logfile) # Log detailed output to separate output file - with open(f"{LOG_DIR}/buildlogs/{target}/{buildlog}", 'w') as f: + with open(log_dir.joinpath(target, buildlog), 'w') as f: f.write("===============\n") f.write(timestamp() + '\n') f.write(f"[PROJECT] {project}\n") f.write(f"[BOARD] {board}\n") f.write(f"[BUILD COMMAND] {build_cmd}\n") f.write("===============\n") - for line in str(res.stdout + res.stderr, encoding="ASCII").splitlines(): - f.write(line + '\n') + f.write(res.stdout + res.stderr) else: log(f"{timestamp()}[{board}] --- [BUILD]\t[SUCCESS] {round(duration, 4)}s", logfile) # Test clean (make clean) clean_cmd = f"make distclean TARGET={target} MAXIM_PATH={maxim_path} BOARD={board} MAKE=make" - res = ps(clean_cmd, env=env) # Run clean command + res = run_cmd(clean_cmd, env=env, cwd=project, shell=True, capture_output=True, encoding="utf-8") # Run clean command # Error check clean command if res.returncode != 0: - log(f"{timestamp()}[{board}] --- [CLEAN]\t[SUCCESS] {str(res.stderr, encoding='ASCII')}", logfile) + log(f"{timestamp()}[{board}] --- [CLEAN]\t[SUCCESS] {res.stderr}", logfile) success = False else: log(f"{timestamp()}[{board}] --- [CLEAN]\t[SUCCESS] {round(duration, 4)}s", logfile) # Add any failed projects to running list project_info = { "target":target, - "project":project_stripped, + "project":project_name, "board":board, "path":project, "logfile":f"buildlogs/{buildlog}" @@ -314,6 +303,11 @@ def test(maxim_path, targets=None, boards=None, projects=None): sync_parser = cmd_parser.add_parser("sync", help="Sync all .vscode project folders") +test_parser = cmd_parser.add_parser("test", help="Run a build test of the SDK.") +test_parser.add_argument("--targets", type=str, nargs="+", required=False, help="Target microcontrollers to test.") +test_parser.add_argument("--boards", type=str, nargs="+", required=False, help="Boards to test. Should match the BSP folder-name exactly.") +test_parser.add_argument("--projects", type=str, nargs="+", required=False, help="Examples to populate. Should match the example's folder name.") + if __name__ == "__main__": args = parser.parse_args() @@ -330,7 +324,10 @@ def test(maxim_path, targets=None, boards=None, projects=None): exit() if args.cmd == "release": - release(args.version, args.maxim_path) + release(args.version) elif args.cmd == "sync": - sync() \ No newline at end of file + sync() + + elif args.cmd == "test": + test(args.maxim_path, targets=args.targets, boards=args.boards, projects=args.projects) diff --git a/readme.md b/readme.md index 22f1558..4cce59b 100644 --- a/readme.md +++ b/readme.md @@ -56,7 +56,7 @@ The project folders in this repo have the following dependencies: { // There may be other settings up here... - "MAXIM_PATH":"C:/MaximSDK", + "MAXIM_PATH":"C:/MaximSDK", // Only use forward slahes '/' when setting this path! "update.mode": "manual", "extensions.autoUpdate": false, diff --git a/sdk.py b/sdk.py deleted file mode 100644 index 9aa1c10..0000000 --- a/sdk.py +++ /dev/null @@ -1,322 +0,0 @@ -""" -/******************************************************************************* -* Copyright (C) 2022 Maxim Integrated Products, Inc., All Rights Reserved. -* -* Permission is hereby granted, free of charge, to any person obtaining a -* copy of this software and associated documentation files (the "Software"), -* to deal in the Software without restriction, including without limitation -* the rights to use, copy, modify, merge, publish, distribute, sublicense, -* and/or sell copies of the Software, and to permit persons to whom the -* Software is furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES -* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -* OTHER DEALINGS IN THE SOFTWARE. -* -* Except as contained in this notice, the name of Maxim Integrated -* Products, Inc. shall not be used except as stated in the Maxim Integrated -* Products, Inc. Branding Policy. -* -* The mere transfer of this software does not imply any licenses -* of trade secrets, proprietary technology, copyrights, patents, -* trademarks, maskwork rights, or any other form of intellectual -* property whatsoever. Maxim Integrated Products, Inc. retains all -* ownership rights. -*******************************************************************************/ -""" - -from dataclasses import dataclass -from typing import Generator -from utils import * -import json -import pickle -import re -import os -from copy import * - -def get_baseitems(g: Generator): - """ - Get a list of items inside of a generator. Returns base string names only, ie. "myfile.txt" or "Examples" for a directory. - - Used to simplify searching for sub-items so that statements like "if i in get_baseitems(some generator/list of paths): ..." can be written more easily - """ - return list(map(lambda i: i.name, g)) - -@dataclass -class Target(): - name: str - boards: list - -@dataclass -class Adapter(): - name: str - cfg_file: Path - -@dataclass -class Board(): - name: str - target: Target - adapters: list - -@dataclass -class Library(): - name: str - path: Path - ipaths: tuple - vpaths: tuple - whitelist: bool - targets: tuple - - def from_libinfo(filepath): - """ - Returns a Library instance instantiated from a json info file - """ - filepath = Path(filepath) - with open(filepath, "r") as f: - libinfo = json.load(f) - keys = libinfo.keys() - assert "name" in keys and "ipaths" in keys and "vpaths" in keys and "whitelist" in keys, f"{filepath} Bad libinfo.json file!" - - _name = libinfo["name"] - _path = filepath.parent - _ipaths = tuple(i for i in libinfo["ipaths"]) - _vpaths = tuple(i for i in libinfo["vpaths"]) - _whitelist = libinfo["whitelist"] - _targets = tuple(i for i in libinfo["targets"]) if "targets" in keys else None - - return Library( - _name, - _path, - _ipaths, - _vpaths, - _whitelist, - _targets - ) - - def get_ipaths(self, target=None): - """ - Returns a tuple of include paths for the library. - If "target" is specified, the return generator will include paths specific to that target in addition to the common paths. - If the library is whitelisted (Library.whitelist is True), "target" is specified, and there is no matching - entry in the library, this function will return an empty tuple. - """ - target_info = None - - if self.whitelist is True and target is None: - return () # Return empty tuple - else: - _ipaths = list(map(lambda p: self.path.joinpath(p), self.ipaths)) # Common paths - - if target is not None and self.targets is not None: # Search for match - for i in self.targets: - if ("name" in i.keys() and i["name"] == target): - target_info = i - - if target_info is not None and "ipaths" in target_info.keys(): - _ipaths += list(map(lambda p: self.path.joinpath(p), target_info["ipaths"])) # Target-specific paths - - return tuple(_ipaths) - - def get_hfiles(self, target=None): - hfiles = [] - for ipath in self.get_ipaths(target): - hfiles += ipath.rglob("*.h") - return tuple(get_baseitems(hfiles)) - - def get_vpaths(self, target: str): - """ - Returns a tuple of browse paths for the library. - If "target" is specified, the return generator will include paths specific to that target in addition to the common paths. - If the library is whitelisted (Library.whitelist is True), "target" is specified, and there is no matching - entry in the library, this function will return an empty tuple. - """ - target_info = None - - if self.whitelist is True and target is None: - return () # Return empty tuple - else: - _vpaths = list(map(lambda p: self.path.joinpath(p), self.vpaths)) # Common paths - - if target is not None and self.targets is not None: # Search for match - for i in self.targets: - if ("name" in i.keys() and i["name"] == target): - target_info = i - - if target_info is not None and "vpaths" in target_info.keys(): - _vpaths += list(map(lambda p: self.path.joinpath(p), target_info["vpaths"])) # Target-specific paths - - return tuple(_vpaths) - - def get_vfiles(self, target): - vfiles = [] - for vpath in self.get_vpaths(target): - vfiles += vpath.rglob("*.c") - return tuple(get_baseitems(vfiles)) - -@dataclass -class Example(): - name: str - path: Path - target: Target - riscv: False - boards: list - hfiles: list - libs: list - -class SDK(): - @time_me - def __init__(self, maxim_path, targets, boards, libs, examples): - self.maxim_path = Path(maxim_path).absolute() - self.targets = targets - self.boards = boards - self.libs = libs - self.examples = examples - - @time_me - def from_search(maxim_path): - # Populate initial info - maxim_path = Path(maxim_path).absolute() - (targets, boards) = get_targets_and_boards(maxim_path) - targets = targets - boards = boards - libs = get_libraries(maxim_path) - examples = get_examples(maxim_path, targets) - - # Match examples to libraries - for e in examples: - for l in libs: - lib_hfiles = l.get_hfiles(e.target.name) - for hfile in e.hfiles: - if hfile in lib_hfiles and l not in e.libs: - e.libs.append(l) - - return SDK(maxim_path, targets, boards, libs, examples) - - @time_me - def freeze(self, filename): - if not Path(filename).exists(): - os.mkdir(Path(filename).parent) - - with open(filename, "wb") as f: - pickle.dump(self, f) - - @time_me - def thaw(filename): - with open(str(filename), "rb") as f: - tmp = pickle.load(f) - maxim_path = tmp.maxim_path - targets = tmp.targets - boards = tmp.boards - libs = tmp.libs - examples = tmp.examples - - return SDK(maxim_path, targets, boards, libs, examples) - -@time_me -def get_targets_and_boards(maxim_path): - maxim_path = Path(maxim_path).absolute() - boards_dir = maxim_path.joinpath("Libraries", "Boards") - - # Detect targets and their supported boards - targets = [] - boards = [] - for target_dir in boards_dir.iterdir(): - t = Target(target_dir.name, []) - - board_dirs = list(target_dir.iterdir()) - if target_dir.joinpath("Include").exists(): board_dirs.remove(target_dir.joinpath("Include")) - if target_dir.joinpath("Source").exists(): board_dirs.remove(target_dir.joinpath("Source")) - - for board_dir in board_dirs: - b = Board(board_dir.name, t, []) - adapterinfo_file = board_dir.joinpath("adapterinfo.json") - if adapterinfo_file.exists(): - with open(adapterinfo_file) as f: - adapterinfo = json.load(f) - for a in adapterinfo: b.adapters.append(Adapter(a["name"], a["cfg_file"])) - - t.boards.append(b) - boards.append(b) - - targets.append(t) - - return (targets, boards) - -@time_me -def get_examples(maxim_path, targets: list): - maxim_path = Path(maxim_path).absolute() - examples_dir = maxim_path.joinpath("Examples") - - # Locate examples for each target - examples = [] - for t in targets: - assert type(t) is Target - for Makefile in examples_dir.joinpath(t.name).rglob("Makefile"): - e = Example( - Makefile.parent.name, - Makefile.parent, - t, - "Makefile.RISCV" in get_baseitems(Makefile.parent.iterdir()), - [], # Filling these in later... - [], - [] - ) - - exampleinfo_file = Makefile.parent.joinpath("exampleinfo.json") - - if exampleinfo_file.exists(): - with open(exampleinfo_file): - exampleinfo = json.load(exampleinfo_file) - e.boards = t.boards if exampleinfo["boards"] == "all" else exampleinfo["boards"] - - # Get all non-standard header files used in the example source code - for cfile in e.path.rglob("*.c"): - hfiles = get_hfiles(cfile) - for hfile in hfiles: - if hfile not in e.hfiles: - e.hfiles.append(hfile) - - examples.append(e) - - return tuple(examples) - -def get_hfiles(filepath): - hfiles = [] - try: - with open(filepath, "r") as c: - lines = c.readlines() - for l in lines: - if "#include" in l: - # Search for non-standard #includes - ie. #include "myheader.h" - # [\"] means character matching " - # \S+ is equivalent to %s in scanf - # Use raw string as recommended by Python docs (r"...") - m = re.search(r"[\"]\S+.h[\"]", l) - if m is not None: - mstring = l[m.start() + 1:m.end() - 1] # strip quotes - if mstring not in hfiles: hfiles.append(mstring) - except: - print(f"Failed to get header files from {filepath}") - - return tuple(hfiles) - -@time_me -def get_libraries(maxim_path): - maxim_path = Path(maxim_path).absolute() - libs_path = maxim_path.joinpath("Libraries") - - libs = [] - - for lib_path in libs_path.iterdir(): - info_file = lib_path.joinpath("libinfo.json") - if info_file.exists(): - libs.append(Library.from_libinfo(info_file)) - - return tuple(libs) \ No newline at end of file diff --git a/utils.py b/utils.py index c595b54..dca96e9 100644 --- a/utils.py +++ b/utils.py @@ -32,10 +32,38 @@ *******************************************************************************/ """ -from dataclasses import dataclass +from collections.abc import MutableMapping +from string import Template import json -from dataclasses import dataclass from pathlib import Path +import hashlib +import os + +class UpperDict(MutableMapping): + def __init__(self, *args, **kwargs): + self.d = dict() + self.update(dict(*args, **kwargs)) + + def _parse_key(self, key): + return str(key).upper().replace(".", "_") + + def __setitem__(self, key, value) -> None: + self.d[self._parse_key(key)] = value + + def __getitem__(self, key): + return self.d[self._parse_key(key)] + + def __delitem__(self, key) -> None: + del self.d[self._parse_key(key)] + + def __iter__(self): + return iter(self.d) + + def __len__(self): + return len(self.d) + +class MSDKTemplate(Template): + delimiter = "##__" def parse_json(filename): """ @@ -43,13 +71,7 @@ def parse_json(filename): """ f = open(filename, "r") d = json.load(f) - - # Convert key values to uppercase for easier template parsing - keys = list(d.keys()) # Keys are changing on the fly, so can't use a view object - for k in keys: - d[k.upper()] = d.pop(k) - - return d + return UpperDict(d) # Timer wrapper function import time @@ -64,4 +86,43 @@ def wrapper(*args, **kwargs): return res - return wrapper \ No newline at end of file + return wrapper + +def hash(val): + if not isinstance(val, bytes): + val = bytes(val, encoding="utf-8") + return hashlib.sha1(val).digest() + +def hash_file(filepath): + return hash(open(Path(filepath), 'rb').read()) + +def hash_folder(folderpath) -> bytes: + folderpath = Path(folderpath) + result = b'' + for dir, subdirs, files in os.walk(folderpath): + for f in sorted(files): + file_path = Path(dir).joinpath(f) + relative_path = file_path.relative_to(folderpath) + + result = hash(result + hash_file(file_path) + bytes(str(relative_path), encoding="utf-8")) + + return result + +def compare_content(content: str, file: Path) -> bool: + """ + Compare the 'content' string to the existing content in 'file'. + + It seems that when a file gets written there may be some metadata that is affecting + the hash functions. As a result, this function writes 'content' to a temporary file, + then checks for equality using the temp file. + """ + if not file.exists(): + return False + + tmp = file.parent.joinpath("tmp") + with open(tmp, "w", encoding='utf-8') as f: + f.write(content) + + match = (hash_file(file) == hash_file(tmp)) + os.remove(tmp) + return match \ No newline at end of file diff --git a/vscmxm.py b/vscmxm.py deleted file mode 100644 index 38ad7fc..0000000 --- a/vscmxm.py +++ /dev/null @@ -1,94 +0,0 @@ -""" -/******************************************************************************* -* Copyright (C) 2022 Maxim Integrated Products, Inc., All Rights Reserved. -* -* Permission is hereby granted, free of charge, to any person obtaining a -* copy of this software and associated documentation files (the "Software"), -* to deal in the Software without restriction, including without limitation -* the rights to use, copy, modify, merge, publish, distribute, sublicense, -* and/or sell copies of the Software, and to permit persons to whom the -* Software is furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES -* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -* OTHER DEALINGS IN THE SOFTWARE. -* -* Except as contained in this notice, the name of Maxim Integrated -* Products, Inc. shall not be used except as stated in the Maxim Integrated -* Products, Inc. Branding Policy. -* -* The mere transfer of this software does not imply any licenses -* of trade secrets, proprietary technology, copyrights, patents, -* trademarks, maskwork rights, or any other form of intellectual -* property whatsoever. Maxim Integrated Products, Inc. retains all -* ownership rights. -*******************************************************************************/ -""" -import argparse -import platform -import os -from generate import * - -# Set up command-line args -# --- -parser = argparse.ArgumentParser(description="VSCode-Maxim command line tool.") - -parser.add_argument("--os", type=str, choices=["Windows", "Linux", "RPi"], help="(Optional) Operating system to generate the project files for. If not specified the script will auto-detect.") -parser.add_argument("--maxim_path", type=str, help="(Optional) Location of the MaximSDK. If this is not specified then the script will attempt to use the MAXIM_PATH environment variable.") - -cmd_parser = parser.add_subparsers(dest="cmd", help="sub-command", required=True) - -SDK_parser = cmd_parser.add_parser("SDK", help="Populate a MaximSDK installation's example projects with VS Code project files.") - -new_parser = cmd_parser.add_parser("new", help="Create a new VSCode-Maxim project") -new_parser.add_argument("location", help="Root location of the project to create. The project will be created in a new folder located at /.") -new_parser.add_argument("name", help="Name of the project. The project will be created in a new folder located at /.") -new_parser.add_argument("target", help="Target microcontroller for the project", choices=whitelist) -new_parser.add_argument("board", help="Target board to use for the project.") -# --- - -if __name__ == "__main__": - args = parser.parse_args() - - # Auto-detect OS - if args.os is None: - current_os = platform.platform() - if "Windows" in current_os: args.os = "Windows" - elif "Linux" in current_os: args.os = "Linux" - else: - print(f"{current_os} is not supported at this time.") - exit() - - # Auto-detect MAXIM_PATH - if args.maxim_path is None: - # Check environment variable - print("Checking MAXIM_PATH environment variable..") - if "MAXIM_PATH" in os.environ.keys(): - args.maxim_path = os.environ["MAXIM_PATH"] - print(f"MaximSDK located at {args.maxim_path}") - - else: - print("Failed to locate the MaximSDK... Please specify --maxim_path manually.") - exit() - else: - # Parse to abs path - args.maxim_path = os.path.abspath(args.maxim_path) - - # Process command - if args.cmd == "SDK": - print(f"Generating .vscode projects for all examples in SDK located at {args.maxim_path}") - populate_maximsdk(target_os=args.os, maxim_path=args.maxim_path) - - elif args.cmd == "new": - pass - # TODO: new project generator - - - \ No newline at end of file