File: compilers.py

package info (click to toggle)
openmsx 0.10.1-2
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 16,628 kB
  • ctags: 19,723
  • sloc: cpp: 131,938; xml: 25,418; tcl: 15,394; python: 4,012; sh: 365; makefile: 26
file content (149 lines) | stat: -rw-r--r-- 4,020 bytes parent folder | download
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
			)