summaryrefslogtreecommitdiff
path: root/var/binary-size.py
blob: 99a1f4d0552734068dc43388e559a67f053b369d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#!/usr/bin/python3 -B

# Copyright (C) 2019-2020 The Nitrocli Developers
# SPDX-License-Identifier: GPL-3.0-or-later

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 cwd:
    check_call(["git", "clone", root, cwd])
    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:]))