shithub: pokecrystal

Download patch

ref: 8577d78f4d2eb04ce4cde83c106870846e50cae2
parent: 4819a54b164268fed7a7f36a3bf4bd4d1f727178
parent: 1bc167379466b67c8b5bd1af311765c8b036c340
author: Rangi <35663410+Rangi42@users.noreply.github.com>
date: Sat Feb 9 13:35:39 EST 2019

Merge pull request #599 from mid-kid/master

Add tool to find unnamed symbols: Take 2

--- a/.travis.yml
+++ b/.travis.yml
@@ -23,3 +23,5 @@
 script:
   - make -j2 compare
   - check_status
+after_success:
+  - .travis/webhook.sh
--- /dev/null
+++ b/.travis/webhook.sh
@@ -1,0 +1,18 @@
+#!/bin/sh
+
+# Only run this script if it's the master branch build.
+[ "$TRAVIS_BRANCH" != 'master' -o "$TRAVIS_PULL_REQUEST" != 'false' ] && exit
+
+root="$(realpath "$(dirname "$0")/..")"
+content=""
+
+# Report unnamed symbols
+content+="$("$root/tools/unnamed.py" -r "$root" "$root/pokecrystal.sym" | grep -v -e '^lib/mobile/' -e '^mobile/' | head)"
+
+curl -H 'Content-Type: application/json' -X POST "$POKECRYSTAL_DISCORD_WEBHOOK_URL" -d@- << EOF
+{
+    "username": "$POKECRYSTAL_DISCORD_WEBHOOK_USERNAME",
+    "avatar_url": "$POKECRYSTAL_DISCORD_WEBHOOK_AVATAR_URL",
+    "content": "\`\`\`$(echo "$content" | sed -E ':a;N;$!ba;s/\r{0,1}\n/\\n/g')\`\`\`"
+}
+EOF
--- /dev/null
+++ b/tools/unnamed.py
@@ -1,0 +1,98 @@
+#!/usr/bin/env python3
+
+from sys import stderr, exit
+from subprocess import Popen, PIPE
+from struct import unpack, calcsize
+from enum import Enum
+
+class symtype(Enum):
+    LOCAL = 0
+    IMPORT = 1
+    EXPORT = 2
+
+def unpack_file(fmt, file):
+    size = calcsize(fmt)
+    return unpack(fmt, file.read(size))
+
+def read_string(file):
+    buf = bytearray()
+    while True:
+        b = file.read(1)
+        if b is None or b == b'\0':
+            return buf.decode()
+        else:
+            buf += b
+
+
+# Fix broken pipe when using `head`
+from signal import signal, SIGPIPE, SIG_DFL
+signal(SIGPIPE,SIG_DFL)
+
+import argparse
+parser = argparse.ArgumentParser(description="Parse the symfile to find unnamed symbols")
+parser.add_argument('symfile', type=argparse.FileType('r'), help="the list of symbols")
+parser.add_argument('-r', '--rootdir', type=str, help="scan the output files to obtain a list of files with unnamed symbols (NOTE: will rebuild objects as necessary)")
+args = parser.parse_args()
+
+# Get list of object files
+objects = None
+if args.rootdir:
+    for line in Popen(["make", "-C", args.rootdir, "-s", "-p"],
+            stdout=PIPE).stdout.read().decode().split("\n"):
+        if line.startswith("crystal_obj := "):
+            objects = line[15:].strip().split()
+            break
+    else:
+        print("Error: Object files not found!", file=stderr)
+        exit(1)
+
+# Scan all unnamed symbols from the symfile
+symbols_total = 0
+symbols = set()
+for line in args.symfile:
+    line = line.split(";")[0].strip()
+    split = line.split(" ")
+    if len(split) < 2:
+        continue
+
+    symbols_total += 1
+
+    symbol = " ".join(split[1:]).strip()
+    if symbol[-3:].lower() == split[0][-3:].lower():
+        symbols.add(symbol)
+
+# If no object files were provided, just print what we know and exit
+print("Unnamed symbols: %d (%.2f%% complete)" % (len(symbols),
+        (symbols_total - len(symbols)) / symbols_total * 100))
+if not objects:
+    for sym in symbols:
+        print(sym)
+    exit()
+
+# Count the amount of symbols in each file
+files = {}
+for objfile in objects:
+    f = open(objfile, "rb")
+    if unpack_file("4s", f)[0] != b'RGB6':
+        print("Error: File '%s' is of an unknown format." % filename, file=stderr)
+        exit(1)
+
+    num_symbols = unpack_file("<II", f)[0]
+    for x in range(num_symbols):
+        sym_name = read_string(f)
+        sym_type = symtype(unpack_file("<B", f)[0])
+        if sym_type == symtype.IMPORT:
+            continue
+        sym_filename = read_string(f)
+        unpack_file("<III", f)
+        if sym_name not in symbols:
+            continue
+
+        if sym_filename not in files:
+            files[sym_filename] = 0
+        files[sym_filename] += 1
+
+# Sort the files, the one with the most amount of symbols first
+files = sorted([(fname, files[fname]) for fname in files], key=lambda x: x[1], reverse=True)
+for f in files:
+    print("%s: %d" % (f[0], f[1]))