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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
from msysutils import msysActive, msysPathToNative
from os import environ
from shlex import split as shsplit
from subprocess import PIPE, STDOUT, Popen
if msysActive():
def fixArgs(args):
for arg in args:
if arg.startswith('-I') or arg.startswith('-L'):
yield arg[ : 2] + msysPathToNative(arg[2 : ])
elif arg.startswith('/'):
yield msysPathToNative(arg)
else:
yield arg
else:
def fixArgs(args):
return iter(args)
class _Command(object):
@classmethod
def fromLine(cls, commandStr, flagsStr):
commandParts = shsplit(commandStr)
flags = shsplit(flagsStr)
env = {}
while commandParts:
if '=' in commandParts[0]:
name, value = commandParts[0].split('=', 1)
del commandParts[0]
env[name] = value
else:
return cls(
env,
commandParts[0],
list(fixArgs(commandParts[1 : ] + flags))
)
else:
raise ValueError('No command specified in "%s"' % commandStr)
def __init__(self, env, executable, flags):
self.__env = env
self.__executable = executable
self.__flags = flags
mergedEnv = dict(environ)
mergedEnv.update(env)
self.__mergedEnv = mergedEnv
def __str__(self):
return ' '.join(
[ self.__executable ] + self.__flags + (
[ '(%s)' % ' '.join(
'%s=%s' % item
for item in sorted(self.__env.iteritems())
) ] if self.__env else []
)
)
def _run(self, log, name, args, inputSeq, captureOutput):
commandLine = [ self.__executable ] + args + self.__flags
try:
proc = Popen(
commandLine,
bufsize = -1,
env = self.__mergedEnv,
stdin = None if inputSeq is None else PIPE,
stdout = PIPE,
stderr = PIPE if captureOutput else STDOUT,
)
except OSError, ex:
print >> log, 'failed to execute %s: %s' % (name, ex)
return None if captureOutput else False
inputText = None if inputSeq is None else '\n'.join(inputSeq) + '\n'
stdoutdata, stderrdata = proc.communicate(inputText)
if captureOutput:
assert stderrdata is not None
messages = stderrdata
else:
assert stderrdata is None
messages = stdoutdata
if messages:
log.write('%s command: %s\n' % (name, ' '.join(commandLine)))
if inputText is not None:
log.write('input:\n')
log.write(inputText)
if not inputText.endswith('\n'):
log.write('\n')
log.write('end input.\n')
# pylint 0.18.0 somehow thinks 'messages' is a list, not a string.
# pylint: disable-msg=E1103
messages = messages.replace('\r', '')
log.write(messages)
if not messages.endswith('\n'):
log.write('\n')
if proc.returncode == 0:
return stdoutdata if captureOutput else True
else:
print >> log, 'return code from %s: %d' % (name, proc.returncode)
return None if captureOutput else False
class CompileCommand(_Command):
__expandSignature = 'EXPAND_MACRO_'
def compile(self, log, sourcePath, objectPath):
return self._run(
log, 'compiler', [ '-c', sourcePath, '-o', objectPath ], None, False
)
def expand(self, log, headers, *keys):
signature = self.__expandSignature
def iterLines():
for header in headers:
yield '#include %s' % header
for key in keys:
yield '%s%s %s' % (signature, key, key)
output = self._run(
log, 'preprocessor', [ '-E', '-' ], iterLines(), True
)
if output is None:
if len(keys) == 1:
return None
else:
return (None, ) * len(keys)
else:
expanded = {}
for line in output.split('\n'):
if line.startswith(signature):
key, value = line[len(signature) : ].split(' ', 1)
value = value.strip()
if key in keys:
if value != key:
expanded[key] = value
else:
log.write(
'Ignoring macro expand signature match on '
'non-requested macro "%s"\n' % key
)
if len(keys) == 1:
return expanded.get(keys[0])
else:
return tuple(expanded.get(key) for key in keys)
class LinkCommand(_Command):
def link(self, log, objectPaths, binaryPath):
return self._run(
log, 'linker', objectPaths + [ '-o', binaryPath ], None, False
)
|