blob: fb69193be873dfd38f1d44779e4480ccef6ee231 [file] [log] [blame]
Daniel Dunbar378530c2009-01-05 19:53:30 +00001import os
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +00002import platform
Daniel Dunbara95c6392009-02-19 22:59:57 +00003import subprocess
Daniel Dunbar378530c2009-01-05 19:53:30 +00004import sys
5import tempfile
6from pprint import pprint
7
8###
9
10import Arguments
11import Jobs
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +000012import HostInfo
Daniel Dunbar378530c2009-01-05 19:53:30 +000013import Phases
14import Tools
15import Types
16import Util
17
18# FIXME: Clean up naming of options and arguments. Decide whether to
19# rename Option and be consistent about use of Option/Arg.
20
21####
22
Daniel Dunbar378530c2009-01-05 19:53:30 +000023class Driver(object):
Daniel Dunbara0026f22009-01-16 23:12:12 +000024 def __init__(self, driverName, driverDir):
25 self.driverName = driverName
26 self.driverDir = driverDir
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +000027 self.hostInfo = None
Daniel Dunbare9f1a692009-01-06 06:12:13 +000028 self.parser = Arguments.OptionParser()
Daniel Dunbar7c2f91b2009-01-14 01:03:36 +000029 self.cccHostBits = self.cccHostMachine = None
30 self.cccHostSystem = self.cccHostRelease = None
31 self.cccCXX = False
Daniel Dunbar4c751dc2009-01-14 01:32:05 +000032 self.cccEcho = False
Daniel Dunbar7c2f91b2009-01-14 01:03:36 +000033 self.cccFallback = False
Daniel Dunbar72999172009-01-29 23:54:06 +000034 self.cccNoClang = self.cccNoClangCXX = self.cccNoClangPreprocessor = False
35 self.cccClangArchs = None
Daniel Dunbar378530c2009-01-05 19:53:30 +000036
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +000037 # Certain options suppress the 'no input files' warning.
38 self.suppressMissingInputWarning = False
39
Daniel Dunbardbc9ee92009-01-09 22:21:24 +000040 # Host queries which can be forcibly over-riden by the user for
41 # testing purposes.
42 #
43 # FIXME: We should make sure these are drawn from a fixed set so
44 # that nothing downstream ever plays a guessing game.
45
46 def getHostBits(self):
47 if self.cccHostBits:
48 return self.cccHostBits
49
50 return platform.architecture()[0].replace('bit','')
51
52 def getHostMachine(self):
53 if self.cccHostMachine:
54 return self.cccHostMachine
55
56 machine = platform.machine()
57 # Normalize names.
58 if machine == 'Power Macintosh':
59 return 'ppc'
Daniel Dunbar405327e2009-01-27 19:29:51 +000060 if machine == 'x86_64':
61 return 'i386'
Daniel Dunbardbc9ee92009-01-09 22:21:24 +000062 return machine
63
64 def getHostSystemName(self):
65 if self.cccHostSystem:
66 return self.cccHostSystem
67
68 return platform.system().lower()
69
Daniel Dunbarc2148562009-01-12 04:21:12 +000070 def getHostReleaseName(self):
71 if self.cccHostRelease:
72 return self.cccHostRelease
73
74 return platform.release()
75
Daniel Dunbar4c751dc2009-01-14 01:32:05 +000076 def getenvBool(self, name):
77 var = os.getenv(name)
78 if not var:
79 return False
80
81 try:
82 return bool(int(var))
83 except:
84 return False
85
Daniel Dunbardbc9ee92009-01-09 22:21:24 +000086 ###
87
Daniel Dunbarf677a602009-01-21 02:03:52 +000088 def getFilePath(self, name, toolChain=None):
89 tc = toolChain or self.toolChain
90 for p in tc.filePathPrefixes:
91 path = os.path.join(p, name)
92 if os.path.exists(path):
93 return path
94 return name
95
96 def getProgramPath(self, name, toolChain=None):
97 tc = toolChain or self.toolChain
98 for p in tc.programPathPrefixes:
99 path = os.path.join(p, name)
100 if os.path.exists(path):
101 return path
102 return name
103
104 ###
105
Daniel Dunbar74727872009-01-06 01:35:44 +0000106 def run(self, argv):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000107 # FIXME: Things to support from environment: GCC_EXEC_PREFIX,
108 # COMPILER_PATH, LIBRARY_PATH, LPATH, CC_PRINT_OPTIONS,
109 # QA_OVERRIDE_GCC3_OPTIONS, ...?
110
111 # FIXME: -V and -b processing
112
113 # Handle some special -ccc- options used for testing which are
114 # only allowed at the beginning of the command line.
115 cccPrintOptions = False
116 cccPrintPhases = False
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000117
118 # FIXME: How to handle override of host? ccc specific options?
119 # Abuse -b?
Daniel Dunbar72999172009-01-29 23:54:06 +0000120 arg = os.getenv('CCC_ADD_ARGS')
121 if arg:
122 args = filter(None, map(str.strip, arg.split(',')))
123 argv = args + argv
Daniel Dunbar4c751dc2009-01-14 01:32:05 +0000124
Daniel Dunbar74727872009-01-06 01:35:44 +0000125 while argv and argv[0].startswith('-ccc-'):
Daniel Dunbara0026f22009-01-16 23:12:12 +0000126 fullOpt,argv = argv[0],argv[1:]
127 opt = fullOpt[5:]
Daniel Dunbar378530c2009-01-05 19:53:30 +0000128
129 if opt == 'print-options':
130 cccPrintOptions = True
131 elif opt == 'print-phases':
132 cccPrintPhases = True
Daniel Dunbar7c2f91b2009-01-14 01:03:36 +0000133 elif opt == 'cxx':
134 self.cccCXX = True
Daniel Dunbar4c751dc2009-01-14 01:32:05 +0000135 elif opt == 'echo':
136 self.cccEcho = True
Daniel Dunbar7c2f91b2009-01-14 01:03:36 +0000137 elif opt == 'fallback':
138 self.cccFallback = True
Daniel Dunbar72999172009-01-29 23:54:06 +0000139
140 elif opt == 'no-clang':
141 self.cccNoClang = True
142 elif opt == 'no-clang-cxx':
143 self.cccNoClangCXX = True
144 elif opt == 'no-clang-cpp':
145 self.cccNoClangPreprocessor = True
146 elif opt == 'clang-archs':
147 self.cccClangArchs,argv = argv[0].split(','),argv[1:]
148
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000149 elif opt == 'host-bits':
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000150 self.cccHostBits,argv = argv[0],argv[1:]
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000151 elif opt == 'host-machine':
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000152 self.cccHostMachine,argv = argv[0],argv[1:]
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000153 elif opt == 'host-system':
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000154 self.cccHostSystem,argv = argv[0],argv[1:]
Daniel Dunbarc2148562009-01-12 04:21:12 +0000155 elif opt == 'host-release':
156 self.cccHostRelease,argv = argv[0],argv[1:]
Daniel Dunbar378530c2009-01-05 19:53:30 +0000157 else:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000158 raise Arguments.InvalidArgumentsError("invalid option: %r" % fullOpt)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000159
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000160 self.hostInfo = HostInfo.getHostInfo(self)
Daniel Dunbar08dea462009-01-10 02:07:54 +0000161 self.toolChain = self.hostInfo.getToolChain()
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000162
Daniel Dunbar74727872009-01-06 01:35:44 +0000163 args = self.parser.parseArgs(argv)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000164
165 # FIXME: Ho hum I have just realized -Xarch_ is broken. We really
166 # need to reparse the Arguments after they have been expanded by
167 # -Xarch. How is this going to work?
168 #
169 # Scratch that, we aren't going to do that; it really disrupts the
170 # organization, doesn't consistently work with gcc-dd, and is
171 # confusing. Instead we are going to enforce that -Xarch_ is only
172 # used with options which do not alter the driver behavior. Let's
173 # hope this is ok, because the current architecture is a little
174 # tied to it.
175
176 if cccPrintOptions:
Daniel Dunbar74727872009-01-06 01:35:44 +0000177 self.printOptions(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000178 sys.exit(0)
179
Daniel Dunbar74727872009-01-06 01:35:44 +0000180 self.handleImmediateOptions(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000181
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000182 if self.hostInfo.useDriverDriver():
Daniel Dunbar74727872009-01-06 01:35:44 +0000183 phases = self.buildPipeline(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000184 else:
Daniel Dunbar74727872009-01-06 01:35:44 +0000185 phases = self.buildNormalPipeline(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000186
187 if cccPrintPhases:
Daniel Dunbar74727872009-01-06 01:35:44 +0000188 self.printPhases(phases, args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000189 sys.exit(0)
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000190
Daniel Dunbar378530c2009-01-05 19:53:30 +0000191 if 0:
192 print Util.pprint(phases)
193
Daniel Dunbar74727872009-01-06 01:35:44 +0000194 jobs = self.bindPhases(phases, args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000195
196 # FIXME: We should provide some basic sanity checking of the
197 # pipeline as a "verification" sort of stage. For example, the
198 # pipeline should never end up writing to an output file in two
199 # places (I think). The pipeline should also never end up writing
200 # to an output file that is an input.
201 #
202 # This is intended to just be a "verify" step, not a functionality
203 # step. It should catch things like the driver driver not
204 # preventing -save-temps, but it shouldn't change behavior (so we
205 # can turn it off in Release-Asserts builds).
206
207 # Print in -### syntax.
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000208 hasHashHashHash = args.getLastArg(self.parser.hashHashHashOption)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000209 if hasHashHashHash:
210 self.claim(hasHashHashHash)
211 for j in jobs.iterjobs():
212 if isinstance(j, Jobs.Command):
Daniel Dunbard87b6ec2009-01-12 19:36:35 +0000213 print >>sys.stderr, ' "%s"' % '" "'.join(j.getArgv())
Daniel Dunbar378530c2009-01-05 19:53:30 +0000214 elif isinstance(j, Jobs.PipedJob):
215 for c in j.commands:
Daniel Dunbard87b6ec2009-01-12 19:36:35 +0000216 print >>sys.stderr, ' "%s" %c' % ('" "'.join(c.getArgv()),
217 "| "[c is j.commands[-1]])
Daniel Dunbar378530c2009-01-05 19:53:30 +0000218 elif not isinstance(j, JobList):
219 raise ValueError,'Encountered unknown job.'
220 sys.exit(0)
221
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000222 vArg = args.getLastArg(self.parser.vOption)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000223 for j in jobs.iterjobs():
224 if isinstance(j, Jobs.Command):
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000225 if vArg or self.cccEcho:
226 print >>sys.stderr, ' '.join(map(str,j.getArgv()))
Anders Carlsson76613bf2009-01-18 02:54:17 +0000227 sys.stderr.flush()
Daniel Dunbara95c6392009-02-19 22:59:57 +0000228 p = self.startSubprocess(j.getArgv(), j.executable)
229 res = p.wait()
Daniel Dunbar378530c2009-01-05 19:53:30 +0000230 if res:
231 sys.exit(res)
Daniel Dunbara95c6392009-02-19 22:59:57 +0000232
Daniel Dunbar378530c2009-01-05 19:53:30 +0000233 elif isinstance(j, Jobs.PipedJob):
Daniel Dunbare19ed8e2009-01-17 02:02:35 +0000234 procs = []
235 for sj in j.commands:
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000236 if vArg or self.cccEcho:
237 print >> sys.stderr, ' '.join(map(str,sj.getArgv()))
Daniel Dunbare19ed8e2009-01-17 02:02:35 +0000238 sys.stdout.flush()
239
240 if not procs:
241 stdin = None
242 else:
243 stdin = procs[-1].stdout
244 if sj is j.commands[-1]:
245 stdout = None
246 else:
247 stdout = subprocess.PIPE
Daniel Dunbara95c6392009-02-19 22:59:57 +0000248
249 procs.append(self.startSubprocess(sj.getArgv(), sj.executable,
250 stdin=stdin,
251 stdout=stdout))
252
Daniel Dunbare19ed8e2009-01-17 02:02:35 +0000253 for proc in procs:
254 res = proc.wait()
255 if res:
256 sys.exit(res)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000257 else:
258 raise ValueError,'Encountered unknown job.'
259
Daniel Dunbara95c6392009-02-19 22:59:57 +0000260 def startSubprocess(self, argv, executable, **kwargs):
261 try:
262 return subprocess.Popen(argv, executable=executable, **kwargs)
263 except OSError, e:
264 self.warning("error trying to exec '%s': %s" %
265 (executable, e.args[1]))
266 sys.exit(1)
267
Daniel Dunbar378530c2009-01-05 19:53:30 +0000268 def claim(self, option):
269 # FIXME: Move to OptionList once introduced and implement.
270 pass
271
272 def warning(self, message):
Daniel Dunbara0026f22009-01-16 23:12:12 +0000273 print >>sys.stderr,'%s: %s' % (self.driverName, message)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000274
Daniel Dunbar74727872009-01-06 01:35:44 +0000275 def printOptions(self, args):
276 for i,arg in enumerate(args):
Daniel Dunbar74727872009-01-06 01:35:44 +0000277 if isinstance(arg, Arguments.MultipleValuesArg):
278 values = list(args.getValues(arg))
279 elif isinstance(arg, Arguments.ValueArg):
280 values = [args.getValue(arg)]
281 elif isinstance(arg, Arguments.JoinedAndSeparateValuesArg):
282 values = [args.getJoinedValue(arg), args.getSeparateValue(arg)]
Daniel Dunbar378530c2009-01-05 19:53:30 +0000283 else:
284 values = []
Daniel Dunbar927a5092009-01-06 02:30:10 +0000285 print 'Option %d - Name: "%s", Values: {%s}' % (i, arg.opt.name,
Daniel Dunbar378530c2009-01-05 19:53:30 +0000286 ', '.join(['"%s"' % v
287 for v in values]))
288
Daniel Dunbar74727872009-01-06 01:35:44 +0000289 def printPhases(self, phases, args):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000290 def printPhase(p, f, steps, arch=None):
291 if p in steps:
292 return steps[p]
293 elif isinstance(p, Phases.BindArchAction):
294 for kid in p.inputs:
295 printPhase(kid, f, steps, p.arch)
296 steps[p] = len(steps)
297 return
298
299 if isinstance(p, Phases.InputAction):
300 phaseName = 'input'
Daniel Dunbar74727872009-01-06 01:35:44 +0000301 inputStr = '"%s"' % args.getValue(p.filename)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000302 else:
303 phaseName = p.phase.name
304 inputs = [printPhase(i, f, steps, arch)
305 for i in p.inputs]
306 inputStr = '{%s}' % ', '.join(map(str, inputs))
307 if arch is not None:
Daniel Dunbar74727872009-01-06 01:35:44 +0000308 phaseName += '-' + args.getValue(arch)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000309 steps[p] = index = len(steps)
310 print "%d: %s, %s, %s" % (index,phaseName,inputStr,p.type.name)
311 return index
312 steps = {}
313 for phase in phases:
314 printPhase(phase, sys.stdout, steps)
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000315
316 def printVersion(self):
317 # FIXME: Print default target triple.
Mike Stump406abf62009-02-11 01:01:17 +0000318 vers = '$HeadURL$'
319 vers = vers.split('/tools/ccc')[0]
Mike Stump6bf3bd42009-02-11 01:11:36 +0000320 vers = vers.split('/clang/tools/clang')[0]
Mike Stump406abf62009-02-11 01:01:17 +0000321 vers = ' (' + vers[10:] + ')'
322 print >>sys.stderr,'ccc version 1.0' + vers
Daniel Dunbar378530c2009-01-05 19:53:30 +0000323
Daniel Dunbar74727872009-01-06 01:35:44 +0000324 def handleImmediateOptions(self, args):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000325 # FIXME: Some driver Arguments are consumed right off the bat,
326 # like -dumpversion. Currently the gcc-dd handles these
327 # poorly, so we should be ok handling them upfront instead of
328 # after driver-driver level dispatching.
329 #
330 # FIXME: The actual order of these options in gcc is all over the
331 # place. The -dump ones seem to be first and in specification
332 # order, but there are other levels of precedence. For example,
333 # -print-search-dirs is evaluated before -print-prog-name=,
334 # regardless of order (and the last instance of -print-prog-name=
335 # wins verse itself).
336 #
337 # FIXME: Do we want to report "argument unused" type errors in the
338 # presence of things like -dumpmachine and -print-search-dirs?
339 # Probably not.
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000340 if (args.getLastArg(self.parser.vOption) or
341 args.getLastArg(self.parser.hashHashHashOption)):
342 self.printVersion()
343 self.suppressMissingInputWarning = True
344
Daniel Dunbarf677a602009-01-21 02:03:52 +0000345 arg = (args.getLastArg(self.parser.dumpmachineOption) or
346 args.getLastArg(self.parser.dumpversionOption) or
347 args.getLastArg(self.parser.printSearchDirsOption))
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000348 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000349 raise NotImplementedError('%s unsupported' % arg.opt.name)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000350
Daniel Dunbarf677a602009-01-21 02:03:52 +0000351 arg = (args.getLastArg(self.parser.dumpspecsOption) or
352 args.getLastArg(self.parser.printMultiDirectoryOption) or
Daniel Dunbar73fb9072009-01-23 02:00:46 +0000353 args.getLastArg(self.parser.printMultiOsDirectoryOption) or
Daniel Dunbarf677a602009-01-21 02:03:52 +0000354 args.getLastArg(self.parser.printMultiLibOption))
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000355 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000356 raise Arguments.InvalidArgumentsError('%s unsupported by this driver' % arg.opt.name)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000357
358 arg = args.getLastArg(self.parser.printFileNameOption)
359 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000360 print self.getFilePath(args.getValue(arg))
361 sys.exit(0)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000362
363 arg = args.getLastArg(self.parser.printProgNameOption)
364 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000365 print self.getProgramPath(args.getValue(arg))
366 sys.exit(0)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000367
Daniel Dunbarf677a602009-01-21 02:03:52 +0000368 arg = args.getLastArg(self.parser.printLibgccFileNameOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000369 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000370 print self.getFilePath('libgcc.a')
371 sys.exit(0)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000372
Daniel Dunbar74727872009-01-06 01:35:44 +0000373 def buildNormalPipeline(self, args):
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000374 hasAnalyze = args.getLastArg(self.parser.analyzeOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000375 hasCombine = args.getLastArg(self.parser.combineOption)
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000376 hasEmitLLVM = args.getLastArg(self.parser.emitLLVMOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000377 hasSyntaxOnly = args.getLastArg(self.parser.syntaxOnlyOption)
378 hasDashC = args.getLastArg(self.parser.cOption)
379 hasDashE = args.getLastArg(self.parser.EOption)
380 hasDashS = args.getLastArg(self.parser.SOption)
Daniel Dunbar445c46d2009-01-20 01:53:54 +0000381 hasDashM = args.getLastArg(self.parser.MOption)
382 hasDashMM = args.getLastArg(self.parser.MMOption)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000383
384 inputType = None
385 inputTypeOpt = None
386 inputs = []
387 for a in args:
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000388 if a.opt is self.parser.inputOption:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000389 inputValue = args.getValue(a)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000390 if inputType is None:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000391 base,ext = os.path.splitext(inputValue)
Daniel Dunbar6dbfa662009-02-05 23:44:44 +0000392 # stdin is handled specially.
393 if inputValue == '-':
394 if args.getLastArg(self.parser.EOption):
395 # Treat as a C input needing preprocessing
396 # (or Obj-C if over-ridden below).
397 klass = Types.CType
398 else:
399 raise Arguments.InvalidArgumentsError("-E or -x required when input is from standard input")
400 elif ext and ext in Types.kTypeSuffixMap:
Daniel Dunbar378530c2009-01-05 19:53:30 +0000401 klass = Types.kTypeSuffixMap[ext]
402 else:
403 # FIXME: Its not clear why we shouldn't just
404 # revert to unknown. I think this is more likely a
405 # bug / unintended behavior in gcc. Not very
406 # important though.
407 klass = Types.ObjectType
Daniel Dunbar6dbfa662009-02-05 23:44:44 +0000408
409 # -ObjC and -ObjC++ over-ride the default
410 # language, but only for "source files". We
411 # just treat everything that isn't a linker
412 # input as a source file.
413 #
414 # FIXME: Clean this up if we move the phase
415 # sequence into the type.
416 if klass is not Types.ObjectType:
417 if args.getLastArg(self.parser.ObjCOption):
418 klass = Types.ObjCType
419 elif args.getLastArg(self.parser.ObjCXXOption):
420 klass = Types.ObjCType
Daniel Dunbar378530c2009-01-05 19:53:30 +0000421 else:
422 assert inputTypeOpt is not None
423 self.claim(inputTypeOpt)
424 klass = inputType
Daniel Dunbara0026f22009-01-16 23:12:12 +0000425
426 # Check that the file exists. It isn't clear this is
427 # worth doing, since the tool presumably does this
428 # anyway, and this just adds an extra stat to the
429 # equation, but this is gcc compatible.
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000430 if inputValue != '-' and not os.path.exists(inputValue):
Daniel Dunbara0026f22009-01-16 23:12:12 +0000431 self.warning("%s: No such file or directory" % inputValue)
432 else:
433 inputs.append((klass, a))
Daniel Dunbarfc2ad022009-01-12 03:33:58 +0000434 elif a.opt.isLinkerInput:
435 # Treat as a linker input.
Daniel Dunbar927a5092009-01-06 02:30:10 +0000436 #
437 # FIXME: This might not be good enough. We may
438 # need to introduce another type for this case, so
439 # that other code which needs to know the inputs
440 # handles this properly. Best not to try and lipo
441 # this, for example.
442 inputs.append((Types.ObjectType, a))
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000443 elif a.opt is self.parser.xOption:
Daniel Dunbar927a5092009-01-06 02:30:10 +0000444 inputTypeOpt = a
445 value = args.getValue(a)
446 if value in Types.kTypeSpecifierMap:
447 inputType = Types.kTypeSpecifierMap[value]
448 else:
449 # FIXME: How are we going to handle diagnostics.
450 self.warning("language %s not recognized" % value)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000451
Daniel Dunbar927a5092009-01-06 02:30:10 +0000452 # FIXME: Its not clear why we shouldn't just
453 # revert to unknown. I think this is more likely a
454 # bug / unintended behavior in gcc. Not very
455 # important though.
Daniel Dunbar6cb42052009-01-13 21:07:43 +0000456 inputType = Types.ObjectType
Daniel Dunbar378530c2009-01-05 19:53:30 +0000457
458 # We claim things here so that options for which we silently allow
459 # override only ever claim the used option.
460 if hasCombine:
461 self.claim(hasCombine)
462
463 finalPhase = Phases.Phase.eOrderPostAssemble
464 finalPhaseOpt = None
465
466 # Determine what compilation mode we are in.
Daniel Dunbar445c46d2009-01-20 01:53:54 +0000467 if hasDashE or hasDashM or hasDashMM:
Daniel Dunbar378530c2009-01-05 19:53:30 +0000468 finalPhase = Phases.Phase.eOrderPreprocess
469 finalPhaseOpt = hasDashE
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000470 elif (hasAnalyze or hasSyntaxOnly or
471 hasEmitLLVM or hasDashS):
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000472 finalPhase = Phases.Phase.eOrderCompile
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000473 finalPhaseOpt = (hasAnalyze or hasSyntaxOnly or
474 hasEmitLLVM or hasDashS)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000475 elif hasDashC:
476 finalPhase = Phases.Phase.eOrderAssemble
477 finalPhaseOpt = hasDashC
478
479 if finalPhaseOpt:
480 self.claim(finalPhaseOpt)
481
Daniel Dunbar80e48b72009-01-17 00:53:19 +0000482 # Reject -Z* at the top level for now.
483 arg = args.getLastArg(self.parser.ZOption)
484 if arg:
485 raise Arguments.InvalidArgumentsError("%s: unsupported use of internal gcc option" % ' '.join(args.render(arg)))
486
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000487 if not inputs and not self.suppressMissingInputWarning:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000488 raise Arguments.InvalidArgumentsError("no input files")
Daniel Dunbar378530c2009-01-05 19:53:30 +0000489
490 actions = []
491 linkerInputs = []
492 # FIXME: This is gross.
493 linkPhase = Phases.LinkPhase()
494 for klass,input in inputs:
495 # Figure out what step to start at.
496
497 # FIXME: This should be part of the input class probably?
498 # Altough it doesn't quite fit there either, things like
499 # asm-with-preprocess don't easily fit into a linear scheme.
500
501 # FIXME: I think we are going to end up wanting to just build
502 # a simple FSA which we run the inputs down.
503 sequence = []
504 if klass.preprocess:
505 sequence.append(Phases.PreprocessPhase())
506 if klass == Types.ObjectType:
507 sequence.append(linkPhase)
508 elif klass.onlyAssemble:
509 sequence.extend([Phases.AssemblePhase(),
510 linkPhase])
511 elif klass.onlyPrecompile:
512 sequence.append(Phases.PrecompilePhase())
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000513 elif hasAnalyze:
514 sequence.append(Phases.AnalyzePhase())
515 elif hasSyntaxOnly:
516 sequence.append(Phases.SyntaxOnlyPhase())
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000517 elif hasEmitLLVM:
518 sequence.append(Phases.EmitLLVMPhase())
Daniel Dunbar378530c2009-01-05 19:53:30 +0000519 else:
520 sequence.extend([Phases.CompilePhase(),
521 Phases.AssemblePhase(),
522 linkPhase])
523
524 if sequence[0].order > finalPhase:
525 assert finalPhaseOpt and finalPhaseOpt.opt
526 # FIXME: Explain what type of input file is. Or just match
527 # gcc warning.
Daniel Dunbar74727872009-01-06 01:35:44 +0000528 self.warning("%s: %s input file unused when %s is present" % (args.getValue(input),
Daniel Dunbar378530c2009-01-05 19:53:30 +0000529 sequence[0].name,
530 finalPhaseOpt.opt.name))
531 else:
532 # Build the pipeline for this file.
533
534 current = Phases.InputAction(input, klass)
535 for transition in sequence:
536 # If the current action produces no output, or we are
537 # past what the user requested, we are done.
538 if (current.type is Types.NothingType or
539 transition.order > finalPhase):
540 break
541 else:
542 if isinstance(transition, Phases.PreprocessPhase):
543 assert isinstance(klass.preprocess, Types.InputType)
544 current = Phases.JobAction(transition,
545 [current],
546 klass.preprocess)
547 elif isinstance(transition, Phases.PrecompilePhase):
548 current = Phases.JobAction(transition,
549 [current],
550 Types.PCHType)
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000551 elif isinstance(transition, Phases.AnalyzePhase):
552 output = Types.PlistType
553 current = Phases.JobAction(transition,
554 [current],
555 output)
556 elif isinstance(transition, Phases.SyntaxOnlyPhase):
557 output = Types.NothingType
558 current = Phases.JobAction(transition,
559 [current],
560 output)
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000561 elif isinstance(transition, Phases.EmitLLVMPhase):
562 if hasDashS:
563 output = Types.LLVMAsmType
564 else:
565 output = Types.LLVMBCType
566 current = Phases.JobAction(transition,
567 [current],
568 output)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000569 elif isinstance(transition, Phases.CompilePhase):
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000570 output = Types.AsmTypeNoPP
Daniel Dunbar378530c2009-01-05 19:53:30 +0000571 current = Phases.JobAction(transition,
572 [current],
573 output)
574 elif isinstance(transition, Phases.AssemblePhase):
575 current = Phases.JobAction(transition,
576 [current],
577 Types.ObjectType)
578 elif transition is linkPhase:
579 linkerInputs.append(current)
580 current = None
581 break
582 else:
583 raise RuntimeError,'Unrecognized transition: %s.' % transition
584 pass
585
586 if current is not None:
587 assert not isinstance(current, Phases.InputAction)
588 actions.append(current)
589
590 if linkerInputs:
591 actions.append(Phases.JobAction(linkPhase,
592 linkerInputs,
593 Types.ImageType))
594
595 return actions
596
Daniel Dunbar74727872009-01-06 01:35:44 +0000597 def buildPipeline(self, args):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000598 # FIXME: We need to handle canonicalization of the specified arch.
599
Daniel Dunbar31c80812009-01-20 21:29:14 +0000600 archs = {}
Daniel Dunbar445c46d2009-01-20 01:53:54 +0000601 hasDashM = args.getLastArg(self.parser.MGroup)
Daniel Dunbara33176f2009-01-21 18:49:34 +0000602 hasSaveTemps = args.getLastArg(self.parser.saveTempsOption)
Daniel Dunbar74727872009-01-06 01:35:44 +0000603 for arg in args:
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000604 if arg.opt is self.parser.archOption:
Daniel Dunbar31c80812009-01-20 21:29:14 +0000605 # FIXME: Canonicalize this.
606 archName = args.getValue(arg)
607 archs[archName] = arg
608
609 archs = archs.values()
Daniel Dunbar378530c2009-01-05 19:53:30 +0000610 if not archs:
Daniel Dunbarf3d5ca02009-01-13 04:05:40 +0000611 archs.append(args.makeSeparateArg(self.hostInfo.getArchName(args),
Daniel Dunbar1ba90982009-01-07 18:54:26 +0000612 self.parser.archOption))
Daniel Dunbar378530c2009-01-05 19:53:30 +0000613
Daniel Dunbar74727872009-01-06 01:35:44 +0000614 actions = self.buildNormalPipeline(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000615
616 # FIXME: Use custom exception for this.
617 #
618 # FIXME: We killed off some others but these aren't yet detected in
619 # a functional manner. If we added information to jobs about which
620 # "auxiliary" files they wrote then we could detect the conflict
621 # these cause downstream.
622 if len(archs) > 1:
623 if hasDashM:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000624 raise Arguments.InvalidArgumentsError("Cannot use -M options with multiple arch flags.")
Daniel Dunbar378530c2009-01-05 19:53:30 +0000625 elif hasSaveTemps:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000626 raise Arguments.InvalidArgumentsError("Cannot use -save-temps with multiple arch flags.")
Daniel Dunbar378530c2009-01-05 19:53:30 +0000627
628 # Execute once per arch.
629 finalActions = []
630 for p in actions:
631 # Make sure we can lipo this kind of output. If not (and it
632 # is an actual output) then we disallow, since we can't
633 # create an output file with the right name without
634 # overwriting it. We could remove this oddity by just
635 # changing the output names to include the arch, which would
636 # also fix -save-temps. Compatibility wins for now.
637 #
638 # FIXME: Is this error substantially less useful than
639 # gcc-dd's? The main problem is that "Cannot use compiler
640 # output with multiple arch flags" won't make sense to most
641 # developers.
642 if (len(archs) > 1 and
643 p.type not in (Types.NothingType,Types.ObjectType,Types.ImageType)):
Daniel Dunbara0026f22009-01-16 23:12:12 +0000644 raise Arguments.InvalidArgumentsError('Cannot use %s output with multiple arch flags.' % p.type.name)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000645
646 inputs = []
647 for arch in archs:
648 inputs.append(Phases.BindArchAction(p, arch))
649
650 # Lipo if necessary. We do it this way because we need to set
651 # the arch flag so that -Xarch_ gets rewritten.
652 if len(inputs) == 1 or p.type == Types.NothingType:
653 finalActions.extend(inputs)
654 else:
655 finalActions.append(Phases.JobAction(Phases.LipoPhase(),
656 inputs,
657 p.type))
658
Daniel Dunbar378530c2009-01-05 19:53:30 +0000659 return finalActions
660
Daniel Dunbar74727872009-01-06 01:35:44 +0000661 def bindPhases(self, phases, args):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000662 jobs = Jobs.JobList()
663
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000664 finalOutput = args.getLastArg(self.parser.oOption)
Daniel Dunbara33176f2009-01-21 18:49:34 +0000665 hasSaveTemps = args.getLastArg(self.parser.saveTempsOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000666 hasNoIntegratedCPP = args.getLastArg(self.parser.noIntegratedCPPOption)
Daniel Dunbarf86e98a2009-01-12 09:23:15 +0000667 hasTraditionalCPP = args.getLastArg(self.parser.traditionalCPPOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000668 hasPipe = args.getLastArg(self.parser.pipeOption)
Daniel Dunbarfc2ad022009-01-12 03:33:58 +0000669
Daniel Dunbar378530c2009-01-05 19:53:30 +0000670 # We claim things here so that options for which we silently allow
671 # override only ever claim the used option.
672 if hasPipe:
673 self.claim(hasPipe)
674 # FIXME: Hack, override -pipe till we support it.
Daniel Dunbare19ed8e2009-01-17 02:02:35 +0000675 if hasSaveTemps:
676 self.warning('-pipe ignored because -save-temps specified')
677 hasPipe = None
Daniel Dunbar378530c2009-01-05 19:53:30 +0000678 # Claim these here. Its not completely accurate but any warnings
679 # about these being unused are likely to be noise anyway.
680 if hasSaveTemps:
681 self.claim(hasSaveTemps)
Daniel Dunbarf86e98a2009-01-12 09:23:15 +0000682
683 if hasTraditionalCPP:
684 self.claim(hasTraditionalCPP)
685 elif hasNoIntegratedCPP:
Daniel Dunbar378530c2009-01-05 19:53:30 +0000686 self.claim(hasNoIntegratedCPP)
Daniel Dunbarf86e98a2009-01-12 09:23:15 +0000687
Daniel Dunbare5fb6792009-01-13 06:25:31 +0000688 # FIXME: Move to... somewhere else.
Daniel Dunbar378530c2009-01-05 19:53:30 +0000689 class InputInfo:
690 def __init__(self, source, type, baseInput):
691 self.source = source
692 self.type = type
693 self.baseInput = baseInput
694
695 def __repr__(self):
696 return '%s(%r, %r, %r)' % (self.__class__.__name__,
697 self.source, self.type, self.baseInput)
Daniel Dunbare5fb6792009-01-13 06:25:31 +0000698
699 def isOriginalInput(self):
700 return self.source is self.baseInput
Daniel Dunbar378530c2009-01-05 19:53:30 +0000701
Daniel Dunbarb3492762009-01-13 18:51:26 +0000702 def createJobs(tc, phase,
703 canAcceptPipe=False, atTopLevel=False, arch=None,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000704 tcArgs=None, linkingOutput=None):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000705 if isinstance(phase, Phases.InputAction):
706 return InputInfo(phase.filename, phase.type, phase.filename)
707 elif isinstance(phase, Phases.BindArchAction):
Daniel Dunbar74727872009-01-06 01:35:44 +0000708 archName = args.getValue(phase.arch)
Daniel Dunbar758cf642009-01-11 22:06:22 +0000709 tc = self.hostInfo.getToolChainForArch(archName)
Daniel Dunbarb3492762009-01-13 18:51:26 +0000710 return createJobs(tc, phase.inputs[0],
711 canAcceptPipe, atTopLevel, phase.arch,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000712 None, linkingOutput)
Daniel Dunbarb3492762009-01-13 18:51:26 +0000713
714 if tcArgs is None:
715 tcArgs = tc.translateArgs(args, arch)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000716
717 assert isinstance(phase, Phases.JobAction)
Daniel Dunbar758cf642009-01-11 22:06:22 +0000718 tool = tc.selectTool(phase)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000719
720 # See if we should use an integrated CPP. We only use an
721 # integrated cpp when we have exactly one input, since this is
722 # the only use case we care about.
723 useIntegratedCPP = False
724 inputList = phase.inputs
725 if (not hasNoIntegratedCPP and
Daniel Dunbarf86e98a2009-01-12 09:23:15 +0000726 not hasTraditionalCPP and
Daniel Dunbar378530c2009-01-05 19:53:30 +0000727 not hasSaveTemps and
728 tool.hasIntegratedCPP()):
729 if (len(phase.inputs) == 1 and
Daniel Dunbar7d494092009-01-20 00:47:24 +0000730 isinstance(phase.inputs[0], Phases.JobAction) and
Daniel Dunbar378530c2009-01-05 19:53:30 +0000731 isinstance(phase.inputs[0].phase, Phases.PreprocessPhase)):
732 useIntegratedCPP = True
733 inputList = phase.inputs[0].inputs
734
735 # Only try to use pipes when exactly one input.
Daniel Dunbar1e46f552009-01-22 23:19:32 +0000736 attemptToPipeInput = len(inputList) == 1 and tool.acceptsPipedInput()
737 inputs = [createJobs(tc, p, attemptToPipeInput, False,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000738 arch, tcArgs, linkingOutput)
Daniel Dunbar758cf642009-01-11 22:06:22 +0000739 for p in inputList]
Daniel Dunbar378530c2009-01-05 19:53:30 +0000740
741 # Determine if we should output to a pipe.
742 canOutputToPipe = canAcceptPipe and tool.canPipeOutput()
743 outputToPipe = False
744 if canOutputToPipe:
745 # Some things default to writing to a pipe if the final
746 # phase and there was no user override.
747 #
748 # FIXME: What is the best way to handle this?
Daniel Dunbar1b391272009-01-18 21:35:24 +0000749 if atTopLevel:
750 if (isinstance(phase.phase, Phases.PreprocessPhase) and
751 not finalOutput):
752 outputToPipe = True
Daniel Dunbar378530c2009-01-05 19:53:30 +0000753 elif hasPipe:
754 outputToPipe = True
755
756 # Figure out where to put the job (pipes).
757 jobList = jobs
Daniel Dunbar1e46f552009-01-22 23:19:32 +0000758 if isinstance(inputs[0].source, Jobs.PipedJob):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000759 jobList = inputs[0].source
Daniel Dunbar31c80812009-01-20 21:29:14 +0000760
Daniel Dunbar378530c2009-01-05 19:53:30 +0000761 baseInput = inputs[0].baseInput
Daniel Dunbard9b7a742009-01-21 00:05:15 +0000762 output,jobList = self.getOutputName(phase, outputToPipe, jobs, jobList, baseInput,
763 args, atTopLevel, hasSaveTemps, finalOutput)
Daniel Dunbarb421dba2009-01-07 18:40:45 +0000764 tool.constructJob(phase, arch, jobList, inputs, output, phase.type,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000765 tcArgs, linkingOutput)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000766
767 return InputInfo(output, phase.type, baseInput)
768
769 # It is an error to provide a -o option if we are making multiple
770 # output files.
771 if finalOutput and len([a for a in phases if a.type is not Types.NothingType]) > 1:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000772 raise Arguments.InvalidArgumentsError("cannot specify -o when generating multiple files")
Daniel Dunbar378530c2009-01-05 19:53:30 +0000773
774 for phase in phases:
Daniel Dunbar31c80812009-01-20 21:29:14 +0000775 # If we are linking an image for multiple archs then the
776 # linker wants -arch_multiple and -final_output <final image
777 # name>. Unfortunately this requires some gross contortions.
778 #
779 # FIXME: This is a hack; find a cleaner way to integrate this
780 # into the process.
781 linkingOutput = None
782 if (isinstance(phase, Phases.JobAction) and
783 isinstance(phase.phase, Phases.LipoPhase)):
784 finalOutput = args.getLastArg(self.parser.oOption)
785 if finalOutput:
786 linkingOutput = finalOutput
787 else:
788 linkingOutput = args.makeSeparateArg('a.out',
789 self.parser.oOption)
790
Daniel Dunbarb3492762009-01-13 18:51:26 +0000791 createJobs(self.toolChain, phase,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000792 canAcceptPipe=True, atTopLevel=True,
793 linkingOutput=linkingOutput)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000794
795 return jobs
Daniel Dunbar31c80812009-01-20 21:29:14 +0000796
797 def getOutputName(self, phase, outputToPipe, jobs, jobList, baseInput,
798 args, atTopLevel, hasSaveTemps, finalOutput):
799 # Figure out where to put the output.
800 if phase.type == Types.NothingType:
801 output = None
802 elif outputToPipe:
803 if isinstance(jobList, Jobs.PipedJob):
804 output = jobList
805 else:
806 jobList = output = Jobs.PipedJob([])
807 jobs.addJob(output)
808 else:
809 # Figure out what the derived output location would be.
810 #
811 # FIXME: gcc has some special case in here so that it doesn't
812 # create output files if they would conflict with an input.
813 if phase.type is Types.ImageType:
814 namedOutput = "a.out"
815 else:
Daniel Dunbar31c80812009-01-20 21:29:14 +0000816 assert phase.type.tempSuffix is not None
Daniel Dunbar0e8c48c2009-02-13 17:42:34 +0000817 inputName = args.getValue(baseInput)
818 if phase.type.appendSuffix:
819 namedOutput = inputName + '.' + phase.type.tempSuffix
820 else:
821 base,_ = os.path.splitext(inputName)
822 namedOutput = base + '.' + phase.type.tempSuffix
Daniel Dunbar31c80812009-01-20 21:29:14 +0000823
824 # Output to user requested destination?
825 if atTopLevel and finalOutput:
826 output = finalOutput
827 # Contruct a named destination?
828 elif atTopLevel or hasSaveTemps:
829 # As an annoying special case, pch generation
830 # doesn't strip the pathname.
831 if phase.type is Types.PCHType:
832 outputName = namedOutput
833 else:
834 outputName = os.path.basename(namedOutput)
835 output = args.makeSeparateArg(outputName,
836 self.parser.oOption)
837 else:
838 # Output to temp file...
839 fd,filename = tempfile.mkstemp(suffix='.'+phase.type.tempSuffix)
840 output = args.makeSeparateArg(filename,
841 self.parser.oOption)
Daniel Dunbard9b7a742009-01-21 00:05:15 +0000842 return output,jobList