| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 1 | import os | 
 | 2 | import sys | 
 | 3 | import tempfile | 
 | 4 | from pprint import pprint | 
 | 5 |  | 
 | 6 | ### | 
 | 7 |  | 
 | 8 | import Arguments | 
 | 9 | import Jobs | 
 | 10 | import Phases | 
 | 11 | import Tools | 
 | 12 | import Types | 
 | 13 | import Util | 
 | 14 |  | 
 | 15 | # FIXME: Clean up naming of options and arguments. Decide whether to | 
 | 16 | # rename Option and be consistent about use of Option/Arg. | 
 | 17 |  | 
 | 18 | #### | 
 | 19 |  | 
 | 20 | class MissingArgumentError(ValueError): | 
 | 21 |     """MissingArgumentError - An option required an argument but none | 
 | 22 |     was given.""" | 
 | 23 |  | 
 | 24 | ### | 
 | 25 |  | 
 | 26 | class Driver(object): | 
 | 27 |     def __init__(self): | 
 | 28 |         self.parser = Arguments.createOptionParser() | 
 | 29 |  | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 30 |     def run(self, argv): | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 31 |         # FIXME: Things to support from environment: GCC_EXEC_PREFIX, | 
 | 32 |         # COMPILER_PATH, LIBRARY_PATH, LPATH, CC_PRINT_OPTIONS, | 
 | 33 |         # QA_OVERRIDE_GCC3_OPTIONS, ...? | 
 | 34 |  | 
 | 35 |         # FIXME: -V and -b processing | 
 | 36 |  | 
 | 37 |         # Handle some special -ccc- options used for testing which are | 
 | 38 |         # only allowed at the beginning of the command line. | 
 | 39 |         cccPrintOptions = False | 
 | 40 |         cccPrintPhases = False | 
 | 41 |         cccUseDriverDriver = True | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 42 |         while argv and argv[0].startswith('-ccc-'): | 
 | 43 |             opt,argv = argv[0][5:],argv[1:] | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 44 |  | 
 | 45 |             if opt == 'print-options': | 
 | 46 |                 cccPrintOptions = True | 
 | 47 |             elif opt == 'print-phases': | 
 | 48 |                 cccPrintPhases = True | 
 | 49 |             elif opt == 'no-driver-driver': | 
 | 50 |                 # FIXME: Remove this once we have some way of being a | 
 | 51 |                 # cross compiler driver (cross driver compiler? compiler | 
 | 52 |                 # cross driver? etc.). | 
 | 53 |                 cccUseDriverDriver = False | 
 | 54 |             else: | 
 | 55 |                 raise ValueError,"Invalid ccc option: %r" % cccPrintOptions | 
 | 56 |  | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 57 |         args = self.parser.parseArgs(argv) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 58 |  | 
 | 59 |         # FIXME: Ho hum I have just realized -Xarch_ is broken. We really | 
 | 60 |         # need to reparse the Arguments after they have been expanded by | 
 | 61 |         # -Xarch. How is this going to work? | 
 | 62 |         # | 
 | 63 |         # Scratch that, we aren't going to do that; it really disrupts the | 
 | 64 |         # organization, doesn't consistently work with gcc-dd, and is | 
 | 65 |         # confusing. Instead we are going to enforce that -Xarch_ is only | 
 | 66 |         # used with options which do not alter the driver behavior. Let's | 
 | 67 |         # hope this is ok, because the current architecture is a little | 
 | 68 |         # tied to it. | 
 | 69 |  | 
 | 70 |         if cccPrintOptions: | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 71 |             self.printOptions(args) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 72 |             sys.exit(0) | 
 | 73 |  | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 74 |         self.handleImmediateOptions(args) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 75 |  | 
 | 76 |         if cccUseDriverDriver: | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 77 |             phases = self.buildPipeline(args) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 78 |         else: | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 79 |             phases = self.buildNormalPipeline(args) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 80 |  | 
 | 81 |         if cccPrintPhases: | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 82 |             self.printPhases(phases, args) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 83 |             sys.exit(0) | 
 | 84 |  | 
 | 85 |         if 0: | 
 | 86 |             print Util.pprint(phases) | 
 | 87 |  | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 88 |         jobs = self.bindPhases(phases, args) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 89 |  | 
 | 90 |         # FIXME: We should provide some basic sanity checking of the | 
 | 91 |         # pipeline as a "verification" sort of stage. For example, the | 
 | 92 |         # pipeline should never end up writing to an output file in two | 
 | 93 |         # places (I think). The pipeline should also never end up writing | 
 | 94 |         # to an output file that is an input. | 
 | 95 |         # | 
 | 96 |         # This is intended to just be a "verify" step, not a functionality | 
 | 97 |         # step. It should catch things like the driver driver not | 
 | 98 |         # preventing -save-temps, but it shouldn't change behavior (so we | 
 | 99 |         # can turn it off in Release-Asserts builds). | 
 | 100 |  | 
 | 101 |         # Print in -### syntax. | 
 | 102 |         hasHashHashHash = None | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 103 |         for arg in args: | 
| Daniel Dunbar | 5039f21 | 2009-01-06 02:30:10 +0000 | [diff] [blame^] | 104 |             if arg.opt.name == '-###': | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 105 |                 hasHashHashHash = arg | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 106 |  | 
 | 107 |         if hasHashHashHash: | 
 | 108 |             self.claim(hasHashHashHash) | 
 | 109 |             for j in jobs.iterjobs(): | 
 | 110 |                 if isinstance(j, Jobs.Command): | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 111 |                     print '"%s"' % '" "'.join(j.render(argv)) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 112 |                 elif isinstance(j, Jobs.PipedJob): | 
 | 113 |                     for c in j.commands: | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 114 |                         print '"%s" %c' % ('" "'.join(c.render(argv)), | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 115 |                                            "| "[c is j.commands[-1]]) | 
 | 116 |                 elif not isinstance(j, JobList): | 
 | 117 |                     raise ValueError,'Encountered unknown job.' | 
 | 118 |             sys.exit(0) | 
 | 119 |  | 
 | 120 |         for j in jobs.iterjobs(): | 
 | 121 |             if isinstance(j, Jobs.Command): | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 122 |                 cmd_args = j.render(argv) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 123 |                 res = os.spawnvp(os.P_WAIT, cmd_args[0], cmd_args) | 
 | 124 |                 if res: | 
 | 125 |                     sys.exit(res) | 
 | 126 |             elif isinstance(j, Jobs.PipedJob): | 
 | 127 |                 raise NotImplementedError,"Piped jobs aren't implemented yet." | 
 | 128 |             else: | 
 | 129 |                 raise ValueError,'Encountered unknown job.' | 
 | 130 |  | 
 | 131 |     def claim(self, option): | 
 | 132 |         # FIXME: Move to OptionList once introduced and implement. | 
 | 133 |         pass | 
 | 134 |  | 
 | 135 |     def warning(self, message): | 
 | 136 |         print >>sys.stderr,'%s: %s' % (sys.argv[0], message) | 
 | 137 |  | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 138 |     def printOptions(self, args): | 
 | 139 |         for i,arg in enumerate(args): | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 140 |             if isinstance(arg, Arguments.MultipleValuesArg): | 
 | 141 |                 values = list(args.getValues(arg)) | 
 | 142 |             elif isinstance(arg, Arguments.ValueArg): | 
 | 143 |                 values = [args.getValue(arg)] | 
 | 144 |             elif isinstance(arg, Arguments.JoinedAndSeparateValuesArg): | 
 | 145 |                 values = [args.getJoinedValue(arg), args.getSeparateValue(arg)] | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 146 |             else: | 
 | 147 |                 values = [] | 
| Daniel Dunbar | 5039f21 | 2009-01-06 02:30:10 +0000 | [diff] [blame^] | 148 |             print 'Option %d - Name: "%s", Values: {%s}' % (i, arg.opt.name,  | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 149 |                                                             ', '.join(['"%s"' % v  | 
 | 150 |                                                                        for v in values])) | 
 | 151 |  | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 152 |     def printPhases(self, phases, args): | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 153 |         def printPhase(p, f, steps, arch=None): | 
 | 154 |             if p in steps: | 
 | 155 |                 return steps[p] | 
 | 156 |             elif isinstance(p, Phases.BindArchAction): | 
 | 157 |                 for kid in p.inputs: | 
 | 158 |                     printPhase(kid, f, steps, p.arch) | 
 | 159 |                 steps[p] = len(steps) | 
 | 160 |                 return | 
 | 161 |  | 
 | 162 |             if isinstance(p, Phases.InputAction): | 
 | 163 |                 phaseName = 'input' | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 164 |                 inputStr = '"%s"' % args.getValue(p.filename) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 165 |             else: | 
 | 166 |                 phaseName = p.phase.name | 
 | 167 |                 inputs = [printPhase(i, f, steps, arch)  | 
 | 168 |                           for i in p.inputs] | 
 | 169 |                 inputStr = '{%s}' % ', '.join(map(str, inputs)) | 
 | 170 |             if arch is not None: | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 171 |                 phaseName += '-' + args.getValue(arch) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 172 |             steps[p] = index = len(steps) | 
 | 173 |             print "%d: %s, %s, %s" % (index,phaseName,inputStr,p.type.name) | 
 | 174 |             return index | 
 | 175 |         steps = {} | 
 | 176 |         for phase in phases: | 
 | 177 |             printPhase(phase, sys.stdout, steps) | 
 | 178 |  | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 179 |     def handleImmediateOptions(self, args): | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 180 |         # FIXME: Some driver Arguments are consumed right off the bat, | 
 | 181 |         # like -dumpversion. Currently the gcc-dd handles these | 
 | 182 |         # poorly, so we should be ok handling them upfront instead of | 
 | 183 |         # after driver-driver level dispatching. | 
 | 184 |         # | 
 | 185 |         # FIXME: The actual order of these options in gcc is all over the | 
 | 186 |         # place. The -dump ones seem to be first and in specification | 
 | 187 |         # order, but there are other levels of precedence. For example, | 
 | 188 |         # -print-search-dirs is evaluated before -print-prog-name=, | 
 | 189 |         # regardless of order (and the last instance of -print-prog-name= | 
 | 190 |         # wins verse itself). | 
 | 191 |         # | 
 | 192 |         # FIXME: Do we want to report "argument unused" type errors in the | 
 | 193 |         # presence of things like -dumpmachine and -print-search-dirs? | 
 | 194 |         # Probably not. | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 195 |         for arg in args: | 
| Daniel Dunbar | 5039f21 | 2009-01-06 02:30:10 +0000 | [diff] [blame^] | 196 |             if arg.opt.name == '-dumpmachine': | 
 | 197 |                 print 'FIXME: %s' % arg.opt.name | 
 | 198 |                 sys.exit(1) | 
 | 199 |             elif arg.opt.name == '-dumpspecs': | 
 | 200 |                 print 'FIXME: %s' % arg.opt.name | 
 | 201 |                 sys.exit(1) | 
 | 202 |             elif arg.opt.name == '-dumpversion': | 
 | 203 |                 print 'FIXME: %s' % arg.opt.name | 
 | 204 |                 sys.exit(1) | 
 | 205 |             elif arg.opt.name == '-print-file-name=': | 
 | 206 |                 print 'FIXME: %s' % arg.opt.name | 
 | 207 |                 sys.exit(1) | 
 | 208 |             elif arg.opt.name == '-print-multi-directory': | 
 | 209 |                 print 'FIXME: %s' % arg.opt.name | 
 | 210 |                 sys.exit(1) | 
 | 211 |             elif arg.opt.name == '-print-multi-lib': | 
 | 212 |                 print 'FIXME: %s' % arg.opt.name | 
 | 213 |                 sys.exit(1) | 
 | 214 |             elif arg.opt.name == '-print-prog-name=': | 
 | 215 |                 print 'FIXME: %s' % arg.opt.name | 
 | 216 |                 sys.exit(1) | 
 | 217 |             elif arg.opt.name == '-print-libgcc-file-name': | 
 | 218 |                 print 'FIXME: %s' % arg.opt.name | 
 | 219 |                 sys.exit(1) | 
 | 220 |             elif arg.opt.name == '-print-search-dirs': | 
 | 221 |                 print 'FIXME: %s' % arg.opt.name | 
 | 222 |                 sys.exit(1) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 223 |  | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 224 |     def buildNormalPipeline(self, args): | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 225 |         hasCombine = None | 
 | 226 |         hasSyntaxOnly = None | 
 | 227 |         hasDashC = hasDashE = hasDashS = None | 
 | 228 |  | 
 | 229 |         inputType = None | 
 | 230 |         inputTypeOpt = None | 
 | 231 |         inputs = [] | 
 | 232 |         for a in args: | 
| Daniel Dunbar | 5039f21 | 2009-01-06 02:30:10 +0000 | [diff] [blame^] | 233 |             if a.opt.name == '<input>': | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 234 |                 if inputType is None: | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 235 |                     base,ext = os.path.splitext(args.getValue(a)) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 236 |                     if ext and ext in Types.kTypeSuffixMap: | 
 | 237 |                         klass = Types.kTypeSuffixMap[ext] | 
 | 238 |                     else: | 
 | 239 |                         # FIXME: Its not clear why we shouldn't just | 
 | 240 |                         # revert to unknown. I think this is more likely a | 
 | 241 |                         # bug / unintended behavior in gcc. Not very | 
 | 242 |                         # important though. | 
 | 243 |                         klass = Types.ObjectType | 
 | 244 |                 else: | 
 | 245 |                     assert inputTypeOpt is not None | 
 | 246 |                     self.claim(inputTypeOpt) | 
 | 247 |                     klass = inputType | 
 | 248 |                 inputs.append((klass, a)) | 
| Daniel Dunbar | 5039f21 | 2009-01-06 02:30:10 +0000 | [diff] [blame^] | 249 |             elif a.opt.name == '-E': | 
 | 250 |                 hasDashE = a | 
 | 251 |             elif a.opt.name == '-S': | 
 | 252 |                 hasDashS = a | 
 | 253 |             elif a.opt.name == '-c': | 
 | 254 |                 hasDashC = a | 
 | 255 |             elif a.opt.name == '-fsyntax-only': | 
 | 256 |                 hasSyntaxOnly = a | 
 | 257 |             elif a.opt.name == '-combine': | 
 | 258 |                 hasCombine = a | 
 | 259 |             elif a.opt.name == '-filelist': | 
 | 260 |                 # Treat as a linker input. | 
 | 261 |                 # | 
 | 262 |                 # FIXME: This might not be good enough. We may | 
 | 263 |                 # need to introduce another type for this case, so | 
 | 264 |                 # that other code which needs to know the inputs | 
 | 265 |                 # handles this properly. Best not to try and lipo | 
 | 266 |                 # this, for example. | 
 | 267 |                 inputs.append((Types.ObjectType, a)) | 
 | 268 |             elif a.opt.name == '-x': | 
 | 269 |                 self.claim(a) | 
 | 270 |                 inputTypeOpt = a | 
 | 271 |                 value = args.getValue(a) | 
 | 272 |                 if value in Types.kTypeSpecifierMap: | 
 | 273 |                     inputType = Types.kTypeSpecifierMap[value] | 
 | 274 |                 else: | 
 | 275 |                     # FIXME: How are we going to handle diagnostics. | 
 | 276 |                     self.warning("language %s not recognized" % value) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 277 |  | 
| Daniel Dunbar | 5039f21 | 2009-01-06 02:30:10 +0000 | [diff] [blame^] | 278 |                     # FIXME: Its not clear why we shouldn't just | 
 | 279 |                     # revert to unknown. I think this is more likely a | 
 | 280 |                     # bug / unintended behavior in gcc. Not very | 
 | 281 |                     # important though. | 
 | 282 |                     inputType = ObjectType | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 283 |  | 
 | 284 |         # We claim things here so that options for which we silently allow | 
 | 285 |         # override only ever claim the used option. | 
 | 286 |         if hasCombine: | 
 | 287 |             self.claim(hasCombine) | 
 | 288 |  | 
 | 289 |         finalPhase = Phases.Phase.eOrderPostAssemble | 
 | 290 |         finalPhaseOpt = None | 
 | 291 |  | 
 | 292 |         # Determine what compilation mode we are in. | 
 | 293 |         if hasDashE: | 
 | 294 |             finalPhase = Phases.Phase.eOrderPreprocess | 
 | 295 |             finalPhaseOpt = hasDashE | 
 | 296 |         elif hasSyntaxOnly: | 
 | 297 |             finalPhase = Phases.Phase.eOrderCompile | 
 | 298 |             finalPhaseOpt = hasSyntaxOnly | 
 | 299 |         elif hasDashS: | 
 | 300 |             finalPhase = Phases.Phase.eOrderCompile | 
 | 301 |             finalPhaseOpt = hasDashS | 
 | 302 |         elif hasDashC: | 
 | 303 |             finalPhase = Phases.Phase.eOrderAssemble | 
 | 304 |             finalPhaseOpt = hasDashC     | 
 | 305 |  | 
 | 306 |         if finalPhaseOpt: | 
 | 307 |             self.claim(finalPhaseOpt) | 
 | 308 |  | 
 | 309 |         # FIXME: Support -combine. | 
 | 310 |         if hasCombine: | 
 | 311 |             raise NotImplementedError,"-combine is not yet supported." | 
 | 312 |  | 
 | 313 |         actions = [] | 
 | 314 |         linkerInputs = [] | 
 | 315 |         # FIXME: This is gross. | 
 | 316 |         linkPhase = Phases.LinkPhase() | 
 | 317 |         for klass,input in inputs: | 
 | 318 |             # Figure out what step to start at. | 
 | 319 |  | 
 | 320 |             # FIXME: This should be part of the input class probably? | 
 | 321 |             # Altough it doesn't quite fit there either, things like | 
 | 322 |             # asm-with-preprocess don't easily fit into a linear scheme. | 
 | 323 |  | 
 | 324 |             # FIXME: I think we are going to end up wanting to just build | 
 | 325 |             # a simple FSA which we run the inputs down. | 
 | 326 |             sequence = [] | 
 | 327 |             if klass.preprocess: | 
 | 328 |                 sequence.append(Phases.PreprocessPhase()) | 
 | 329 |             if klass == Types.ObjectType: | 
 | 330 |                 sequence.append(linkPhase) | 
 | 331 |             elif klass.onlyAssemble: | 
 | 332 |                 sequence.extend([Phases.AssemblePhase(), | 
 | 333 |                                  linkPhase]) | 
 | 334 |             elif klass.onlyPrecompile: | 
 | 335 |                 sequence.append(Phases.PrecompilePhase()) | 
 | 336 |             else: | 
 | 337 |                 sequence.extend([Phases.CompilePhase(), | 
 | 338 |                                  Phases.AssemblePhase(), | 
 | 339 |                                  linkPhase]) | 
 | 340 |  | 
 | 341 |             if sequence[0].order > finalPhase: | 
 | 342 |                 assert finalPhaseOpt and finalPhaseOpt.opt | 
 | 343 |                 # FIXME: Explain what type of input file is. Or just match | 
 | 344 |                 # gcc warning. | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 345 |                 self.warning("%s: %s input file unused when %s is present" % (args.getValue(input), | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 346 |                                                                               sequence[0].name, | 
 | 347 |                                                                               finalPhaseOpt.opt.name)) | 
 | 348 |             else: | 
 | 349 |                 # Build the pipeline for this file. | 
 | 350 |  | 
 | 351 |                 current = Phases.InputAction(input, klass) | 
 | 352 |                 for transition in sequence: | 
 | 353 |                     # If the current action produces no output, or we are | 
 | 354 |                     # past what the user requested, we are done. | 
 | 355 |                     if (current.type is Types.NothingType or | 
 | 356 |                         transition.order > finalPhase): | 
 | 357 |                         break | 
 | 358 |                     else: | 
 | 359 |                         if isinstance(transition, Phases.PreprocessPhase): | 
 | 360 |                             assert isinstance(klass.preprocess, Types.InputType) | 
 | 361 |                             current = Phases.JobAction(transition, | 
 | 362 |                                                        [current], | 
 | 363 |                                                        klass.preprocess) | 
 | 364 |                         elif isinstance(transition, Phases.PrecompilePhase): | 
 | 365 |                             current = Phases.JobAction(transition, | 
 | 366 |                                                        [current], | 
 | 367 |                                                        Types.PCHType) | 
 | 368 |                         elif isinstance(transition, Phases.CompilePhase): | 
 | 369 |                             if hasSyntaxOnly: | 
 | 370 |                                 output = Types.NothingType | 
 | 371 |                             else: | 
 | 372 |                                 output = Types.AsmTypeNoPP | 
 | 373 |                             current = Phases.JobAction(transition, | 
 | 374 |                                                        [current], | 
 | 375 |                                                        output) | 
 | 376 |                         elif isinstance(transition, Phases.AssemblePhase): | 
 | 377 |                             current = Phases.JobAction(transition, | 
 | 378 |                                                        [current], | 
 | 379 |                                                        Types.ObjectType) | 
 | 380 |                         elif transition is linkPhase: | 
 | 381 |                             linkerInputs.append(current) | 
 | 382 |                             current = None | 
 | 383 |                             break | 
 | 384 |                         else: | 
 | 385 |                             raise RuntimeError,'Unrecognized transition: %s.' % transition | 
 | 386 |                         pass | 
 | 387 |  | 
 | 388 |                 if current is not None: | 
 | 389 |                     assert not isinstance(current, Phases.InputAction) | 
 | 390 |                     actions.append(current) | 
 | 391 |  | 
 | 392 |         if linkerInputs: | 
 | 393 |             actions.append(Phases.JobAction(linkPhase, | 
 | 394 |                                             linkerInputs,  | 
 | 395 |                                             Types.ImageType)) | 
 | 396 |  | 
 | 397 |         return actions | 
 | 398 |  | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 399 |     def buildPipeline(self, args): | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 400 |         # FIXME: We need to handle canonicalization of the specified arch. | 
 | 401 |  | 
 | 402 |         archs = [] | 
 | 403 |         hasOutput = None | 
 | 404 |         hasDashM = hasSaveTemps = None | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 405 |         for arg in args: | 
| Daniel Dunbar | 5039f21 | 2009-01-06 02:30:10 +0000 | [diff] [blame^] | 406 |             if arg.opt.name == '-arch': | 
 | 407 |                 archs.append(arg) | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 408 |             elif arg.opt.name.startswith('-M'): | 
 | 409 |                 hasDashM = arg | 
 | 410 |             elif arg.opt.name in ('-save-temps','--save-temps'): | 
 | 411 |                 hasSaveTemps = arg | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 412 |  | 
 | 413 |         if not archs: | 
 | 414 |             # FIXME: Need to infer arch so that we sub -Xarch | 
 | 415 |             # correctly. | 
 | 416 |             archs.append(Arguments.DerivedArg('i386')) | 
 | 417 |  | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 418 |         actions = self.buildNormalPipeline(args) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 419 |  | 
 | 420 |         # FIXME: Use custom exception for this. | 
 | 421 |         # | 
 | 422 |         # FIXME: We killed off some others but these aren't yet detected in | 
 | 423 |         # a functional manner. If we added information to jobs about which | 
 | 424 |         # "auxiliary" files they wrote then we could detect the conflict | 
 | 425 |         # these cause downstream. | 
 | 426 |         if len(archs) > 1: | 
 | 427 |             if hasDashM: | 
 | 428 |                 raise ValueError,"Cannot use -M options with multiple arch flags." | 
 | 429 |             elif hasSaveTemps: | 
 | 430 |                 raise ValueError,"Cannot use -save-temps with multiple arch flags." | 
 | 431 |  | 
 | 432 |         # Execute once per arch. | 
 | 433 |         finalActions = [] | 
 | 434 |         for p in actions: | 
 | 435 |             #  Make sure we can lipo this kind of output. If not (and it | 
 | 436 |             #  is an actual output) then we disallow, since we can't | 
 | 437 |             #  create an output file with the right name without | 
 | 438 |             #  overwriting it. We could remove this oddity by just | 
 | 439 |             #  changing the output names to include the arch, which would | 
 | 440 |             #  also fix -save-temps. Compatibility wins for now. | 
 | 441 |             # | 
 | 442 |             # FIXME: Is this error substantially less useful than | 
 | 443 |             # gcc-dd's? The main problem is that "Cannot use compiler | 
 | 444 |             # output with multiple arch flags" won't make sense to most | 
 | 445 |             # developers. | 
 | 446 |             if (len(archs) > 1 and | 
 | 447 |                 p.type not in (Types.NothingType,Types.ObjectType,Types.ImageType)): | 
 | 448 |                 raise ValueError,'Cannot use %s output with multiple arch flags.' % p.type.name | 
 | 449 |  | 
 | 450 |             inputs = [] | 
 | 451 |             for arch in archs: | 
 | 452 |                 inputs.append(Phases.BindArchAction(p, arch)) | 
 | 453 |  | 
 | 454 |             # Lipo if necessary. We do it this way because we need to set | 
 | 455 |             # the arch flag so that -Xarch_ gets rewritten. | 
 | 456 |             if len(inputs) == 1 or p.type == Types.NothingType: | 
 | 457 |                 finalActions.extend(inputs) | 
 | 458 |             else: | 
 | 459 |                 finalActions.append(Phases.JobAction(Phases.LipoPhase(), | 
 | 460 |                                                      inputs,  | 
 | 461 |                                                      p.type)) | 
 | 462 |  | 
 | 463 |         # FIXME: We need to add -Wl,arch_multiple and -Wl,final_output in | 
 | 464 |         # certain cases. This may be icky because we need to figure out the | 
 | 465 |         # mode first. Current plan is to hack on the pipeline once it is built | 
 | 466 |         # and we know what is being spit out. This avoids having to handling | 
 | 467 |         # things like -c and -combine in multiple places. | 
 | 468 |         # | 
 | 469 |         # The annoying one of these is -Wl,final_output because it involves | 
 | 470 |         # communication across different phases. | 
 | 471 |         # | 
 | 472 |         # Hopefully we can do this purely as part of the binding, but | 
 | 473 |         # leaving comment here for now until it is clear this works. | 
 | 474 |  | 
 | 475 |         return finalActions | 
 | 476 |  | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 477 |     def bindPhases(self, phases, args): | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 478 |         jobs = Jobs.JobList() | 
 | 479 |  | 
 | 480 |         finalOutput = None | 
 | 481 |         hasSaveTemps = hasNoIntegratedCPP = hasPipe = None | 
 | 482 |         forward = [] | 
 | 483 |         for a in args: | 
| Daniel Dunbar | 5039f21 | 2009-01-06 02:30:10 +0000 | [diff] [blame^] | 484 |             if a.opt.name == '<input>': | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 485 |                 pass | 
| Daniel Dunbar | 5039f21 | 2009-01-06 02:30:10 +0000 | [diff] [blame^] | 486 |             elif a.opt.name == '-save-temps': | 
 | 487 |                 hasSaveTemps = a | 
 | 488 |             elif a.opt.name == '-no-integrated-cpp': | 
 | 489 |                 hasNoIntegratedCPP = a | 
 | 490 |             elif a.opt.name == '-o': | 
 | 491 |                 finalOutput = a | 
 | 492 |             elif a.opt.name == '-pipe': | 
 | 493 |                 hasPipe = a | 
 | 494 |             elif a.opt.name in ('-E', '-S', '-c', | 
 | 495 |                                 '-arch', '-fsyntax-only', '-combine', '-x', | 
 | 496 |                                 '-###'): | 
 | 497 |                 pass | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 498 |             else: | 
 | 499 |                 forward.append(a) | 
 | 500 |  | 
 | 501 |         # We claim things here so that options for which we silently allow | 
 | 502 |         # override only ever claim the used option. | 
 | 503 |         if hasPipe: | 
 | 504 |             self.claim(hasPipe) | 
 | 505 |             # FIXME: Hack, override -pipe till we support it. | 
 | 506 |             hasPipe = None | 
 | 507 |         # Claim these here. Its not completely accurate but any warnings | 
 | 508 |         # about these being unused are likely to be noise anyway. | 
 | 509 |         if hasSaveTemps: | 
 | 510 |             self.claim(hasSaveTemps) | 
 | 511 |         if hasNoIntegratedCPP: | 
 | 512 |             self.claim(hasNoIntegratedCPP) | 
 | 513 |  | 
 | 514 |         toolMap = { | 
 | 515 |             Phases.PreprocessPhase : Tools.GCC_PreprocessTool(), | 
 | 516 |             Phases.CompilePhase : Tools.GCC_CompileTool(), | 
 | 517 |             Phases.PrecompilePhase : Tools.GCC_PrecompileTool(), | 
 | 518 |             Phases.AssemblePhase : Tools.DarwinAssemblerTool(), | 
 | 519 |             Phases.LinkPhase : Tools.Collect2Tool(), | 
 | 520 |             Phases.LipoPhase : Tools.LipoTool(), | 
 | 521 |             } | 
 | 522 |  | 
 | 523 |         class InputInfo: | 
 | 524 |             def __init__(self, source, type, baseInput): | 
 | 525 |                 self.source = source | 
 | 526 |                 self.type = type | 
 | 527 |                 self.baseInput = baseInput | 
 | 528 |  | 
 | 529 |             def __repr__(self): | 
 | 530 |                 return '%s(%r, %r, %r)' % (self.__class__.__name__, | 
 | 531 |                                            self.source, self.type, self.baseInput) | 
 | 532 |  | 
 | 533 |         def createJobs(phase, forwardArgs, | 
 | 534 |                        canAcceptPipe=False, atTopLevel=False, arch=None): | 
 | 535 |             if isinstance(phase, Phases.InputAction): | 
 | 536 |                 return InputInfo(phase.filename, phase.type, phase.filename) | 
 | 537 |             elif isinstance(phase, Phases.BindArchAction): | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 538 |                 archName = args.getValue(phase.arch) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 539 |                 filteredArgs = [] | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 540 |                 for arg in forwardArgs: | 
| Daniel Dunbar | 5039f21 | 2009-01-06 02:30:10 +0000 | [diff] [blame^] | 541 |                     if arg.opt.name == '-arch': | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 542 |                         if arg is phase.arch: | 
 | 543 |                             filteredArgs.append(arg) | 
 | 544 |                     elif arg.opt.name == '-Xarch_': | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 545 |                         # FIXME: gcc-dd has another conditional for passing | 
 | 546 |                         # through, when the arch conditional array has an empty | 
 | 547 |                         # string. Why? | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 548 |                         if args.getJoinedValue(arg) == archName: | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 549 |                             # FIXME: This is wrong, we don't want a | 
 | 550 |                             # DerivedArg we want an actual parsed version | 
 | 551 |                             # of this arg. | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 552 |                             filteredArgs.append(Arguments.DerivedArg(args.getSeparateValue(arg))) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 553 |                     else: | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 554 |                         filteredArgs.append(arg) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 555 |                          | 
 | 556 |                 return createJobs(phase.inputs[0], filteredArgs, | 
 | 557 |                                   canAcceptPipe, atTopLevel, phase.arch) | 
 | 558 |  | 
 | 559 |             assert isinstance(phase, Phases.JobAction) | 
 | 560 |             tool = toolMap[phase.phase.__class__] | 
 | 561 |  | 
 | 562 |             # See if we should use an integrated CPP. We only use an | 
 | 563 |             # integrated cpp when we have exactly one input, since this is | 
 | 564 |             # the only use case we care about. | 
 | 565 |             useIntegratedCPP = False | 
 | 566 |             inputList = phase.inputs | 
 | 567 |             if (not hasNoIntegratedCPP and  | 
 | 568 |                 not hasSaveTemps and | 
 | 569 |                 tool.hasIntegratedCPP()): | 
 | 570 |                 if (len(phase.inputs) == 1 and  | 
 | 571 |                     isinstance(phase.inputs[0].phase, Phases.PreprocessPhase)): | 
 | 572 |                     useIntegratedCPP = True | 
 | 573 |                     inputList = phase.inputs[0].inputs | 
 | 574 |  | 
 | 575 |             # Only try to use pipes when exactly one input. | 
 | 576 |             canAcceptPipe = len(inputList) == 1 and tool.acceptsPipedInput() | 
 | 577 |             inputs = [createJobs(p, forwardArgs, canAcceptPipe, False, arch) for p in inputList] | 
 | 578 |  | 
 | 579 |             # Determine if we should output to a pipe. | 
 | 580 |             canOutputToPipe = canAcceptPipe and tool.canPipeOutput() | 
 | 581 |             outputToPipe = False | 
 | 582 |             if canOutputToPipe: | 
 | 583 |                 # Some things default to writing to a pipe if the final | 
 | 584 |                 # phase and there was no user override.   | 
 | 585 |                 #  | 
 | 586 |                 # FIXME: What is the best way to handle this? | 
 | 587 |                 if (atTopLevel and  | 
 | 588 |                     isinstance(phase, Phases.PreprocessPhase) and  | 
 | 589 |                     not finalOutput): | 
 | 590 |                     outputToPipe = True | 
 | 591 |                 elif hasPipe: | 
 | 592 |                     outputToPipe = True | 
 | 593 |  | 
 | 594 |             # Figure out where to put the job (pipes). | 
 | 595 |             jobList = jobs | 
 | 596 |             if canAcceptPipe and isinstance(inputs[0].source, Jobs.PipedJob): | 
 | 597 |                 jobList = inputs[0].source | 
 | 598 |  | 
 | 599 |             # Figure out where to put the output. | 
 | 600 |             baseInput = inputs[0].baseInput | 
 | 601 |             if phase.type == Types.NothingType: | 
 | 602 |                 output = None             | 
 | 603 |             elif outputToPipe: | 
 | 604 |                 if isinstance(jobList, Jobs.PipedJob): | 
 | 605 |                     output = jobList | 
 | 606 |                 else: | 
 | 607 |                     jobList = output = Jobs.PipedJob([]) | 
 | 608 |                     jobs.addJob(output) | 
 | 609 |             else: | 
 | 610 |                 # Figure out what the derived output location would be. | 
 | 611 |                 #  | 
 | 612 |                 # FIXME: gcc has some special case in here so that it doesn't | 
 | 613 |                 # create output files if they would conflict with an input. | 
| Daniel Dunbar | 1e5f3eb | 2009-01-06 01:35:44 +0000 | [diff] [blame] | 614 |                 inputName = args.getValue(baseInput) | 
| Daniel Dunbar | a567751 | 2009-01-05 19:53:30 +0000 | [diff] [blame] | 615 |                 if phase.type is Types.ImageType: | 
 | 616 |                     namedOutput = "a.out" | 
 | 617 |                 else: | 
 | 618 |                     base,_ = os.path.splitext(inputName) | 
 | 619 |                     assert phase.type.tempSuffix is not None | 
 | 620 |                     namedOutput = base + phase.type.tempSuffix | 
 | 621 |  | 
 | 622 |                 # Output to user requested destination? | 
 | 623 |                 if atTopLevel and finalOutput: | 
 | 624 |                     output = finalOutput | 
 | 625 |                 # Contruct a named destination? | 
 | 626 |                 elif atTopLevel or hasSaveTemps: | 
 | 627 |                     output = Arguments.DerivedArg(namedOutput) | 
 | 628 |                 else: | 
 | 629 |                     # Output to temp file... | 
 | 630 |                     fd,filename = tempfile.mkstemp(suffix=phase.type.tempSuffix) | 
 | 631 |                     output = Arguments.DerivedArg(filename) | 
 | 632 |  | 
 | 633 |             tool.constructJob(phase, arch, jobList, inputs, output, phase.type, forwardArgs) | 
 | 634 |  | 
 | 635 |             return InputInfo(output, phase.type, baseInput) | 
 | 636 |  | 
 | 637 |         # It is an error to provide a -o option if we are making multiple | 
 | 638 |         # output files. | 
 | 639 |         if finalOutput and len([a for a in phases if a.type is not Types.NothingType]) > 1: | 
 | 640 |             # FIXME: Custom exception. | 
 | 641 |             raise ValueError,"Cannot specify -o when generating multiple files." | 
 | 642 |  | 
 | 643 |         for phase in phases: | 
 | 644 |             createJobs(phase, forward, canAcceptPipe=True, atTopLevel=True) | 
 | 645 |  | 
 | 646 |         return jobs |