123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- 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 = {'LC_ALL': 'C.UTF-8'}
- 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.items())
- ) ] 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 as ex:
- print('failed to execute %s: %s' % (name, ex), file=log)
- return None if captureOutput else False
- inputText = None if inputSeq is None \
- else ('\n'.join(inputSeq) + '\n').encode('utf-8')
- stdoutdata, stderrdata = proc.communicate(inputText)
- stdouttext = stdoutdata.decode('utf-8', 'replace')
- if captureOutput:
- assert stderrdata is not None
- messages = stderrdata.decode('utf-8', 'replace')
- else:
- assert stderrdata is None
- messages = stdouttext
- if messages:
- log.write('%s command: %s\n' % (name, ' '.join(commandLine)))
- if inputText is not None:
- log.write('input:\n')
- log.write(str(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 stdouttext if captureOutput else True
- else:
- print('return code from %s: %d' % (name, proc.returncode), file=log)
- 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 = {}
- prevKey = None
- for line in output.split('\n'):
- line = line.strip()
- if not line or line.startswith('#'):
- continue
- if line.startswith(signature):
- prevKey = None
- keyValueStr = line[len(signature) : ]
- try:
- key, value = keyValueStr.split(None, 1)
- except ValueError:
- key, value = keyValueStr, ''
- if key not in keys:
- log.write(
- 'Ignoring macro expand signature match on '
- 'non-requested macro "%s"\n' % key
- )
- continue
- elif value == '':
- # GCC5 puts value on separate line.
- prevKey = key
- continue
- elif prevKey is not None:
- key = prevKey
- value = line
- prevKey = None
- else:
- continue
- if value != key:
- expanded[key] = value
- 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
- )
|