1import errno
2import itertools
3import math
4import os
5import platform
6import signal
7import subprocess
8import sys
9
10def to_bytes(str):
11    # Encode to UTF-8 to get binary data.
12    return str.encode('utf-8')
13
14def to_string(bytes):
15    if isinstance(bytes, str):
16        return bytes
17    return to_bytes(bytes)
18
19def convert_string(bytes):
20    try:
21        return to_string(bytes.decode('utf-8'))
22    except UnicodeError:
23        return str(bytes)
24
25def detectCPUs():
26    """
27    Detects the number of CPUs on a system. Cribbed from pp.
28    """
29    # Linux, Unix and MacOS:
30    if hasattr(os, "sysconf"):
31        if "SC_NPROCESSORS_ONLN" in os.sysconf_names:
32            # Linux & Unix:
33            ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
34            if isinstance(ncpus, int) and ncpus > 0:
35                return ncpus
36        else: # OSX:
37            return int(capture(['sysctl', '-n', 'hw.ncpu']))
38    # Windows:
39    if "NUMBER_OF_PROCESSORS" in os.environ:
40        ncpus = int(os.environ["NUMBER_OF_PROCESSORS"])
41        if ncpus > 0:
42            return ncpus
43    return 1 # Default
44
45def mkdir_p(path):
46    """mkdir_p(path) - Make the "path" directory, if it does not exist; this
47    will also make directories for any missing parent directories."""
48    if not path or os.path.exists(path):
49        return
50
51    parent = os.path.dirname(path)
52    if parent != path:
53        mkdir_p(parent)
54
55    try:
56        os.mkdir(path)
57    except OSError:
58        e = sys.exc_info()[1]
59        # Ignore EEXIST, which may occur during a race condition.
60        if e.errno != errno.EEXIST:
61            raise
62
63def capture(args, env=None):
64    """capture(command) - Run the given command (or argv list) in a shell and
65    return the standard output."""
66    p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
67                         env=env)
68    out,_ = p.communicate()
69    return convert_string(out)
70
71def which(command, paths = None):
72    """which(command, [paths]) - Look up the given command in the paths string
73    (or the PATH environment variable, if unspecified)."""
74
75    if paths is None:
76        paths = os.environ.get('PATH','')
77
78    # Check for absolute match first.
79    if os.path.isfile(command):
80        return command
81
82    # Would be nice if Python had a lib function for this.
83    if not paths:
84        paths = os.defpath
85
86    # Get suffixes to search.
87    # On Cygwin, 'PATHEXT' may exist but it should not be used.
88    if os.pathsep == ';':
89        pathext = os.environ.get('PATHEXT', '').split(';')
90    else:
91        pathext = ['']
92
93    # Search the paths...
94    for path in paths.split(os.pathsep):
95        for ext in pathext:
96            p = os.path.join(path, command + ext)
97            if os.path.exists(p):
98                return p
99
100    return None
101
102def checkToolsPath(dir, tools):
103    for tool in tools:
104        if not os.path.exists(os.path.join(dir, tool)):
105            return False;
106    return True;
107
108def whichTools(tools, paths):
109    for path in paths.split(os.pathsep):
110        if checkToolsPath(path, tools):
111            return path
112    return None
113
114def printHistogram(items, title = 'Items'):
115    items.sort(key = lambda item: item[1])
116
117    maxValue = max([v for _,v in items])
118
119    # Select first "nice" bar height that produces more than 10 bars.
120    power = int(math.ceil(math.log(maxValue, 10)))
121    for inc in itertools.cycle((5, 2, 2.5, 1)):
122        barH = inc * 10**power
123        N = int(math.ceil(maxValue / barH))
124        if N > 10:
125            break
126        elif inc == 1:
127            power -= 1
128
129    histo = [set() for i in range(N)]
130    for name,v in items:
131        bin = min(int(N * v/maxValue), N-1)
132        histo[bin].add(name)
133
134    barW = 40
135    hr = '-' * (barW + 34)
136    print('\nSlowest %s:' % title)
137    print(hr)
138    for name,value in items[-20:]:
139        print('%.2fs: %s' % (value, name))
140    print('\n%s Times:' % title)
141    print(hr)
142    pDigits = int(math.ceil(math.log(maxValue, 10)))
143    pfDigits = max(0, 3-pDigits)
144    if pfDigits:
145        pDigits += pfDigits + 1
146    cDigits = int(math.ceil(math.log(len(items), 10)))
147    print("[%s] :: [%s] :: [%s]" % ('Range'.center((pDigits+1)*2 + 3),
148                                    'Percentage'.center(barW),
149                                    'Count'.center(cDigits*2 + 1)))
150    print(hr)
151    for i,row in enumerate(histo):
152        pct = float(len(row)) / len(items)
153        w = int(barW * pct)
154        print("[%*.*fs,%*.*fs) :: [%s%s] :: [%*d/%*d]" % (
155            pDigits, pfDigits, i*barH, pDigits, pfDigits, (i+1)*barH,
156            '*'*w, ' '*(barW-w), cDigits, len(row), cDigits, len(items)))
157
158# Close extra file handles on UNIX (on Windows this cannot be done while
159# also redirecting input).
160kUseCloseFDs = not (platform.system() == 'Windows')
161def executeCommand(command, cwd=None, env=None):
162    p = subprocess.Popen(command, cwd=cwd,
163                         stdin=subprocess.PIPE,
164                         stdout=subprocess.PIPE,
165                         stderr=subprocess.PIPE,
166                         env=env, close_fds=kUseCloseFDs)
167    out,err = p.communicate()
168    exitCode = p.wait()
169
170    # Detect Ctrl-C in subprocess.
171    if exitCode == -signal.SIGINT:
172        raise KeyboardInterrupt
173
174    # Ensure the resulting output is always of string type.
175    out = convert_string(out)
176    err = convert_string(err)
177
178    return out, err, exitCode
179
180def usePlatformSdkOnDarwin(config, lit_config):
181    # On Darwin, support relocatable SDKs by providing Clang with a
182    # default system root path.
183    if 'darwin' in config.target_triple:
184        try:
185            cmd = subprocess.Popen(['xcrun', '--show-sdk-path'],
186                                   stdout=subprocess.PIPE, stderr=subprocess.PIPE)
187            out, err = cmd.communicate()
188            out = out.strip()
189            res = cmd.wait()
190        except OSError:
191            res = -1
192        if res == 0 and out:
193            sdk_path = out
194            lit_config.note('using SDKROOT: %r' % sdk_path)
195            config.environment['SDKROOT'] = sdk_path
196