aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Mueller <deso@posteo.net>2019-01-16 18:58:32 -0800
committerDaniel Mueller <deso@posteo.net>2019-01-16 18:58:32 -0800
commit4cf1b0c003a8bd0214e548a77bf0336148235931 (patch)
treeb5179deb570aef2f056c01284666f1daa1080798
parent6cae9704af80f6ffbca2770c5bbf9ab94ef846f8 (diff)
downloadnitrocli-4cf1b0c003a8bd0214e548a77bf0336148235931.tar.gz
nitrocli-4cf1b0c003a8bd0214e548a77bf0336148235931.tar.bz2
Add a script to determine the nitrocli binary size at a git revision
We have been loosely tracking the resulting size of the stripped release binary as that is arguably the most relevant metric to optimize for. To have a better idea of the influence of various changes and their effect on the binary size, this change adds a script that automates the process of gathering this metric. E.g., $ var/binary-size.py HEAD~3 HEAD --unit kib > 994 > 970
-rwxr-xr-xnitrocli/var/binary-size.py134
1 files changed, 134 insertions, 0 deletions
diff --git a/nitrocli/var/binary-size.py b/nitrocli/var/binary-size.py
new file mode 100755
index 0000000..3653814
--- /dev/null
+++ b/nitrocli/var/binary-size.py
@@ -0,0 +1,134 @@
+#!/usr/bin/python3 -B
+
+#/***************************************************************************
+# * Copyright (C) 2019 Daniel Mueller (deso@posteo.net) *
+# * *
+# * This program is free software: you can redistribute it and/or modify *
+# * it under the terms of the GNU General Public License as published by *
+# * the Free Software Foundation, either version 3 of the License, or *
+# * (at your option) any later version. *
+# * *
+# * This program is distributed in the hope that it will be useful, *
+# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+# * GNU General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU General Public License *
+# * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+# ***************************************************************************/
+
+from argparse import (
+ ArgumentParser,
+ ArgumentTypeError,
+)
+from concurrent.futures import (
+ ThreadPoolExecutor,
+)
+from json import (
+ loads as jsonLoad,
+)
+from os import (
+ stat,
+)
+from os.path import (
+ join,
+)
+from subprocess import (
+ check_call,
+ check_output,
+)
+from sys import (
+ argv,
+ exit,
+)
+from tempfile import (
+ TemporaryDirectory,
+)
+
+UNITS = {
+ "byte": 1,
+ "kib": 1024,
+ "mib": 1024 * 1024,
+}
+
+def unit(string):
+ """Create a unit."""
+ if string in UNITS:
+ return UNITS[string]
+ else:
+ raise ArgumentTypeError("Invalid unit: \"%s\"." % string)
+
+
+def nitrocliPath(cwd):
+ """Determine the path to the nitrocli release build binary."""
+ out = check_output(["cargo", "metadata", "--format-version=1"], cwd=cwd)
+ data = jsonLoad(out)
+ return join(data["target_directory"], "release", "nitrocli")
+
+
+def fileSize(path):
+ """Determine the size of the file at the given path."""
+ return stat(path).st_size
+
+
+def repoRoot():
+ """Retrieve the root directory of the current git repository."""
+ out = check_output(["git", "rev-parse", "--show-toplevel"])
+ return out.decode().strip()
+
+
+def resolveCommit(commit):
+ """Resolve a commit into a SHA1 hash."""
+ out = check_output(["git", "rev-parse", "--verify", "%s^{commit}" % commit])
+ return out.decode().strip()
+
+
+def determineSizeAt(root, rev):
+ """Determine the size of the nitrocli release build binary at the given git revision."""
+ sha1 = resolveCommit(rev)
+ with TemporaryDirectory() as d:
+ cwd = join(d, "nitrocli")
+ check_call(["git", "clone", root, d])
+ check_call(["git", "checkout", "--quiet", sha1], cwd=cwd)
+ check_call(["cargo", "build", "--quiet", "--release"], cwd=cwd)
+
+ ncli = nitrocliPath(cwd)
+ check_call(["strip", ncli])
+ return fileSize(ncli)
+
+
+def setupArgumentParser():
+ """Create and initialize an argument parser."""
+ parser = ArgumentParser()
+ parser.add_argument(
+ "revs", metavar="REVS", nargs="+",
+ help="The revisions at which to measure the release binary size.",
+ )
+ parser.add_argument(
+ "-u", "--unit", default="byte", dest="unit", metavar="UNIT", type=unit,
+ help="The unit in which to output the result (%s)." % "|".join(UNITS.keys()),
+ )
+ return parser
+
+
+def main(args):
+ """Determine the size of the nitrocli binary at given git revisions."""
+ parser = setupArgumentParser()
+ ns = parser.parse_args(args)
+ root = repoRoot()
+ futures = []
+ executor = ThreadPoolExecutor()
+
+ for rev in ns.revs:
+ futures += [executor.submit(lambda r=rev: determineSizeAt(root, r))]
+
+ executor.shutdown(wait=True)
+
+ for future in futures:
+ print(int(round(future.result() / ns.unit, 0)))
+
+ return 0
+
+
+if __name__ == "__main__":
+ exit(main(argv[1:]))