From ca0731685792d12bc60636c1184d32b60ec0595c Mon Sep 17 00:00:00 2001 From: Seeky <58006653+SeekyCt@users.noreply.github.com> Date: Sat, 16 Sep 2023 15:58:15 +0100 Subject: [PATCH] git subrepo pull --force spm-headers subrepo: subdir: "spm-headers" merged: "fe088fe" upstream: origin: "https://github.com/SeekyCt/spm-headers.git" branch: "spm-decomp" commit: "fe088fe" git-subrepo: version: "0.4.5" origin: "???" commit: "???" --- spm-headers/.github/workflows/build.yml | 28 +++ spm-headers/.gitignore | 2 + spm-headers/.gitrepo | 4 +- spm-headers/README.md | 4 + spm-headers/configure.py | 294 ++++++++++++++++++++++++ spm-headers/include/common.h | 2 +- spm-headers/include/msl/stdio.h | 6 +- spm-headers/include/spm/evt_env.h | 1 + spm-headers/include/spm/filemgr.h | 1 + spm-headers/include/spm/mobjdrv.h | 1 - spm-headers/include/spm/rel/sp4_13.h | 8 +- spm-headers/include/spm/romfont.h | 3 +- spm-headers/include/spm/system.h | 3 +- spm-headers/include/wii/os/OSError.h | 3 +- spm-headers/linker/spm.eu0.lst | 65 +++--- spm-headers/tools/incgen.py | 59 +++++ spm-headers/tools/incgen_single.py | 24 ++ spm-headers/tools/symbolcheck.py | 118 ++++++++++ 18 files changed, 582 insertions(+), 44 deletions(-) create mode 100644 spm-headers/.github/workflows/build.yml create mode 100644 spm-headers/.gitignore create mode 100644 spm-headers/configure.py create mode 100644 spm-headers/tools/incgen.py create mode 100644 spm-headers/tools/incgen_single.py create mode 100644 spm-headers/tools/symbolcheck.py diff --git a/spm-headers/.github/workflows/build.yml b/spm-headers/.github/workflows/build.yml new file mode 100644 index 0000000..c7aeecc --- /dev/null +++ b/spm-headers/.github/workflows/build.yml @@ -0,0 +1,28 @@ +name: Build + +on: + push: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + container: ghcr.io/seekyct/spm-docker:v1 + strategy: + fail-fast: false + matrix: + region: [eu0, eu1, us0, us1, us2, jp0, jp1, kr0] + test: [mod_ctx, old_mod_ctx, decomp_ctx, mod_ctx_shuffle, test_mod_individual] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Git config + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - name: Build + run: | + python3 configure.py ${{matrix.test}} \ + --regions ${{matrix.region}} \ + --shuffle 500 \ + --wine wibo \ + --codewarrior /spmfiles/4199_60831/mwcceppc.exe + ninja diff --git a/spm-headers/.gitignore b/spm-headers/.gitignore new file mode 100644 index 0000000..f862010 --- /dev/null +++ b/spm-headers/.gitignore @@ -0,0 +1,2 @@ +build +build.ninja diff --git a/spm-headers/.gitrepo b/spm-headers/.gitrepo index bcce90d..6502f73 100644 --- a/spm-headers/.gitrepo +++ b/spm-headers/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/SeekyCt/spm-headers.git branch = spm-decomp - commit = e738b81e267d9d6d2773679f10be4c835c9c3120 - parent = 5b50966483e9991d4b171b6c6ad46901686987ef + commit = fe088fe1cf6a3fe5beb0fa0457b7228125c3aaf3 + parent = ab4cb8c6cc8b7fe30f2d9a9aa6006259ab73033e method = merge cmdver = 0.4.5 diff --git a/spm-headers/README.md b/spm-headers/README.md index 03268d2..2015dcd 100644 --- a/spm-headers/README.md +++ b/spm-headers/README.md @@ -21,6 +21,10 @@ It is recommended to use this with [git subrepo](https://github.com/ingydotnet/g For use in decomp, the `include` and `decomp` folders should be added to the include path, and the preprocessor define `DECOMP` should be used. +### Testing + +The `SKIP_PPCDIS` preprocessor define will stop `ppcdis.h` being included. + ## Mods For use in mods, the `include` and `mod` folder should be added to the include path and an lst from `linker` should be used. If including your compiler's C++ standard library, the preprocessor define `USE_STL` should be used. diff --git a/spm-headers/configure.py b/spm-headers/configure.py new file mode 100644 index 0000000..06e9807 --- /dev/null +++ b/spm-headers/configure.py @@ -0,0 +1,294 @@ +from argparse import ArgumentParser +from io import StringIO +import os +from sys import executable as PYTHON, platform +from typing import List + +from ninja_syntax import Writer + + +parser = ArgumentParser() +parser.add_argument("tests", type=str, nargs="*", help="Tests to run" \ + "(mod_ctx, old_mod_ctx, decomp_ctx, mod_ctx_shuffle, test_mod_individual)") +parser.add_argument("--regions", type=str, nargs="+", help="Regions to test") +parser.add_argument("--seed", type=int, default=1, help="Shuffling seed") +parser.add_argument("--shuffle", type=int, default=50, help="Number of randomised orders to test") +parser.add_argument("--individual", action="store_true", help="Test every header on its own") +parser.add_argument("--codewarrior", type=str, help="mwcceppc.exe path") +parser.add_argument("-w", "--wine", type=str, help="Wine override (ignored on Windows)") +args = parser.parse_args() + +outbuf = StringIO() +n = Writer(outbuf) + +############# +# Variables # +############# + +n.variable("builddir", "build") +n.variable("incdir", "include") + +n.variable("seed", args.seed) + +n.variable("incgen", f"{PYTHON} tools/incgen.py") +n.variable("incgen_single", f"{PYTHON} tools/incgen_single.py") + +n.variable("devkitppc", os.environ.get("DEVKITPPC")) +n.variable("cpp", os.path.join("$devkitppc", "bin", "powerpc-eabi-cpp")) + +MOD_INCLUDES = ["$incdir", "$mod_incdir"] +n.variable("mod_cc", os.path.join("$devkitppc", "bin", "powerpc-eabi-g++")) +n.variable("mod_incdir", "mod") +n.variable("mod_source", os.path.join("$builddir", "mod.c")) +n.variable( + "mod_machdep", + ' '.join([ + "-mno-sdata", # Disable SDA sections since not main binary + "-DGEKKO", # CPU preprocessor define + "-mcpu=750", # Set CPU to 750cl + "-meabi", # Set ppc abi to eabi + "-mhard-float", # Enable hardware floats + "-nostdlib", # Don't link std lib + "-mregnames", # Enable r prefix for registers in asm + "-ffreestanding", # Tell compiler environment isn't hosted + ]) +) +n.variable( + "mod_cflags", + ' '.join([ + "$mod_machdep", + "$mod_includes", + + "-ffunction-sections", # Allow function deadstripping + "-fdata-sections", # Allow data deadstripping + "-g", # Emit debug info + "-O3", # High optimisation for speed + "-Wall", # Enable all warnings + "-Wextra", # Enable even more warnings + "-Wpedantic", # Enable even more warnings than that + "-Wshadow", # Enable variable shadowing warning + "-Werror", # Error on warnings + "-fmax-errors=1", # Stop after 1 error + ]) +) +n.variable( + "mod_cxxflags", + ' '.join([ + "$mod_cflags", + + "-fno-exceptions", # Disable C++ exceptions + "-fno-rtti", # Disable runtime type info + "-std=gnu++17", # Use C++17 with GNU extensions + ]) +) + +DECOMP_INCLUDES = [ + "$incdir", + "$decomp_incdir", +] +if args.codewarrior: + cw = args.codewarrior.replace("/", os.sep) + if args.wine: + wine = args.wine + else: + wine = "wine" + if platform != "win32": + cw = f"{wine} {cw}" +else: + cw = None +n.variable("decomp_cc", cw) +n.variable("decomp_incdir", "decomp") +n.variable("decomp_source", os.path.join("$builddir", "decomp.c")) +n.variable( + "decomp_cxxflags", + ' '.join([ + "-lang c++", + "-W all", + "-fp fmadd", + "-Cpp_exceptions off", + "-O4", + "-use_lmw_stmw on", + "-str pool", + "-rostr", + "-sym dwarf-2", + "-ipa file", + ]) +) + +REGIONS = [ + "eu0", + "eu1", + "jp0", + "jp1", + "us0", + "us1", + "us2", + "kr0", +] + +######## +# Rules# +######## + +ALLOW_CHAIN = "cmd /c " if os.name == "nt" else "" + +n.rule( + "incgen", + ALLOW_CHAIN + "$incgen $dirs -s $seed -i $iteration > $out" +) + +n.rule( + "incgen_single", + ALLOW_CHAIN + "$incgen_single $header > $out" +) + +n.rule( + "mod_cc", + command = "$mod_cc -MMD -MT $out -MF $out.d $mod_cxxflags $flags -c $in -o $out", + depfile = "$out.d", + deps = "gcc", + description = "Mod CC $out", +) + +n.rule( + "decomp_cc", + command = ALLOW_CHAIN + "$cpp -M $in -MF $out.d $cppflags && " \ + "$decomp_cc $decomp_cflags $flags -c $in -o $out", + description = "Decomp CC $in", + deps = "gcc", + depfile = "$out.d" +) + +########## +# Builds # +########## + +def incgen(source: str, dirs: List[str], iteration: int = 0): + n.build( + source, + rule="incgen", + inputs=[], + variables={ + "dirs" : ' '.join(dirs), + "iteration" : str(iteration), + } + ) + +def incgen_single(source, header: str): + n.build( + source, + rule="incgen_single", + inputs=[], + variables={ + "header" : header, + } + ) + +def compile(dest: str, source: str, includes: List[str], defines: List[str], decomp: bool = False): + define_flags = ' '.join(f"-D{d}" for d in defines) + gcc_include_flags = ' '.join(f"-I {d}" for d in includes) + if decomp: + mwcc_include_flags = "-I- " + ' '.join(f"-i {d}" for d in includes) + n.build( + dest, + "decomp_cc", + [source], + variables={ + "cppflags" : f"{gcc_include_flags} {define_flags}", + "flags" : f"{mwcc_include_flags} {define_flags}", + } + ) + else: + n.build( + dest, + "mod_cc", + [source], + variables={ + "flags" : f"{gcc_include_flags} {define_flags}", + } + ) + +def compile_regions(dest: str, source: str, regions: List[str], includes: List[str], + defines: List[str], decomp: bool = False): + for region in regions: + compile(dest.format(region=region), source, includes, defines + [f"SPM_{region.upper()}"], + decomp) + +def find_headers(dirname: str, base=None) -> List[str]: + """Returns a list of all headers in a folder recursively""" + + if base is None: + base = dirname + + ret = [] + for name in os.listdir(dirname): + path = os.path.join(dirname, name) + if os.path.isdir(path): + ret.extend(find_headers(path, base)) + elif name.endswith('.h'): + ret.append(path[len(base)+1:]) + + return ret + +# Test the headers in the spm-utils modding setup +def test_mod_ctx(regions: List[str]): + compile_regions(os.path.join("$builddir", "{region}", "mod.o"), "$mod_source", regions, + MOD_INCLUDES, ["USE_STL"]) + +# Test the headers in the old modding setup +def test_old_mod_ctx(regions: List[str]): + compile_regions(os.path.join("$builddir", "{region}", "old_mod.o"), "$mod_source", regions, + MOD_INCLUDES, []) + +# Test the headers in the decomp setup +def test_decomp_ctx(regions: List[str]): + assert args.codewarrior, "Error: decomp_ctx test requires --codewarrior" + compile_regions(os.path.join("$builddir", "{region}", "decomp.o"), "$decomp_source", regions, + DECOMP_INCLUDES, ["DECOMP", "SKIP_PPCDIS"], True) + +# Test shuffled include orders +def test_mod_ctx_shuffle(regions: List[str]): + assert args.shuffle, "mod_ctx_shuffle test requires --shuffle" + for i in range(1, 1 + args.shuffle): + source = os.path.join("$builddir", f"shuffle_{args.seed}", f"{i}.cpp") + incgen(source, MOD_INCLUDES, i) + compile_regions(os.path.join("$builddir", "{region}", f"shuffle_mod_{args.seed}_{i}.o"), + source, regions, MOD_INCLUDES, ["USE_STL"]) + +# Test individual headers +def test_mod_individual(regions: List[str]): + for header in find_headers("include"): + name = header.removeprefix("include/").removesuffix(".h") + source = os.path.join("$builddir", "individual", f"{name}.c") + incgen_single(source, header) + compile_regions(os.path.join("$builddir", "{region}", "individual", f"{name}.o"), source, + regions, MOD_INCLUDES, ["USE_STL", "SPM_EU0"]) + +test_fns = { + "mod_ctx" : test_mod_ctx, + "old_mod_ctx" : test_old_mod_ctx, + "decomp_ctx" : test_decomp_ctx, + "mod_ctx_shuffle" : test_mod_ctx_shuffle, + "test_mod_individual" : test_mod_individual, +} + +incgen("$mod_source", MOD_INCLUDES) +default_tests = ["mod_ctx", "old_mod_ctx"] + +if args.codewarrior: + incgen("$decomp_source", DECOMP_INCLUDES) + default_tests.append("decomp_ctx") + +# Add requested tests +tests = set(args.tests if args.tests else default_tests) +regions = args.regions if args.regions else REGIONS +for test in tests: + if test not in test_fns: + assert False, f"Error: unknown test {test}" + else: + test_fns[test](regions) + + +with open("build.ninja", 'w') as f: + f.write(outbuf.getvalue()) +n.close() diff --git a/spm-headers/include/common.h b/spm-headers/include/common.h index c674c19..981e5af 100644 --- a/spm-headers/include/common.h +++ b/spm-headers/include/common.h @@ -1,6 +1,6 @@ #pragma once -#ifdef DECOMP +#if (defined DECOMP) && !(defined SKIP_PPCDIS) // Decomp should include ppcdis.h in all files #include #else diff --git a/spm-headers/include/msl/stdio.h b/spm-headers/include/msl/stdio.h index 4e5dfb8..bf215a3 100644 --- a/spm-headers/include/msl/stdio.h +++ b/spm-headers/include/msl/stdio.h @@ -12,8 +12,8 @@ USING(msl::stdarg::va_list) int vsprintf(char * s, const char * format, va_list arg); -int snprintf(char * dest, size_t n, const char * format, ...); -int sprintf(char * dest, const char * format, ...); -int sscanf(const char * str, const char * format, ...); +int ATTRIBUTE_FORMAT(printf, 3, 4) snprintf(char * dest, size_t n, const char * format, ...); +int ATTRIBUTE_FORMAT(printf, 2, 3) sprintf(char * dest, const char * format, ...); +int ATTRIBUTE_FORMAT(scanf, 2, 3) sscanf(const char * str, const char * format, ...); CPP_WRAPPER_END() diff --git a/spm-headers/include/spm/evt_env.h b/spm-headers/include/spm/evt_env.h index bbf6bec..a845402 100644 --- a/spm-headers/include/spm/evt_env.h +++ b/spm-headers/include/spm/evt_env.h @@ -1,6 +1,7 @@ #pragma once #include +#include CPP_WRAPPER(spm::evt_env) diff --git a/spm-headers/include/spm/filemgr.h b/spm-headers/include/spm/filemgr.h index 0df4f5a..e194c12 100644 --- a/spm-headers/include/spm/filemgr.h +++ b/spm-headers/include/spm/filemgr.h @@ -122,6 +122,7 @@ DECOMP_STATIC(void filemgr_dvdReadDoneCallback(s32 result, DVDFileInfo * fileInf Loads a file asynchronously Returns a null pointer if the file isn't loaded yet */ +ATTRIBUTE_FORMAT(printf, 3, 4) FileEntry * fileAsyncf(s32 fileType, s32 p2, const char * format, ...); FileEntry * fileAsync(const char * path, s32 fileType, s32 p3); diff --git a/spm-headers/include/spm/mobjdrv.h b/spm-headers/include/spm/mobjdrv.h index 2e28a56..a639263 100644 --- a/spm-headers/include/spm/mobjdrv.h +++ b/spm-headers/include/spm/mobjdrv.h @@ -5,7 +5,6 @@ #pragma once #include -#include #include #include #include diff --git a/spm-headers/include/spm/rel/sp4_13.h b/spm-headers/include/spm/rel/sp4_13.h index 2f95de5..4d2cdd3 100644 --- a/spm-headers/include/spm/rel/sp4_13.h +++ b/spm-headers/include/spm/rel/sp4_13.h @@ -13,10 +13,10 @@ char * spReturnAngryAnimPos(char *param_1); char * func_80c5c304(char *param_1); EVT_DECLARE_USER_FUNC(func_80c5c36c, 0) -DECOMP_STATIC(NPCTribeAnimDef spMrLStartTribeAnimDefs[0]) -DECOMP_STATIC(NPCTribeAnimDef spMrLTribeAnimDefs[10]) -DECOMP_STATIC(NPCTribeAnimDef spMrLTauntTribeAnimDefs[6]) -DECOMP_STATIC(NPCTribeAnimDef spTheGreenThunderTribeAnimDefs[10]) +DECOMP_STATIC(NPCTribeAnimDef spMrLStartTribeAnimDefs[1]) +DECOMP_STATIC(NPCTribeAnimDef spMrLTribeAnimDefs[11]) +DECOMP_STATIC(NPCTribeAnimDef spMrLTauntTribeAnimDefs[7]) +DECOMP_STATIC(NPCTribeAnimDef spTheGreenThunderTribeAnimDefs[5]) EVT_DECLARE(mr_l_appear_evt) EVT_DECLARE(brobot_appear_evt) diff --git a/spm-headers/include/spm/romfont.h b/spm-headers/include/spm/romfont.h index 6413c95..48ad5db 100644 --- a/spm-headers/include/spm/romfont.h +++ b/spm-headers/include/spm/romfont.h @@ -33,7 +33,8 @@ DECOMP_STATIC(RomfontWork romfont_work) const char * romFontGetMessage(s32 id); void romFontInit(); DECOMP_STATIC(void romfont_romFontMake()) -void romFontPrintGX(f32 x, f32 y, f32 scale, const GXColor * colour, const char * msg, ...); +void ATTRIBUTE_FORMAT(printf, 5, 6) romFontPrintGX(f32 x, f32 y, f32 scale, const GXColor * colour, + const char * msg, ...); s32 romFontGetWidth(const char * message); #endif diff --git a/spm-headers/include/spm/system.h b/spm-headers/include/spm/system.h index fc6b109..ca5be71 100644 --- a/spm-headers/include/spm/system.h +++ b/spm-headers/include/spm/system.h @@ -84,7 +84,8 @@ const char * getMapdataDvdRoot(); Assertion failure handlers */ s32 NORETURN __assert(const char * filename, s32 line, const char * assertion); -s32 NORETURN __assert2(const char * filename, s32 line, const char * assertion, const char * message, ...); +s32 NORETURN ATTRIBUTE_FORMAT(printf, 4, 5) __assert2( + const char * filename, s32 line, const char * assertion, const char * message, ...); /* Rounds a float to an int diff --git a/spm-headers/include/wii/os/OSError.h b/spm-headers/include/wii/os/OSError.h index 8dd8837..5fe7e4e 100644 --- a/spm-headers/include/wii/os/OSError.h +++ b/spm-headers/include/wii/os/OSError.h @@ -12,7 +12,8 @@ ATTRIBUTE_FORMAT(printf, 1, 2) void OSReport(const char * message, ...); UNKNOWN_FUNCTION(OSVReport); // Weak symbol, games overrides with its own -void OSPanic(const char * filename, s32 line, const char * msg, ...); +ATTRIBUTE_FORMAT(printf, 3, 4) void OSPanic(const char * filename, s32 line, + const char * msg, ...); UNKNOWN_FUNCTION(OSSetErrorHandler); void __OSUnhandledException(s32 p1, s32 p2, s32 p3, s32 p4); diff --git a/spm-headers/linker/spm.eu0.lst b/spm-headers/linker/spm.eu0.lst index 8ec43e0..5950939 100644 --- a/spm-headers/linker/spm.eu0.lst +++ b/spm-headers/linker/spm.eu0.lst @@ -855,21 +855,26 @@ // 80cf5180 5 .data // 80f64340 6 .bss +// machi.c +1,1,52e4:evt_machi_set_elv_descs +// more + +//sp4_13.c +// text +1,1,16854:spReturnAngryAnimPos +1,1,16a08:func_80c5c304 +1,1,16a70:func_80c5c36c +// data +1,5,a95f8:spMrLStartTribeAnimDefs +1,5,a9600:spMrLTribeAnimDefs +1,5,a96e8:spMrLTauntTribeAnimDefs +1,5,a9788:spTheGreenThunderTribeAnimDefs +1,5,a9b24:mr_l_appear_evt +1,5,aa4a0:brobot_appear_evt +// more + // an2_08.c -1,6,1320:an2_08_wp -1,5,fa148:lbl_80def2c8 -1,5,fa26c:rpg_snd_hit_evt -1,5,fa2b8:rpg_snd_miss_evt -1,5,fa380:rpg_attacking_evt -1,5,fa858:rpg_use_pixls_evt -1,5,fb380:rpg_use_items_evt -1,5,fc7d8:rpg_run_away_evt -1,5,fc9f0:underchomp_attack_1_evt -1,5,fcd30:underchomp_attack_2_evt -1,5,fd070:underchomp_attack_3_evt -1,5,fd3b0:rpg_check_win_or_continue_evt -1,5,fe150:final_rpg_child_evt -1,5,fe1cc:begin_rpg_parent_evt +// text 1,1,2700c:func_80c6c908 1,1,27050:rpgHandleMenu 1,1,27590:rpg_screen_draw @@ -894,6 +899,22 @@ 1,1,2d344:evt_rpg_point_handling 1,1,2d37c:evt_rpg_get_item_msg 1,1,2d3fc:evt_an2_08_draw_face +// data +1,5,fa148:lbl_80def2c8 +1,5,fa26c:rpg_snd_hit_evt +1,5,fa2b8:rpg_snd_miss_evt +1,5,fa380:rpg_attacking_evt +1,5,fa858:rpg_use_pixls_evt +1,5,fb380:rpg_use_items_evt +1,5,fc7d8:rpg_run_away_evt +1,5,fc9f0:underchomp_attack_1_evt +1,5,fcd30:underchomp_attack_2_evt +1,5,fd070:underchomp_attack_3_evt +1,5,fd3b0:rpg_check_win_or_continue_evt +1,5,fe150:final_rpg_child_evt +1,5,fe1cc:begin_rpg_parent_evt +// bss +1,6,1320:an2_08_wp //an.c 1,1,319ac:evt_an_init_tpl @@ -901,22 +922,6 @@ 1,1,31cb4:evt_an_remove_npcs // more -//sp4_13.c -1,1,16854:spReturnAngryAnimPos -1,1,16a08:func_80c5c304 -1,1,16a70:func_80c5c36c -1,5,a95f8:spMrLStartTribeAnimDefs -1,5,a9600:spMrLTribeAnimDefs -1,5,a96e8:spMrLTauntTribeAnimDefs -1,5,a9788:spTheGreenThunderTribeAnimDefs -1,5,a9b24:mr_l_appear_evt -1,5,aa4a0:brobot_appear_evt -// more - -// machi.c -1,1,000052e4:evt_machi_set_elv_descs -// more - // dan.c // text 1,1,3c5b4:evt_dan_read_data diff --git a/spm-headers/tools/incgen.py b/spm-headers/tools/incgen.py new file mode 100644 index 0000000..339c855 --- /dev/null +++ b/spm-headers/tools/incgen.py @@ -0,0 +1,59 @@ +""" +Creates a source file including all headers +""" + +from argparse import ArgumentParser +from os import listdir +from os.path import isdir +from random import seed, shuffle +from typing import List + + +def find_headers(dirname: str, base=None) -> List[str]: + """Returns a list of all headers in a folder recursively""" + + if base is None: + base = dirname + + ret = [] + for name in listdir(dirname): + path = dirname + '/' + name + if isdir(path): + ret.extend(find_headers(path, base)) + elif name.endswith('.h'): + ret.append(path[len(base)+1:]) + + return ret + + +def make_includes(dirnames: List[str], iteration: int = 0) -> str: + """Returns a chain of #includes for all headers in a folder""" + + headers = [] + for dirname in dirnames: + headers.extend(find_headers(dirname)) + + for i in range(iteration): + shuffle(headers) + + return '\n'.join( + f"#include <{header}>" + for header in headers + ) + + +if __name__ == "__main__": + parser = ArgumentParser() + parser.add_argument("dirs", type=str, nargs='+', help="Folders to include") + parser.add_argument("-s", "--seed", type=int, help="Shuffling seed") + parser.add_argument("-i", "--iteration", type=int, + help="Shuffling iteration number (0 = unshuffled)") + args = parser.parse_args() + + seed(args.seed) + + # Find all headers + includes = make_includes(args.dirs, args.iteration) + + # Output + print(includes) diff --git a/spm-headers/tools/incgen_single.py b/spm-headers/tools/incgen_single.py new file mode 100644 index 0000000..e400607 --- /dev/null +++ b/spm-headers/tools/incgen_single.py @@ -0,0 +1,24 @@ +""" +Creates a source file including all headers +""" + +from argparse import ArgumentParser +from random import seed, shuffle + + +def make_include(path: str) -> str: + """Generates a #include for a path""" + + return f"#include <{path}>" + + +if __name__ == "__main__": + parser = ArgumentParser() + parser.add_argument("path", type=str, help="Header to include") + args = parser.parse_args() + + # Find all headers + includes = make_include(args.path) + + # Output + print(includes) diff --git a/spm-headers/tools/symbolcheck.py b/spm-headers/tools/symbolcheck.py new file mode 100644 index 0000000..24d331a --- /dev/null +++ b/spm-headers/tools/symbolcheck.py @@ -0,0 +1,118 @@ +from argparse import ArgumentParser +from dataclasses import dataclass +from typing import Dict, Set + +from pycparser.c_parser import CParser +from pycparser import c_ast as ca + + +class Visitor(ca.NodeVisitor): + decls: Set[str] + + def __init__(self): + self.decls = set() + + def visit_Struct(self, struct: ca.Struct) -> None: + pass + + def visit_Union(self, union: ca.Union) -> None: + pass + + def visit_Decl(self, decl: ca.Decl) -> None: + if decl.name is not None: + self.decls.add(decl.name) + if not isinstance(decl.type, ca.FuncDecl): + self.visit(decl.type) + + def visit_Enum(self, enum: ca.Enum) -> None: + pass + + def visit_FuncDef(self, fn: ca.FuncDef) -> None: + if fn.decl.name is not None: + self.decls.add(fn.decl.name) + + +@dataclass(frozen=True) +class SymbolLoc: + module_id: int + section_id: int + offset: int + + +class LstParser: + name_map: Dict[str, SymbolLoc] + loc_map: Dict[SymbolLoc, str] + + def _parse_line(self, line: str): + line = line.lstrip() + if len(line) == 0 or line.startswith("//"): + return + + colon_parts = [s.strip() for s in line.split(":")] + if len(colon_parts) != 2: + return + name = colon_parts[1] + + comma_parts = colon_parts[0].split(",") + if len(comma_parts) == 1: + module_id = 0 + section_id = 0 + offset = int(comma_parts[0], 16) + else: + module_id = int(comma_parts[0]) + section_id = int(comma_parts[1]) + offset = int(comma_parts[2], 16) + + loc = SymbolLoc(module_id, section_id, offset) + self.name_map[name] = loc + self.addr_map[loc] = name + + def __init__(self, path: str): + self.name_map = {} + self.addr_map = {} + + with open(path) as f: + lines = f.readlines() + + for line in lines: + self._parse_line(line) + + def get_name(self, loc: SymbolLoc) -> str: + return self.loc_map[loc] + + def get_loc(self, name: SymbolLoc) -> str: + return self.addr_map[name] + + def get_names(self) -> Set[str]: + return set(self.name_map.keys()) + + +def get_c_decls(path: str) -> Set[str]: + with open(path, "r") as f: + txt = f.read() + + c_parser = CParser() + ast = c_parser.parse(txt) + v = Visitor() + v.visit(ast) + return v.decls + + +def get_lst_decls(path: str) -> Set[str]: + lst_decls = LstParser(args.lst_path) + return lst_decls.get_names() + + +if __name__ == "__main__": + parser = ArgumentParser() + parser.add_argument("ctx_path", type=str, help="Context path") + parser.add_argument("lst_path", type=str, help="LST path") + args = parser.parse_args() + + ctx_decls = get_c_decls(args.ctx_path) + lst_decls = get_lst_decls(args.lst_path) + + extra = lst_decls - ctx_decls + missing = ctx_decls - lst_decls + print("\n".join(f"+ {s}" for s in sorted(extra))) + print("\n".join(f"- {s}" for s in sorted(missing)))