1import os
2import re
3import sys
4import tempfile
5import subprocess
6from urllib.request import urlopen
7from urllib.error import HTTPError, URLError
8
9ARTIFACTS_URL = "http://autobuild.buildroot.net/artefacts/"
10BASE_DIR = os.path.realpath(os.path.join(os.path.dirname(__file__), "../../.."))
11
12
13def log_file_path(builddir, stage, logtofile=True):
14    """Return path to log file"""
15    return "{}-{}.log".format(builddir, stage) if logtofile else None
16
17
18def open_log_file(builddir, stage, logtofile=True):
19    """
20    Open a file for logging and return its handler.
21    If logtofile is True, returns sys.stdout. Otherwise opens a file
22    with a suitable name in the build directory.
23    """
24    return open(log_file_path(builddir, stage, logtofile), 'a+') if logtofile else sys.stdout
25
26
27def basepath(relpath=""):
28    """Return the absolute path for a file or directory relative to the Buildroot top directory."""
29    return os.path.join(BASE_DIR, relpath)
30
31
32def filepath(relpath):
33    return os.path.join(BASE_DIR, "support/testing", relpath)
34
35
36def download(dldir, filename):
37    finalpath = os.path.join(dldir, filename)
38    if os.path.exists(finalpath):
39        return finalpath
40
41    if not os.path.exists(dldir):
42        os.makedirs(dldir)
43
44    tmpfile = tempfile.mktemp(dir=dldir)
45    print("Downloading to {}".format(tmpfile))
46
47    try:
48        url_fh = urlopen(os.path.join(ARTIFACTS_URL, filename))
49        with open(tmpfile, "w+b") as tmpfile_fh:
50            tmpfile_fh.write(url_fh.read())
51    except (HTTPError, URLError) as err:
52        os.unlink(tmpfile)
53        raise err
54
55    print("Renaming from {} to {}".format(tmpfile, finalpath))
56    os.rename(tmpfile, finalpath)
57    return finalpath
58
59
60def run_cmd_on_host(builddir, cmd):
61    """Call subprocess.check_output and return the text output."""
62    out = subprocess.check_output(cmd,
63                                  stderr=open(os.devnull, "w"),
64                                  cwd=builddir,
65                                  env={"LANG": "C"},
66                                  universal_newlines=True)
67    return out
68
69
70def get_elf_arch_tag(builddir, prefix, fpath, tag):
71    """
72    Runs the cross readelf on 'fpath', then extracts the value of tag 'tag'.
73    Example:
74    >>> get_elf_arch_tag('output', 'arm-none-linux-gnueabi-',
75                         'bin/busybox', 'Tag_CPU_arch')
76    v5TEJ
77    >>>
78    """
79    cmd = ["host/bin/{}-readelf".format(prefix),
80           "-A", os.path.join("target", fpath)]
81    out = run_cmd_on_host(builddir, cmd)
82    regexp = re.compile(r"^  {}: (.*)$".format(tag))
83    for line in out.splitlines():
84        m = regexp.match(line)
85        if not m:
86            continue
87        return m.group(1)
88    return None
89
90
91def get_file_arch(builddir, prefix, fpath):
92    return get_elf_arch_tag(builddir, prefix, fpath, "Tag_CPU_arch")
93
94
95def get_elf_prog_interpreter(builddir, prefix, fpath):
96    """
97    Runs the cross readelf on 'fpath' to extract the program interpreter
98    name and returns it.
99    Example:
100    >>> get_elf_prog_interpreter('br-tests/TestExternalToolchainLinaroArm',
101                                 'arm-linux-gnueabihf',
102                                 'bin/busybox')
103    /lib/ld-linux-armhf.so.3
104    >>>
105    """
106    cmd = ["host/bin/{}-readelf".format(prefix),
107           "-l", os.path.join("target", fpath)]
108    out = run_cmd_on_host(builddir, cmd)
109    regexp = re.compile(r"^ *\[Requesting program interpreter: (.*)\]$")
110    for line in out.splitlines():
111        m = regexp.match(line)
112        if not m:
113            continue
114        return m.group(1)
115    return None
116
117
118def img_round_power2(img):
119    """
120    Rounds up the size of an image file to the next power of 2
121    """
122    sz = os.stat(img).st_size
123    pow2 = 1
124    while pow2 < sz:
125        pow2 = pow2 << 1
126    with open(img, 'ab') as f:
127        f.truncate(pow2)
128