| 1 |
from msysutils import msysActive, msysPathToNative |
| 2 |
|
| 3 |
from os import environ |
| 4 |
from shlex import split as shsplit |
| 5 |
from subprocess import PIPE, STDOUT, Popen |
| 6 |
|
| 7 |
if msysActive(): |
| 8 |
def fixArgs(args): |
| 9 |
for arg in args: |
| 10 |
if arg.startswith('-I') or arg.startswith('-L'): |
| 11 |
yield arg[ : 2] + msysPathToNative(arg[2 : ]) |
| 12 |
elif arg.startswith('/'): |
| 13 |
yield msysPathToNative(arg) |
| 14 |
else: |
| 15 |
yield arg |
| 16 |
else: |
| 17 |
def fixArgs(args): |
| 18 |
return iter(args) |
| 19 |
|
| 20 |
class _Command(object): |
| 21 |
|
| 22 |
@classmethod |
| 23 |
def fromLine(cls, commandStr, flagsStr): |
| 24 |
commandParts = shsplit(commandStr) |
| 25 |
flags = shsplit(flagsStr) |
| 26 |
env = {'LC_ALL': 'C.UTF-8'} |
| 27 |
while commandParts: |
| 28 |
if '=' in commandParts[0]: |
| 29 |
name, value = commandParts[0].split('=', 1) |
| 30 |
del commandParts[0] |
| 31 |
env[name] = value |
| 32 |
else: |
| 33 |
return cls( |
| 34 |
env, |
| 35 |
commandParts[0], |
| 36 |
list(fixArgs(commandParts[1 : ] + flags)) |
| 37 |
) |
| 38 |
else: |
| 39 |
raise ValueError('No command specified in "%s"' % commandStr) |
| 40 |
|
| 41 |
def __init__(self, env, executable, flags): |
| 42 |
self.__env = env |
| 43 |
self.__executable = executable |
| 44 |
self.__flags = flags |
| 45 |
|
| 46 |
mergedEnv = dict(environ) |
| 47 |
mergedEnv.update(env) |
| 48 |
self.__mergedEnv = mergedEnv |
| 49 |
|
| 50 |
def __str__(self): |
| 51 |
return ' '.join( |
| 52 |
[ self.__executable ] + self.__flags + ( |
| 53 |
[ '(%s)' % ' '.join( |
| 54 |
'%s=%s' % item |
| 55 |
for item in sorted(self.__env.items()) |
| 56 |
) ] if self.__env else [] |
| 57 |
) |
| 58 |
) |
| 59 |
|
| 60 |
def _run(self, log, name, args, inputSeq, captureOutput): |
| 61 |
commandLine = [ self.__executable ] + args + self.__flags |
| 62 |
try: |
| 63 |
proc = Popen( |
| 64 |
commandLine, |
| 65 |
bufsize = -1, |
| 66 |
env = self.__mergedEnv, |
| 67 |
stdin = None if inputSeq is None else PIPE, |
| 68 |
stdout = PIPE, |
| 69 |
stderr = PIPE if captureOutput else STDOUT, |
| 70 |
) |
| 71 |
except OSError as ex: |
| 72 |
print('failed to execute %s: %s' % (name, ex), file=log) |
| 73 |
return None if captureOutput else False |
| 74 |
inputText = None if inputSeq is None \ |
| 75 |
else ('\n'.join(inputSeq) + '\n').encode('utf-8') |
| 76 |
stdoutdata, stderrdata = proc.communicate(inputText) |
| 77 |
stdouttext = stdoutdata.decode('utf-8', 'replace') |
| 78 |
if captureOutput: |
| 79 |
assert stderrdata is not None |
| 80 |
messages = stderrdata.decode('utf-8', 'replace') |
| 81 |
else: |
| 82 |
assert stderrdata is None |
| 83 |
messages = stdouttext |
| 84 |
if messages: |
| 85 |
log.write('%s command: %s\n' % (name, ' '.join(commandLine))) |
| 86 |
if inputText is not None: |
| 87 |
log.write('input:\n') |
| 88 |
log.write(str(inputText)) |
| 89 |
if not inputText.endswith(b'\n'): |
| 90 |
log.write('\n') |
| 91 |
log.write('end input.\n') |
| 92 |
|
| 93 |
|
| 94 |
messages = messages.replace('\r', '') |
| 95 |
log.write(messages) |
| 96 |
if not messages.endswith('\n'): |
| 97 |
log.write('\n') |
| 98 |
if proc.returncode == 0: |
| 99 |
return stdouttext if captureOutput else True |
| 100 |
else: |
| 101 |
print('return code from %s: %d' % (name, proc.returncode), file=log) |
| 102 |
return None if captureOutput else False |
| 103 |
|
| 104 |
class CompileCommand(_Command): |
| 105 |
__expandSignature = 'EXPAND_MACRO_' |
| 106 |
|
| 107 |
def compile(self, log, sourcePath, objectPath): |
| 108 |
return self._run( |
| 109 |
log, 'compiler', [ '-c', sourcePath, '-o', objectPath ], None, False |
| 110 |
) |
| 111 |
|
| 112 |
def expand(self, log, headers, *keys): |
| 113 |
signature = self.__expandSignature |
| 114 |
def iterLines(): |
| 115 |
for header in headers: |
| 116 |
yield '#include %s' % header |
| 117 |
for key in keys: |
| 118 |
yield '%s%s %s' % (signature, key, key) |
| 119 |
output = self._run( |
| 120 |
log, 'preprocessor', [ '-E', '-' ], iterLines(), True |
| 121 |
) |
| 122 |
if output is None: |
| 123 |
if len(keys) == 1: |
| 124 |
return None |
| 125 |
else: |
| 126 |
return (None, ) * len(keys) |
| 127 |
else: |
| 128 |
expanded = {} |
| 129 |
prevKey = None |
| 130 |
for line in output.split('\n'): |
| 131 |
line = line.strip() |
| 132 |
if not line or line.startswith('#'): |
| 133 |
continue |
| 134 |
if line.startswith(signature): |
| 135 |
prevKey = None |
| 136 |
keyValueStr = line[len(signature) : ] |
| 137 |
try: |
| 138 |
key, value = keyValueStr.split(None, 1) |
| 139 |
except ValueError: |
| 140 |
key, value = keyValueStr, '' |
| 141 |
if key not in keys: |
| 142 |
log.write( |
| 143 |
'Ignoring macro expand signature match on ' |
| 144 |
'non-requested macro "%s"\n' % key |
| 145 |
) |
| 146 |
continue |
| 147 |
elif value == '': |
| 148 |
|
| 149 |
prevKey = key |
| 150 |
continue |
| 151 |
elif prevKey is not None: |
| 152 |
key = prevKey |
| 153 |
value = line |
| 154 |
prevKey = None |
| 155 |
else: |
| 156 |
continue |
| 157 |
if value != key: |
| 158 |
expanded[key] = value |
| 159 |
if len(keys) == 1: |
| 160 |
return expanded.get(keys[0]) |
| 161 |
else: |
| 162 |
return tuple(expanded.get(key) for key in keys) |
| 163 |
|
| 164 |
class LinkCommand(_Command): |
| 165 |
|
| 166 |
def link(self, log, objectPaths, binaryPath): |
| 167 |
return self._run( |
| 168 |
log, 'linker', objectPaths + [ '-o', binaryPath ], None, False |
| 169 |
) |