[cfe-commits] r61739 - in /cfe/trunk/tools/ccc: ./ ccclib/ ccclib/Arguments.py ccclib/Driver.py ccclib/Jobs.py ccclib/Phases.py ccclib/Tools.py ccclib/Types.py ccclib/Util.py ccclib/__init__.py test/ test/ccc/ test/ccc/Xarch.c test/ccc/argument-types.c test/ccc/hello.c test/ccc/integrated-cpp.c test/ccc/invalid.c test/ccc/phases.c test/ccc/universal-hello.c xcc

Daniel Dunbar daniel at zuster.org
Mon Jan 5 11:53:31 PST 2009


Author: ddunbar
Date: Mon Jan  5 13:53:30 2009
New Revision: 61739

URL: http://llvm.org/viewvc/llvm-project?rev=61739&view=rev
Log:
Add prototype ccc rewrite.
 - Entry point is tools/ccc/xcc until we are a functional replacement
   for ccc.

This is highly experimental (FIXME/LOC ratio of 3.4%), quite crufty,
and barely usable (and then only on my specific Darwin). However, many
of the right ideas are present, and it already fixes a number of
things gcc gets wrong.

The major missing component is argument translation for tools
(translating driver arguments into cc1/ld/as/etc. arguments). This is
a large part of the driver functionality and will probably double the
LOC, but my hope is that the current architecture is relatively
stable.

Documentation & motivation to follow soon...

Added:
    cfe/trunk/tools/ccc/
    cfe/trunk/tools/ccc/ccclib/
    cfe/trunk/tools/ccc/ccclib/Arguments.py
    cfe/trunk/tools/ccc/ccclib/Driver.py
    cfe/trunk/tools/ccc/ccclib/Jobs.py
    cfe/trunk/tools/ccc/ccclib/Phases.py
    cfe/trunk/tools/ccc/ccclib/Tools.py
    cfe/trunk/tools/ccc/ccclib/Types.py
    cfe/trunk/tools/ccc/ccclib/Util.py
    cfe/trunk/tools/ccc/ccclib/__init__.py
    cfe/trunk/tools/ccc/test/
    cfe/trunk/tools/ccc/test/ccc/
    cfe/trunk/tools/ccc/test/ccc/Xarch.c
    cfe/trunk/tools/ccc/test/ccc/argument-types.c
    cfe/trunk/tools/ccc/test/ccc/hello.c
    cfe/trunk/tools/ccc/test/ccc/integrated-cpp.c
    cfe/trunk/tools/ccc/test/ccc/invalid.c
    cfe/trunk/tools/ccc/test/ccc/phases.c
    cfe/trunk/tools/ccc/test/ccc/universal-hello.c
    cfe/trunk/tools/ccc/xcc   (with props)

Added: cfe/trunk/tools/ccc/ccclib/Arguments.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/ccc/ccclib/Arguments.py?rev=61739&view=auto

==============================================================================
--- cfe/trunk/tools/ccc/ccclib/Arguments.py (added)
+++ cfe/trunk/tools/ccc/ccclib/Arguments.py Mon Jan  5 13:53:30 2009
@@ -0,0 +1,446 @@
+class Option(object):
+    """Root option class."""
+    def __init__(self, name):
+        self.name = name
+
+    def accept(self, index, arg, it):
+        """accept(index, arg, iterator) -> Arg or None
+        
+        Accept the argument at the given index, returning an Arg, or
+        return None if the option does not accept this argument.
+
+        May raise MissingArgumentError.
+        """
+        abstract
+
+    def __repr__(self):
+        return '<%s name=%r>' % (self.__class__.__name__,
+                                 self.name)
+
+class FlagOption(Option):
+    """An option which takes no arguments."""
+
+    def accept(self, index, arg, it):
+        if arg == self.name:
+            return Arg(index, self)
+
+class JoinedOption(Option):
+    """An option which literally prefixes its argument."""
+
+    def accept(self, index, arg, it):        
+        if arg.startswith(self.name):
+            return JoinedValueArg(index, self)
+
+class SeparateOption(Option):
+    """An option which is followed by its value."""
+
+    def accept(self, index, arg, it):
+        if arg == self.name:
+            try:
+                _,value = it.next()
+            except StopIteration:
+                raise MissingArgumentError,self
+            return SeparateValueArg(index, self)
+
+class MultiArgOption(Option):
+    """An option which takes multiple arguments."""
+
+    def __init__(self, name, numArgs):
+        assert numArgs > 1
+        super(MultiArgOption, self).__init__(name)
+        self.numArgs = numArgs
+
+    def accept(self, index, arg, it):
+        if arg.startswith(self.name):
+            try:
+                values = [it.next()[1] for i in range(self.numArgs)]
+            except StopIteration:
+                raise MissingArgumentError,self
+            return MultipleValuesArg(index, self)
+
+class JoinedOrSeparateOption(Option):
+    """An option which either literally prefixes its value or is
+    followed by an value."""
+
+    def accept(self, index, arg, it):
+        if arg.startswith(self.name):
+            if len(arg) != len(self.name): # Joined case
+                return JoinedValueArg(index, self)
+            else:
+                try:
+                    _,value = it.next()
+                except StopIteration:
+                    raise MissingArgumentError,self
+                return SeparateValueArg(index, self)
+
+class JoinedAndSeparateOption(Option):
+    """An option which literally prefixes its value and is followed by
+    an value."""
+
+    def accept(self, index, arg, it):
+        if arg.startswith(self.name):
+            try:
+                _,value = it.next()
+            except StopIteration:
+                raise MissingArgumentError,self
+            return JoinedAndSeparateValuesArg(index, self)
+
+###
+
+class Arg(object):
+    """Arg - Base class for actual driver arguments."""
+    def __init__(self, index, opt):
+        self.index = index
+        self.opt = opt
+
+    def __repr__(self):
+        return '<%s index=%r opt=%r>' % (self.__class__.__name__,
+                                         self.index,
+                                         self.opt)
+
+    def render(self, args):
+        """render(args) -> [str]
+
+        Map the argument into a list of actual program arguments,
+        given the source argument array."""
+        assert self.opt
+        return [self.opt.name]
+
+class ValueArg(Arg):
+    """ValueArg - An instance of an option which has an argument."""
+
+    def getValue(self, args):
+        abstract
+
+    def setValue(self, args, value):
+        abstract
+
+class UnknownArg(ValueArg):
+    def __init__(self, index):
+        super(UnknownArg, self).__init__(index, None)
+
+    def getValue(self, args):
+        return args[self.index]
+    
+    def setValue(self, args, value):
+        args[self.index] = value
+
+    def render(self, args):
+        return [args[self.index]]
+
+class JoinedValueArg(ValueArg):
+    def getValue(self, args):
+        return args[self.index][len(self.opt.name):]
+
+    def setValue(self, args, value):
+        assert self.opt.name == args[self.index][:len(self.opt.name)]
+        args[self.index] = self.opt.name + value
+
+    def render(self, args):
+        return [self.opt.name + self.getValue(args)]
+
+class SeparateValueArg(ValueArg):
+    def getValue(self, args):
+        return args[self.index+1]
+
+    def setValue(self, args, value):
+        args[self.index+1] = value
+
+    def render(self, args):
+        return [self.opt.name, self.getValue(args)]
+
+class MultipleValuesArg(Arg):
+    def getValues(self, args):
+        return args[self.index + 1:self.index + 1 + self.opt.numArgs]
+
+    def setValues(self, args, value):
+        assert self.opt.numArgs == len(value)
+        args[self.index + 1:self.index + 1 + self.opt.numArgs] = value
+
+    def render(self, args):
+        return [self.opt.name] + self.getValues(args)
+
+# FIXME: Man, this is lame. It is only used by -Xarch. Maybe easier to
+# just special case?
+class JoinedAndSeparateValuesArg(Arg):
+    """JoinedAndSeparateValuesArg - An argument with both joined and
+    separate values."""
+
+    def getJoinedValue(self, args):
+        return args[self.index][len(self.opt.name):]
+
+    def getSeparateValue(self, args):
+        return args[self.index+1]
+
+    def setJoinedValue(self, args, value):
+        assert self.opt.name == args[self.index][:len(self.opt.name)]
+        args[self.index] = self.opt.name + value
+        
+    def setSeparateValue(self, args, vaue):
+        args[self.index+1] = value
+
+    def render(self, args):
+        return ([self.opt.name + self.getJoinedValue(args)] + 
+                [self.getSeparateValue(args)])
+
+class InputArg(ValueArg):
+    """InputArg - An input file (positional) argument."""
+
+    def __init__(self, index):
+        super(ValueArg, self).__init__(index, None)
+
+    def getValue(self, args):
+        return args[self.index]
+
+    def setValue(self, args, value):
+        args[self.index] = value
+ 
+    def render(self, args):
+        return [self.getValue(args)]
+
+class DerivedArg(ValueArg):
+    """DerivedArg - A synthesized argument which does not correspend
+    to the actual input arguments array."""
+
+    def __init__(self, value):
+        super(ValueArg, self).__init__(-1, None)
+        self.value = value
+
+    def getValue(self, args):
+        return self.value
+
+    def setValue(self, args):
+        raise ValueError,"Cannot call setValue() on a DerivedArg."
+    
+    def render(self, args):
+        return [self.value]
+
+class OptionParser:
+    def __init__(self):
+        self.options = []
+    
+    def addOption(self, opt):
+        self.options.append(opt)
+
+    def chunkArgs(self, argv):
+        """
+        chunkArgs([str]) -> [Arg]
+
+        Parse command line into individual option instances.
+        """
+
+        iargs = enumerate(argv)
+        it = iter(iargs)
+        args = []
+        for i,a in it:
+            # FIXME: Handle '@'
+            if not a: 
+                # gcc's handling of empty arguments doesn't make
+                # sense, but this is not a common use case. :)
+                #
+                # We just ignore them here (note that other things may
+                # still take them as arguments).
+                pass
+            elif a[0] == '-' and a != '-':
+                args.append(self.lookupOptForArg(i, a, it))
+            else:
+                args.append(InputArg(i))
+        return args
+    
+    def lookupOptForArg(self, i, arg, it):
+        for op in self.options:
+            opt = op.accept(i, arg, it)
+            if opt is not None:
+                return opt
+        return UnknownArg(i)
+
+def createOptionParser():
+    op = OptionParser()
+    
+    # Driver driver options
+    op.addOption(SeparateOption('-arch'))
+
+    # Misc driver options
+    op.addOption(FlagOption('-pass-exit-codes'))
+    op.addOption(FlagOption('--help'))
+    op.addOption(FlagOption('--target-help'))
+    
+    op.addOption(FlagOption('-dumpspecs'))
+    op.addOption(FlagOption('-dumpversion'))
+    op.addOption(FlagOption('-dumpmachine'))
+    op.addOption(FlagOption('-print-search-dirs'))
+    op.addOption(FlagOption('-print-libgcc-file-name'))
+    # FIXME: Hrm, where does this come from? It isn't always true that
+    # we take both - and --. For example, gcc --S ... ends up sending
+    # -fS to cc1. Investigate.
+    op.addOption(FlagOption('--print-libgcc-file-name'))
+    op.addOption(JoinedOption('-print-file-name='))
+    op.addOption(JoinedOption('-print-prog-name='))
+    op.addOption(JoinedOption('--print-prog-name='))
+    op.addOption(FlagOption('-print-multi-directory'))
+    op.addOption(FlagOption('-print-multi-lib'))
+    op.addOption(FlagOption('-print-multi-os-directory'))
+
+    # Hmmm, who really takes this?
+    op.addOption(FlagOption('--version'))
+    
+    # Pipeline control
+    op.addOption(FlagOption('-###'))
+    op.addOption(FlagOption('-E'))
+    op.addOption(FlagOption('-S'))
+    op.addOption(FlagOption('-c'))
+    op.addOption(FlagOption('-combine'))
+    op.addOption(FlagOption('-no-integrated-cpp'))
+    op.addOption(FlagOption('-pipe'))
+    op.addOption(FlagOption('-save-temps'))
+    op.addOption(FlagOption('--save-temps'))
+    op.addOption(JoinedOption('-specs='))
+    op.addOption(FlagOption('-time'))
+    op.addOption(FlagOption('-v'))
+
+    # Input/output stuff
+    op.addOption(JoinedOrSeparateOption('-o'))
+    op.addOption(JoinedOrSeparateOption('-x'))
+
+    # FIXME: What do these actually do? The documentation is less than
+    # clear.
+    op.addOption(FlagOption('-ObjC'))
+    op.addOption(FlagOption('-ObjC++'))
+
+    # FIXME: Weird, gcc claims this here in help but I'm not sure why;
+    # perhaps interaction with preprocessor? Investigate.
+    op.addOption(JoinedOption('-std='))
+    op.addOption(JoinedOrSeparateOption('--sysroot'))
+
+    # Version control
+    op.addOption(JoinedOrSeparateOption('-B'))
+    op.addOption(JoinedOrSeparateOption('-V'))
+    op.addOption(JoinedOrSeparateOption('-b'))
+
+    # Blanket pass-through options.
+    
+    op.addOption(JoinedOption('-Wa,'))
+    op.addOption(SeparateOption('-Xassembler'))
+
+    op.addOption(JoinedOption('-Wp,'))
+    op.addOption(SeparateOption('-Xpreprocessor'))
+
+    op.addOption(JoinedOption('-Wl,'))
+    op.addOption(SeparateOption('-Xlinker'))
+
+    ####
+    # Bring on the random garbage.
+
+    op.addOption(FlagOption('-MD'))
+    op.addOption(FlagOption('-MP'))
+    op.addOption(FlagOption('-MM'))
+    op.addOption(JoinedOrSeparateOption('-MF'))
+    op.addOption(JoinedOrSeparateOption('-MT'))
+    op.addOption(FlagOption('-undef'))
+
+    op.addOption(FlagOption('-w'))
+    op.addOption(JoinedOrSeparateOption('-allowable_client'))
+    op.addOption(JoinedOrSeparateOption('-client_name'))
+    op.addOption(JoinedOrSeparateOption('-compatibility_version'))
+    op.addOption(JoinedOrSeparateOption('-current_version'))
+    op.addOption(JoinedOrSeparateOption('-exported_symbols_list'))
+    op.addOption(JoinedOrSeparateOption('-idirafter'))
+    op.addOption(JoinedOrSeparateOption('-iquote'))
+    op.addOption(JoinedOrSeparateOption('-isysroot'))
+    op.addOption(JoinedOrSeparateOption('-keep_private_externs'))
+    op.addOption(JoinedOrSeparateOption('-seg1addr'))
+    op.addOption(JoinedOrSeparateOption('-segprot'))
+    op.addOption(JoinedOrSeparateOption('-sub_library'))
+    op.addOption(JoinedOrSeparateOption('-sub_umbrella'))
+    op.addOption(JoinedOrSeparateOption('-umbrella'))
+    op.addOption(JoinedOrSeparateOption('-undefined'))
+    op.addOption(JoinedOrSeparateOption('-unexported_symbols_list'))
+    op.addOption(JoinedOrSeparateOption('-weak_framework'))
+    op.addOption(JoinedOption('-headerpad_max_install_names'))
+    op.addOption(FlagOption('-twolevel_namespace'))
+    op.addOption(FlagOption('-prebind'))
+    op.addOption(FlagOption('-prebind_all_twolevel_modules'))
+    op.addOption(FlagOption('-single_module'))
+    op.addOption(FlagOption('-nomultidefs'))
+    op.addOption(FlagOption('-nostdlib'))
+    op.addOption(FlagOption('-nostdinc'))
+    op.addOption(FlagOption('-static'))
+    op.addOption(FlagOption('-shared'))
+    op.addOption(FlagOption('-C'))
+    op.addOption(FlagOption('-CC'))
+    op.addOption(FlagOption('-R'))
+    op.addOption(FlagOption('-P'))
+    op.addOption(FlagOption('-all_load'))
+    op.addOption(FlagOption('--constant-cfstrings'))
+    op.addOption(FlagOption('-traditional'))
+    op.addOption(FlagOption('--traditional'))
+    op.addOption(FlagOption('-no_dead_strip_inits_and_terms'))
+    op.addOption(MultiArgOption('-sectalign', numArgs=3))
+    op.addOption(MultiArgOption('-sectcreate', numArgs=3))
+    op.addOption(MultiArgOption('-sectorder', numArgs=3))
+
+    # I dunno why these don't end up working when joined. Maybe
+    # because of translation?
+    op.addOption(SeparateOption('-filelist'))
+    op.addOption(SeparateOption('-framework'))
+    op.addOption(SeparateOption('-install_name'))
+    op.addOption(SeparateOption('-seg_addr_table'))
+    op.addOption(SeparateOption('-seg_addr_table_filename'))
+
+    # Where are these coming from? I can't find them...
+    op.addOption(JoinedOrSeparateOption('-e')) # Gets forwarded to linker
+    op.addOption(JoinedOrSeparateOption('-r'))
+
+    # Is this actually declared anywhere? I can only find it in a
+    # spec. :(
+    op.addOption(FlagOption('-pg'))
+
+    doNotReallySupport = 1
+    if doNotReallySupport:
+        # Archaic gcc option.
+        op.addOption(FlagOption('-cpp-precomp'))
+        op.addOption(FlagOption('-no-cpp-precomp'))
+
+    # C options for testing
+
+    op.addOption(JoinedOrSeparateOption('-include'))
+    op.addOption(JoinedOrSeparateOption('-A'))
+    op.addOption(JoinedOrSeparateOption('-D'))
+    op.addOption(JoinedOrSeparateOption('-F'))
+    op.addOption(JoinedOrSeparateOption('-I'))
+    op.addOption(JoinedOrSeparateOption('-L'))
+    op.addOption(JoinedOrSeparateOption('-U'))
+    op.addOption(JoinedOrSeparateOption('-l'))
+    op.addOption(JoinedOrSeparateOption('-u'))
+
+    # FIXME: What is going on here? '-X' goes to linker, and -X ... goes nowhere?
+    op.addOption(FlagOption('-X'))
+    # Not exactly sure how to decompose this. I split out -Xarch_
+    # because we need to recognize that in the driver driver part.
+    # FIXME: Man, this is lame it needs its own option.
+    op.addOption(JoinedAndSeparateOption('-Xarch_'))
+    op.addOption(JoinedOption('-X'))
+
+    # The driver needs to know about this flag.
+    op.addOption(FlagOption('-fsyntax-only'))
+
+    # FIXME: Wrong?
+    # FIXME: What to do about the ambiguity of options like
+    # -dumpspecs? How is this handled in gcc?
+    op.addOption(JoinedOption('-d'))
+    op.addOption(JoinedOption('-g'))
+    op.addOption(JoinedOption('-f'))
+    op.addOption(JoinedOption('-m'))
+    op.addOption(JoinedOption('-i'))
+    op.addOption(JoinedOption('-O'))
+    op.addOption(JoinedOption('-W'))
+    # FIXME: Weird. This option isn't really separate, --param=a=b
+    # works. An alias somewhere?
+    op.addOption(SeparateOption('--param'))
+
+    # FIXME: What is this? Seems to do something on Linux. I think
+    # only one is valid, but have a log that uses both.
+    op.addOption(FlagOption('-pthread'))
+    op.addOption(FlagOption('-pthreads'))
+
+    return op

Added: cfe/trunk/tools/ccc/ccclib/Driver.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/ccc/ccclib/Driver.py?rev=61739&view=auto

==============================================================================
--- cfe/trunk/tools/ccc/ccclib/Driver.py (added)
+++ cfe/trunk/tools/ccc/ccclib/Driver.py Mon Jan  5 13:53:30 2009
@@ -0,0 +1,666 @@
+import os
+import sys
+import tempfile
+from pprint import pprint
+
+###
+
+import Arguments
+import Jobs
+import Phases
+import Tools
+import Types
+import Util
+
+# FIXME: Clean up naming of options and arguments. Decide whether to
+# rename Option and be consistent about use of Option/Arg.
+
+####
+
+class MissingArgumentError(ValueError):
+    """MissingArgumentError - An option required an argument but none
+    was given."""
+
+###
+
+class Driver(object):
+    def __init__(self):
+        self.parser = Arguments.createOptionParser()
+
+    def run(self, args):
+        # FIXME: Things to support from environment: GCC_EXEC_PREFIX,
+        # COMPILER_PATH, LIBRARY_PATH, LPATH, CC_PRINT_OPTIONS,
+        # QA_OVERRIDE_GCC3_OPTIONS, ...?
+
+        # FIXME: -V and -b processing
+
+        # Handle some special -ccc- options used for testing which are
+        # only allowed at the beginning of the command line.
+        cccPrintOptions = False
+        cccPrintPhases = False
+        cccUseDriverDriver = True
+        while args and args[0].startswith('-ccc-'):
+            opt,args = args[0][5:],args[1:]
+
+            if opt == 'print-options':
+                cccPrintOptions = True
+            elif opt == 'print-phases':
+                cccPrintPhases = True
+            elif opt == 'no-driver-driver':
+                # FIXME: Remove this once we have some way of being a
+                # cross compiler driver (cross driver compiler? compiler
+                # cross driver? etc.).
+                cccUseDriverDriver = False
+            else:
+                raise ValueError,"Invalid ccc option: %r" % cccPrintOptions
+
+        options = self.parser.chunkArgs(args)
+
+        # FIXME: Ho hum I have just realized -Xarch_ is broken. We really
+        # need to reparse the Arguments after they have been expanded by
+        # -Xarch. How is this going to work?
+        #
+        # Scratch that, we aren't going to do that; it really disrupts the
+        # organization, doesn't consistently work with gcc-dd, and is
+        # confusing. Instead we are going to enforce that -Xarch_ is only
+        # used with options which do not alter the driver behavior. Let's
+        # hope this is ok, because the current architecture is a little
+        # tied to it.
+
+        if cccPrintOptions:
+            self.printOptions(args, options)
+            sys.exit(0)
+
+        self.handleImmediateOptions(args, options)
+
+        if cccUseDriverDriver:
+            phases = self.buildPipeline(options, args)
+        else:
+            phases = self.buildNormalPipeline(options, args)
+
+        if cccPrintPhases:
+            self.printPhases(args, phases)
+            sys.exit(0)
+
+        if 0:
+            print Util.pprint(phases)
+
+        jobs = self.bindPhases(phases, options, args)
+
+        # FIXME: We should provide some basic sanity checking of the
+        # pipeline as a "verification" sort of stage. For example, the
+        # pipeline should never end up writing to an output file in two
+        # places (I think). The pipeline should also never end up writing
+        # to an output file that is an input.
+        #
+        # This is intended to just be a "verify" step, not a functionality
+        # step. It should catch things like the driver driver not
+        # preventing -save-temps, but it shouldn't change behavior (so we
+        # can turn it off in Release-Asserts builds).
+
+        # Print in -### syntax.
+        hasHashHashHash = None
+        for oi in options:
+            if oi.opt and oi.opt.name == '-###':
+                hasHashHashHash = oi
+
+        if hasHashHashHash:
+            self.claim(hasHashHashHash)
+            for j in jobs.iterjobs():
+                if isinstance(j, Jobs.Command):
+                    print '"%s"' % '" "'.join(j.render(args))
+                elif isinstance(j, Jobs.PipedJob):
+                    for c in j.commands:
+                        print '"%s" %c' % ('" "'.join(c.render(args)),
+                                           "| "[c is j.commands[-1]])
+                elif not isinstance(j, JobList):
+                    raise ValueError,'Encountered unknown job.'
+            sys.exit(0)
+
+        for j in jobs.iterjobs():
+            if isinstance(j, Jobs.Command):
+                cmd_args = j.render(args)
+                res = os.spawnvp(os.P_WAIT, cmd_args[0], cmd_args)
+                if res:
+                    sys.exit(res)
+            elif isinstance(j, Jobs.PipedJob):
+                raise NotImplementedError,"Piped jobs aren't implemented yet."
+            else:
+                raise ValueError,'Encountered unknown job.'
+
+    def claim(self, option):
+        # FIXME: Move to OptionList once introduced and implement.
+        pass
+
+    def warning(self, message):
+        print >>sys.stderr,'%s: %s' % (sys.argv[0], message)
+
+    def printOptions(self, args, options):
+        for i,oi in enumerate(options):
+            if isinstance(oi, Arguments.InputArg):
+                name = "<input>"
+            elif isinstance(oi, Arguments.UnknownArg):
+                name = "<unknown>"
+            else:
+                assert oi.opt
+                name = oi.opt.name
+            if isinstance(oi, Arguments.MultipleValuesArg):
+                values = list(oi.getValues(args))
+            elif isinstance(oi, Arguments.ValueArg):
+                values = [oi.getValue(args)]
+            elif isinstance(oi, Arguments.JoinedAndSeparateValuesArg):
+                values = [oi.getJoinedValue(args), oi.getSeparateValue(args)]
+            else:
+                values = []
+            print 'Option %d - Name: "%s", Values: {%s}' % (i, name, 
+                                                            ', '.join(['"%s"' % v 
+                                                                       for v in values]))
+
+    def printPhases(self, args, phases):
+        def printPhase(p, f, steps, arch=None):
+            if p in steps:
+                return steps[p]
+            elif isinstance(p, Phases.BindArchAction):
+                for kid in p.inputs:
+                    printPhase(kid, f, steps, p.arch)
+                steps[p] = len(steps)
+                return
+
+            if isinstance(p, Phases.InputAction):
+                phaseName = 'input'
+                inputStr = '"%s"' % p.filename.getValue(args)
+            else:
+                phaseName = p.phase.name
+                inputs = [printPhase(i, f, steps, arch) 
+                          for i in p.inputs]
+                inputStr = '{%s}' % ', '.join(map(str, inputs))
+            if arch is not None:
+                phaseName += '-' + arch.getValue(args)
+            steps[p] = index = len(steps)
+            print "%d: %s, %s, %s" % (index,phaseName,inputStr,p.type.name)
+            return index
+        steps = {}
+        for phase in phases:
+            printPhase(phase, sys.stdout, steps)
+
+    def handleImmediateOptions(self, args, options):
+        # FIXME: Some driver Arguments are consumed right off the bat,
+        # like -dumpversion. Currently the gcc-dd handles these
+        # poorly, so we should be ok handling them upfront instead of
+        # after driver-driver level dispatching.
+        #
+        # FIXME: The actual order of these options in gcc is all over the
+        # place. The -dump ones seem to be first and in specification
+        # order, but there are other levels of precedence. For example,
+        # -print-search-dirs is evaluated before -print-prog-name=,
+        # regardless of order (and the last instance of -print-prog-name=
+        # wins verse itself).
+        #
+        # FIXME: Do we want to report "argument unused" type errors in the
+        # presence of things like -dumpmachine and -print-search-dirs?
+        # Probably not.
+        for oi in options:
+            if oi.opt is not None:
+                if oi.opt.name == '-dumpmachine':
+                    print 'FIXME: %s' % oi.opt.name
+                    sys.exit(1)
+                elif oi.opt.name == '-dumpspecs':
+                    print 'FIXME: %s' % oi.opt.name
+                    sys.exit(1)
+                elif oi.opt.name == '-dumpversion':
+                    print 'FIXME: %s' % oi.opt.name
+                    sys.exit(1)
+                elif oi.opt.name == '-print-file-name=':
+                    print 'FIXME: %s' % oi.opt.name
+                    sys.exit(1)
+                elif oi.opt.name == '-print-multi-directory':
+                    print 'FIXME: %s' % oi.opt.name
+                    sys.exit(1)
+                elif oi.opt.name == '-print-multi-lib':
+                    print 'FIXME: %s' % oi.opt.name
+                    sys.exit(1)
+                elif oi.opt.name == '-print-prog-name=':
+                    print 'FIXME: %s' % oi.opt.name
+                    sys.exit(1)
+                elif oi.opt.name == '-print-libgcc-file-name':
+                    print 'FIXME: %s' % oi.opt.name
+                    sys.exit(1)
+                elif oi.opt.name == '-print-search-dirs':
+                    print 'FIXME: %s' % oi.opt.name
+                    sys.exit(1)
+
+    def buildNormalPipeline(self, args, inputArgs):
+        hasCombine = None
+        hasSyntaxOnly = None
+        hasDashC = hasDashE = hasDashS = None
+
+        inputType = None
+        inputTypeOpt = None
+        inputs = []
+        for a in args:
+            if isinstance(a, Arguments.InputArg):
+                if inputType is None:
+                    base,ext = os.path.splitext(a.getValue(inputArgs))
+                    if ext and ext in Types.kTypeSuffixMap:
+                        klass = Types.kTypeSuffixMap[ext]
+                    else:
+                        # FIXME: Its not clear why we shouldn't just
+                        # revert to unknown. I think this is more likely a
+                        # bug / unintended behavior in gcc. Not very
+                        # important though.
+                        klass = Types.ObjectType
+                else:
+                    assert inputTypeOpt is not None
+                    self.claim(inputTypeOpt)
+                    klass = inputType
+                inputs.append((klass, a))
+            elif a.opt is not None:
+                # FIXME: We should warn about inconsistent and duplicate
+                # usage of these flags.
+                if a.opt.name == '-E':
+                    hasDashE = a
+                elif a.opt.name == '-S':
+                    hasDashS = a
+                elif a.opt.name == '-c':
+                    hasDashC = a
+                elif a.opt.name == '-fsyntax-only':
+                    hasSyntaxOnly = a
+                elif a.opt.name == '-combine':
+                    hasCombine = a
+                elif a.opt.name == '-filelist':
+                    # FIXME: This might not be good enough. We may
+                    # need to introduce another type of InputArg for
+                    # this case, so that other code which needs to
+                    # know the inputs handles this properly. Best not
+                    # to try and lipo this, for example.
+                    #
+                    # Treat as a linker input.
+                    inputs.append((Types.ObjectType, a))
+                elif a.opt.name == '-x':
+                    self.claim(a)
+                    inputTypeOpt = a
+                    value = a.getValue(inputArgs)
+                    if value in Types.kTypeSpecifierMap:
+                        inputType = Types.kTypeSpecifierMap[value]
+                    else:
+                        # FIXME: How are we going to handle diagnostics.
+                        self.warning("language %s not recognized" % value)
+
+                        # FIXME: Its not clear why we shouldn't just
+                        # revert to unknown. I think this is more likely a
+                        # bug / unintended behavior in gcc. Not very
+                        # important though.
+                        inputType = ObjectType
+
+        # We claim things here so that options for which we silently allow
+        # override only ever claim the used option.
+        if hasCombine:
+            self.claim(hasCombine)
+
+        finalPhase = Phases.Phase.eOrderPostAssemble
+        finalPhaseOpt = None
+
+        # Determine what compilation mode we are in.
+        if hasDashE:
+            finalPhase = Phases.Phase.eOrderPreprocess
+            finalPhaseOpt = hasDashE
+        elif hasSyntaxOnly:
+            finalPhase = Phases.Phase.eOrderCompile
+            finalPhaseOpt = hasSyntaxOnly
+        elif hasDashS:
+            finalPhase = Phases.Phase.eOrderCompile
+            finalPhaseOpt = hasDashS
+        elif hasDashC:
+            finalPhase = Phases.Phase.eOrderAssemble
+            finalPhaseOpt = hasDashC    
+
+        if finalPhaseOpt:
+            self.claim(finalPhaseOpt)
+
+        # FIXME: Support -combine.
+        if hasCombine:
+            raise NotImplementedError,"-combine is not yet supported."
+
+        actions = []
+        linkerInputs = []
+        # FIXME: This is gross.
+        linkPhase = Phases.LinkPhase()
+        for klass,input in inputs:
+            # Figure out what step to start at.
+
+            # FIXME: This should be part of the input class probably?
+            # Altough it doesn't quite fit there either, things like
+            # asm-with-preprocess don't easily fit into a linear scheme.
+
+            # FIXME: I think we are going to end up wanting to just build
+            # a simple FSA which we run the inputs down.
+            sequence = []
+            if klass.preprocess:
+                sequence.append(Phases.PreprocessPhase())
+            if klass == Types.ObjectType:
+                sequence.append(linkPhase)
+            elif klass.onlyAssemble:
+                sequence.extend([Phases.AssemblePhase(),
+                                 linkPhase])
+            elif klass.onlyPrecompile:
+                sequence.append(Phases.PrecompilePhase())
+            else:
+                sequence.extend([Phases.CompilePhase(),
+                                 Phases.AssemblePhase(),
+                                 linkPhase])
+
+            if sequence[0].order > finalPhase:
+                assert finalPhaseOpt and finalPhaseOpt.opt
+                # FIXME: Explain what type of input file is. Or just match
+                # gcc warning.
+                self.warning("%s: %s input file unused when %s is present" % (input.getValue(inputArgs),
+                                                                              sequence[0].name,
+                                                                              finalPhaseOpt.opt.name))
+            else:
+                # Build the pipeline for this file.
+
+                current = Phases.InputAction(input, klass)
+                for transition in sequence:
+                    # If the current action produces no output, or we are
+                    # past what the user requested, we are done.
+                    if (current.type is Types.NothingType or
+                        transition.order > finalPhase):
+                        break
+                    else:
+                        if isinstance(transition, Phases.PreprocessPhase):
+                            assert isinstance(klass.preprocess, Types.InputType)
+                            current = Phases.JobAction(transition,
+                                                       [current],
+                                                       klass.preprocess)
+                        elif isinstance(transition, Phases.PrecompilePhase):
+                            current = Phases.JobAction(transition,
+                                                       [current],
+                                                       Types.PCHType)
+                        elif isinstance(transition, Phases.CompilePhase):
+                            if hasSyntaxOnly:
+                                output = Types.NothingType
+                            else:
+                                output = Types.AsmTypeNoPP
+                            current = Phases.JobAction(transition,
+                                                       [current],
+                                                       output)
+                        elif isinstance(transition, Phases.AssemblePhase):
+                            current = Phases.JobAction(transition,
+                                                       [current],
+                                                       Types.ObjectType)
+                        elif transition is linkPhase:
+                            linkerInputs.append(current)
+                            current = None
+                            break
+                        else:
+                            raise RuntimeError,'Unrecognized transition: %s.' % transition
+                        pass
+
+                if current is not None:
+                    assert not isinstance(current, Phases.InputAction)
+                    actions.append(current)
+
+        if linkerInputs:
+            actions.append(Phases.JobAction(linkPhase,
+                                            linkerInputs, 
+                                            Types.ImageType))
+
+        return actions
+
+    def buildPipeline(self, args, inputArgs):
+        # FIXME: We need to handle canonicalization of the specified arch.
+
+        archs = []
+        hasOutput = None
+        hasDashM = hasSaveTemps = None
+        for o in args:
+            if o.opt is None:
+                continue
+
+            if isinstance(o, Arguments.ValueArg):
+                if o.opt.name == '-arch':
+                    archs.append(o)
+            elif o.opt.name.startswith('-M'):
+                hasDashM = o
+            elif o.opt.name in ('-save-temps','--save-temps'):
+                hasSaveTemps = o
+
+        if not archs:
+            # FIXME: Need to infer arch so that we sub -Xarch
+            # correctly.
+            archs.append(Arguments.DerivedArg('i386'))
+
+        actions = self.buildNormalPipeline(args, inputArgs)
+
+        # FIXME: Use custom exception for this.
+        #
+        # FIXME: We killed off some others but these aren't yet detected in
+        # a functional manner. If we added information to jobs about which
+        # "auxiliary" files they wrote then we could detect the conflict
+        # these cause downstream.
+        if len(archs) > 1:
+            if hasDashM:
+                raise ValueError,"Cannot use -M options with multiple arch flags."
+            elif hasSaveTemps:
+                raise ValueError,"Cannot use -save-temps with multiple arch flags."
+
+        # Execute once per arch.
+        finalActions = []
+        for p in actions:
+            #  Make sure we can lipo this kind of output. If not (and it
+            #  is an actual output) then we disallow, since we can't
+            #  create an output file with the right name without
+            #  overwriting it. We could remove this oddity by just
+            #  changing the output names to include the arch, which would
+            #  also fix -save-temps. Compatibility wins for now.
+            #
+            # FIXME: Is this error substantially less useful than
+            # gcc-dd's? The main problem is that "Cannot use compiler
+            # output with multiple arch flags" won't make sense to most
+            # developers.
+            if (len(archs) > 1 and
+                p.type not in (Types.NothingType,Types.ObjectType,Types.ImageType)):
+                raise ValueError,'Cannot use %s output with multiple arch flags.' % p.type.name
+
+            inputs = []
+            for arch in archs:
+                inputs.append(Phases.BindArchAction(p, arch))
+
+            # Lipo if necessary. We do it this way because we need to set
+            # the arch flag so that -Xarch_ gets rewritten.
+            if len(inputs) == 1 or p.type == Types.NothingType:
+                finalActions.extend(inputs)
+            else:
+                finalActions.append(Phases.JobAction(Phases.LipoPhase(),
+                                                     inputs, 
+                                                     p.type))
+
+        # FIXME: We need to add -Wl,arch_multiple and -Wl,final_output in
+        # certain cases. This may be icky because we need to figure out the
+        # mode first. Current plan is to hack on the pipeline once it is built
+        # and we know what is being spit out. This avoids having to handling
+        # things like -c and -combine in multiple places.
+        #
+        # The annoying one of these is -Wl,final_output because it involves
+        # communication across different phases.
+        #
+        # Hopefully we can do this purely as part of the binding, but
+        # leaving comment here for now until it is clear this works.
+
+        return finalActions
+
+    def bindPhases(self, phases, args, inputArgs):
+        jobs = Jobs.JobList()
+
+        finalOutput = None
+        hasSaveTemps = hasNoIntegratedCPP = hasPipe = None
+        forward = []
+        for a in args:
+            if isinstance(a, Arguments.InputArg):
+                pass
+            elif a.opt is not None:
+                if a.opt.name == '-save-temps':
+                    hasSaveTemps = a
+                elif a.opt.name == '-no-integrated-cpp':
+                    hasNoIntegratedCPP = a
+                elif a.opt.name == '-o':
+                    finalOutput = a
+                elif a.opt.name == '-pipe':
+                    hasPipe = a
+                elif a.opt.name in ('-E', '-S', '-c',
+                                    '-arch', '-fsyntax-only', '-combine', '-x',
+                                    '-###'):
+                    pass
+                else:
+                    forward.append(a)
+            else:
+                forward.append(a)
+
+        # We claim things here so that options for which we silently allow
+        # override only ever claim the used option.
+        if hasPipe:
+            self.claim(hasPipe)
+            # FIXME: Hack, override -pipe till we support it.
+            hasPipe = None
+        # Claim these here. Its not completely accurate but any warnings
+        # about these being unused are likely to be noise anyway.
+        if hasSaveTemps:
+            self.claim(hasSaveTemps)
+        if hasNoIntegratedCPP:
+            self.claim(hasNoIntegratedCPP)
+
+        toolMap = {
+            Phases.PreprocessPhase : Tools.GCC_PreprocessTool(),
+            Phases.CompilePhase : Tools.GCC_CompileTool(),
+            Phases.PrecompilePhase : Tools.GCC_PrecompileTool(),
+            Phases.AssemblePhase : Tools.DarwinAssemblerTool(),
+            Phases.LinkPhase : Tools.Collect2Tool(),
+            Phases.LipoPhase : Tools.LipoTool(),
+            }
+
+        class InputInfo:
+            def __init__(self, source, type, baseInput):
+                self.source = source
+                self.type = type
+                self.baseInput = baseInput
+
+            def __repr__(self):
+                return '%s(%r, %r, %r)' % (self.__class__.__name__,
+                                           self.source, self.type, self.baseInput)
+
+        def createJobs(phase, forwardArgs,
+                       canAcceptPipe=False, atTopLevel=False, arch=None):
+            if isinstance(phase, Phases.InputAction):
+                return InputInfo(phase.filename, phase.type, phase.filename)
+            elif isinstance(phase, Phases.BindArchAction):
+                archName = phase.arch.getValue(inputArgs)
+                filteredArgs = []
+                for oi in forwardArgs:
+                    if oi.opt is None:
+                        filteredArgs.append(oi)
+                    elif oi.opt.name == '-arch':
+                        if oi is phase.arch:
+                            filteredArgs.append(oi)
+                    elif oi.opt.name == '-Xarch_':
+                        # FIXME: gcc-dd has another conditional for passing
+                        # through, when the arch conditional array has an empty
+                        # string. Why?
+                        if oi.getJoinedValue(inputArgs) == archName:
+                            # FIXME: This is wrong, we don't want a
+                            # DerivedArg we want an actual parsed version
+                            # of this arg.
+                            filteredArgs.append(Arguments.DerivedArg(oi.getSeparateValue(inputArgs)))
+                    else:
+                        filteredArgs.append(oi)
+                        
+                return createJobs(phase.inputs[0], filteredArgs,
+                                  canAcceptPipe, atTopLevel, phase.arch)
+
+            assert isinstance(phase, Phases.JobAction)
+            tool = toolMap[phase.phase.__class__]
+
+            # See if we should use an integrated CPP. We only use an
+            # integrated cpp when we have exactly one input, since this is
+            # the only use case we care about.
+            useIntegratedCPP = False
+            inputList = phase.inputs
+            if (not hasNoIntegratedCPP and 
+                not hasSaveTemps and
+                tool.hasIntegratedCPP()):
+                if (len(phase.inputs) == 1 and 
+                    isinstance(phase.inputs[0].phase, Phases.PreprocessPhase)):
+                    useIntegratedCPP = True
+                    inputList = phase.inputs[0].inputs
+
+            # Only try to use pipes when exactly one input.
+            canAcceptPipe = len(inputList) == 1 and tool.acceptsPipedInput()
+            inputs = [createJobs(p, forwardArgs, canAcceptPipe, False, arch) for p in inputList]
+
+            # Determine if we should output to a pipe.
+            canOutputToPipe = canAcceptPipe and tool.canPipeOutput()
+            outputToPipe = False
+            if canOutputToPipe:
+                # Some things default to writing to a pipe if the final
+                # phase and there was no user override.  
+                # 
+                # FIXME: What is the best way to handle this?
+                if (atTopLevel and 
+                    isinstance(phase, Phases.PreprocessPhase) and 
+                    not finalOutput):
+                    outputToPipe = True
+                elif hasPipe:
+                    outputToPipe = True
+
+            # Figure out where to put the job (pipes).
+            jobList = jobs
+            if canAcceptPipe and isinstance(inputs[0].source, Jobs.PipedJob):
+                jobList = inputs[0].source
+
+            # Figure out where to put the output.
+            baseInput = inputs[0].baseInput
+            if phase.type == Types.NothingType:
+                output = None            
+            elif outputToPipe:
+                if isinstance(jobList, Jobs.PipedJob):
+                    output = jobList
+                else:
+                    jobList = output = Jobs.PipedJob([])
+                    jobs.addJob(output)
+            else:
+                # Figure out what the derived output location would be.
+                # 
+                # FIXME: gcc has some special case in here so that it doesn't
+                # create output files if they would conflict with an input.
+                inputName = baseInput.getValue(inputArgs)
+                if phase.type is Types.ImageType:
+                    namedOutput = "a.out"
+                else:
+                    base,_ = os.path.splitext(inputName)
+                    assert phase.type.tempSuffix is not None
+                    namedOutput = base + phase.type.tempSuffix
+
+                # Output to user requested destination?
+                if atTopLevel and finalOutput:
+                    output = finalOutput
+                # Contruct a named destination?
+                elif atTopLevel or hasSaveTemps:
+                    output = Arguments.DerivedArg(namedOutput)
+                else:
+                    # Output to temp file...
+                    fd,filename = tempfile.mkstemp(suffix=phase.type.tempSuffix)
+                    output = Arguments.DerivedArg(filename)
+
+            tool.constructJob(phase, arch, jobList, inputs, output, phase.type, forwardArgs)
+
+            return InputInfo(output, phase.type, baseInput)
+
+        # It is an error to provide a -o option if we are making multiple
+        # output files.
+        if finalOutput and len([a for a in phases if a.type is not Types.NothingType]) > 1:
+            # FIXME: Custom exception.
+            raise ValueError,"Cannot specify -o when generating multiple files."
+
+        for phase in phases:
+            createJobs(phase, forward, canAcceptPipe=True, atTopLevel=True)
+
+        return jobs

Added: cfe/trunk/tools/ccc/ccclib/Jobs.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/ccc/ccclib/Jobs.py?rev=61739&view=auto

==============================================================================
--- cfe/trunk/tools/ccc/ccclib/Jobs.py (added)
+++ cfe/trunk/tools/ccc/ccclib/Jobs.py Mon Jan  5 13:53:30 2009
@@ -0,0 +1,60 @@
+import Arguments
+import Util
+
+class Job(object):
+    """Job - A set of commands to execute as a single task."""
+
+    def iterjobs(self):
+        abstract
+
+class Command(Job):
+    """Command - Represent the information needed to execute a single
+    process."""
+
+    def __init__(self, executable, args):
+        assert Util.all_true(args, lambda x: isinstance(x, Arguments.Arg))
+        self.executable = executable
+        self.args = args
+
+    def __repr__(self):
+        return Util.prefixAndPPrint(self.__class__.__name__,
+                                    (self.executable, self.args))
+    
+    def render(self, args):
+        argv = [self.executable]
+        for oi in self.args:
+            argv.extend(oi.render(args))
+        return argv
+
+    def iterjobs(self):
+        yield self
+    
+class PipedJob(Job):
+    """PipedJob - A sequence of piped commands."""
+
+    def __init__(self, commands):
+        assert all_true(args, lambda x: isinstance(x, Arguments.Command))
+        self.commands = list(commands)
+
+    def addJob(self, job):
+        assert isinstance(job, Command)
+        self.commands.append(job)
+
+    def __repr__(self):
+        return Util.prefixAndPPrint(self.__class__.__name__, (self.commands,))
+
+class JobList(Job):
+    """JobList - A sequence of jobs to perform."""
+
+    def __init__(self, jobs=[]):
+        self.jobs = list(jobs)
+    
+    def addJob(self, job):
+        self.jobs.append(job)
+
+    def __repr__(self):
+        return Util.prefixAndPPrint(self.__class__.__name__, (self.jobs,))
+
+    def iterjobs(self):
+        for j in self.jobs:
+            yield j

Added: cfe/trunk/tools/ccc/ccclib/Phases.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/ccc/ccclib/Phases.py?rev=61739&view=auto

==============================================================================
--- cfe/trunk/tools/ccc/ccclib/Phases.py (added)
+++ cfe/trunk/tools/ccc/ccclib/Phases.py Mon Jan  5 13:53:30 2009
@@ -0,0 +1,86 @@
+import Util
+
+class Action(object):
+    def __init__(self, inputs, type):
+        self.inputs = inputs
+        self.type = type
+
+class BindArchAction(Action):
+    """BindArchAction - Represent an architecture binding for child
+    actions."""
+
+    def __init__(self, input, arch):
+        super(BindArchAction, self).__init__([input], input.type)
+        self.arch = arch
+
+    def __repr__(self):
+        return Util.prefixAndPPrint(self.__class__.__name__,
+                                    (self.inputs[0], self.arch))
+
+class InputAction(Action):
+    """InputAction - Adapt an input file to an action & type. """
+
+    def __init__(self, filename, type):
+        super(InputAction, self).__init__([], type)
+        self.filename = filename
+
+    def __repr__(self):
+        return Util.prefixAndPPrint(self.__class__.__name__,
+                                    (self.filename, self.type))
+
+class JobAction(Action):
+    """JobAction - Represent a job tied to a particular compilation
+    phase."""
+
+    def __init__(self, phase, inputs, type):
+        super(JobAction, self).__init__(inputs, type)
+        self.phase = phase
+
+    def __repr__(self):
+        return Util.prefixAndPPrint(self.__class__.__name__,
+                                    (self.phase, self.inputs, self.type))
+
+###
+
+class Phase(object):
+    """Phase - Represent an abstract task in the compilation
+    pipeline."""
+
+    eOrderNone = 0
+    eOrderPreprocess = 1
+    eOrderCompile = 2
+    eOrderAssemble = 3
+    eOrderPostAssemble = 4
+    
+    def __init__(self, name, order):
+        self.name = name
+        self.order = order
+
+    def __repr__(self):
+        return Util.prefixAndPPrint(self.__class__.__name__,
+                                    (self.name, self.order))
+
+class PreprocessPhase(Phase):
+    def __init__(self):
+        super(PreprocessPhase, self).__init__("preprocessor", Phase.eOrderPreprocess)
+
+class PrecompilePhase(Phase):
+    def __init__(self):
+        super(PrecompilePhase, self).__init__("precompiler", Phase.eOrderCompile)
+
+class CompilePhase(Phase):
+    def __init__(self):
+        super(CompilePhase, self).__init__("compiler", Phase.eOrderCompile)
+
+class AssemblePhase(Phase):
+    def __init__(self):
+        super(AssemblePhase, self).__init__("assembler", Phase.eOrderAssemble)
+
+class LinkPhase(Phase):
+    def __init__(self):
+        super(LinkPhase, self).__init__("linker", Phase.eOrderPostAssemble)
+
+class LipoPhase(Phase):
+    def __init__(self):
+        super(LipoPhase, self).__init__("lipo", Phase.eOrderPostAssemble)
+

Added: cfe/trunk/tools/ccc/ccclib/Tools.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/ccc/ccclib/Tools.py?rev=61739&view=auto

==============================================================================
--- cfe/trunk/tools/ccc/ccclib/Tools.py (added)
+++ cfe/trunk/tools/ccc/ccclib/Tools.py Mon Jan  5 13:53:30 2009
@@ -0,0 +1,169 @@
+import Arguments
+import Jobs
+import Types
+
+class Tool(object):
+    """Tool - A concrete implementation of a phase."""
+
+    eFlagsPipedInput = 1 << 0
+    eFlagsPipedOutput = 1 << 1
+    eFlagsIntegratedCPP = 1 << 2
+
+    def __init__(self, name, flags = 0):
+        self.name = name
+        self.flags = flags
+
+    def acceptsPipedInput(self):
+        return not not (self.flags & Tool.eFlagsPipedInput)
+    def canPipeOutput(self):
+        return not not (self.flags & Tool.eFlagsPipedOutput)
+    def hasIntegratedCPP(self):
+        return not not (self.flags & Tool.eFlagsIntegratedCPP)
+
+class GCC_Common_Tool(Tool):
+    def constructJob(self, phase, arch, jobs, inputs, 
+                     output, outputType, args,
+                     extraArgs):
+        assert len(inputs) == 1
+
+        input = inputs[0]
+
+        cmd_args = args + extraArgs
+        if arch:
+            # FIXME: Clean this up.
+            if isinstance(arch, Arguments.DerivedArg):
+                cmd_args.extend([Arguments.DerivedArg('-arch'),
+                                 arch])
+            else:
+                cmd_args.append(arch)
+        if isinstance(output, Jobs.PipedJob):
+            cmd_args.extend([Arguments.DerivedArg('-o'), Arguments.DerivedArg('-')])
+        elif output is None:
+            cmd_args.append(Arguments.DerivedArg('-fsyntax-only'))
+        else:
+            # FIXME: Ditch this hack.
+            if isinstance(output, Arguments.DerivedArg):
+                cmd_args.extend([Arguments.DerivedArg('-o'), output])
+            else:
+                cmd_args.append(output)
+
+        cmd_args.extend([Arguments.DerivedArg('-x'),
+                         Arguments.DerivedArg(input.type.name)])
+        if isinstance(input.source, Jobs.PipedJob):
+            cmd_args.append(Arguments.DerivedArg('-'))
+        else:
+            cmd_args.append(input.source)
+
+        jobs.addJob(Jobs.Command('gcc', cmd_args))
+
+class GCC_PreprocessTool(GCC_Common_Tool):
+    def __init__(self):
+        super(GCC_PreprocessTool, self).__init__('gcc',
+                                                 (Tool.eFlagsPipedInput |
+                                                  Tool.eFlagsPipedOutput))
+
+    def constructJob(self, phase, arch, jobs, inputs, 
+                     output, outputType, args):
+        return super(GCC_PreprocessTool, self).constructJob(phase, arch, jobs, inputs,
+                                                            output, outputType, args,
+                                                            [Arguments.DerivedArg('-E')])
+
+class GCC_CompileTool(GCC_Common_Tool):
+    def __init__(self):
+        super(GCC_CompileTool, self).__init__('gcc',
+                                              (Tool.eFlagsPipedInput |
+                                               Tool.eFlagsPipedOutput |
+                                               Tool.eFlagsIntegratedCPP))
+
+    def constructJob(self, phase, arch, jobs, inputs, 
+                     output, outputType, args):
+        return super(GCC_CompileTool, self).constructJob(phase, arch, jobs, inputs,
+                                                         output, outputType, args,
+                                                         [Arguments.DerivedArg('-S')])
+
+class GCC_PrecompileTool(GCC_Common_Tool):
+    def __init__(self):
+        super(GCC_PrecompileTool, self).__init__('gcc',
+                                                 (Tool.eFlagsPipedInput |
+                                                  Tool.eFlagsIntegratedCPP))
+
+    def constructJob(self, phase, arch, jobs, inputs, 
+                     output, outputType, args):
+        return super(GCC_PrecompileTool, self).constructJob(phase, arch, jobs, inputs,
+                                                            output, outputType, args,
+                                                            [])
+
+class DarwinAssemblerTool(Tool):
+    def __init__(self):
+        super(DarwinAssemblerTool, self).__init__('as',
+                                                  Tool.eFlagsPipedInput)
+
+    def constructJob(self, phase, arch, jobs, inputs, 
+                     output, outputType, args):
+        assert len(inputs) == 1
+        assert outputType is Types.ObjectType
+
+        input = inputs[0]
+
+        cmd_args = []
+        if arch:
+            # FIXME: Clean this up.
+            if isinstance(arch, Arguments.DerivedArg):
+                cmd_args.extend([Arguments.DerivedArg('-arch'),
+                                 arch])
+            else:
+                cmd_args.append(arch)
+        cmd_args.append(Arguments.DerivedArg('-force_cpusubtype_ALL'))
+        if isinstance(output, Arguments.DerivedArg):
+            cmd_args.extend([Arguments.DerivedArg('-o'), output])
+        else:
+            cmd_args.append(output)
+        if isinstance(input.source, Jobs.PipedJob):
+            cmd_args.append(Arguments.DerivedArg('-'))
+        else:
+            cmd_args.append(input.source)
+        jobs.addJob(Jobs.Command('as', cmd_args))
+
+class Collect2Tool(Tool):
+    kCollect2Path = '/usr/libexec/gcc/i686-apple-darwin10/4.2.1/collect2'
+    def __init__(self):
+        super(Collect2Tool, self).__init__('collect2')
+
+    def constructJob(self, phase, arch, jobs, inputs,
+                     output, outputType, args):
+        assert outputType is Types.ImageType
+
+        cmd_args = []
+        for arg in args:
+            if arg.opt:
+                if arg.opt.name in ('-framework',):
+                    cmd_args.append(arg)
+        for input in inputs:
+            cmd_args.append(input.source)            
+        if isinstance(output, Arguments.DerivedArg):
+            cmd_args.extend([Arguments.DerivedArg('-o'), output])
+        else:
+            cmd_args.append(output)
+        cmd_args.extend([Arguments.DerivedArg('-L/usr/lib/gcc/i686-apple-darwin10/4.2.1'),
+                         Arguments.DerivedArg('-lcrt1.10.5.o'),
+                         Arguments.DerivedArg('-lgcc_s.10.5'),
+                         Arguments.DerivedArg('-lgcc'),
+                         Arguments.DerivedArg('-lSystem')])
+        jobs.addJob(Jobs.Command(self.kCollect2Path, cmd_args))
+
+class LipoTool(Tool):
+    def __init__(self):
+        super(LipoTool, self).__init__('lipo')
+
+    def constructJob(self, phase, arch, jobs, inputs,
+                     output, outputType, args):
+        assert outputType is Types.ImageType
+
+        cmd_args = [Arguments.DerivedArg('-create')]
+        if isinstance(output, Arguments.DerivedArg):
+            cmd_args.extend([Arguments.DerivedArg('-o'), output])
+        else:
+            cmd_args.append(output)
+        for input in inputs:
+            cmd_args.append(input.source)
+        jobs.addJob(Jobs.Command('lipo', cmd_args))

Added: cfe/trunk/tools/ccc/ccclib/Types.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/ccc/ccclib/Types.py?rev=61739&view=auto

==============================================================================
--- cfe/trunk/tools/ccc/ccclib/Types.py (added)
+++ cfe/trunk/tools/ccc/ccclib/Types.py Mon Jan  5 13:53:30 2009
@@ -0,0 +1,123 @@
+class InputType(object):
+    """InputType - Information about various classes of files which
+    the driver recognizes and control processing."""
+    
+    def __init__(self, name, preprocess=None, onlyAssemble=False, 
+                 onlyPrecompile=False, tempSuffix=None):
+        assert preprocess is None or isinstance(preprocess, InputType)
+        self.name = name
+        self.preprocess = preprocess
+        self.onlyAssemble = onlyAssemble
+        self.onlyPrecompile = onlyPrecompile
+        self.tempSuffix = tempSuffix
+
+    def __repr__(self):
+        return '%s(%r, %r, %r, %r, %r)' % (self.__class__.__name__,
+                                           self.name,
+                                           self.preprocess, 
+                                           self.onlyAssemble,
+                                           self.onlyPrecompile,
+                                           self.tempSuffix)
+
+# C family source language (with and without preprocessing).
+CTypeNoPP = InputType('cpp-output', tempSuffix='i')
+CType = InputType('c', CTypeNoPP)
+ObjCTypeNoPP = InputType('objective-c-cpp-output', tempSuffix='mi')
+ObjCType = InputType('objective-c', ObjCTypeNoPP)
+CXXTypeNoPP = InputType('c++-cpp-output', tempSuffix='ii')
+CXXType = InputType('c++', CXXTypeNoPP)
+ObjCXXTypeNoPP = InputType('objective-c++-cpp-output', tempSuffix='mii')
+ObjCXXType = InputType('c++', ObjCXXTypeNoPP)
+
+# C family input files to precompile.
+CHeaderNoPPType = InputType('c-header-cpp-output', onlyPrecompile=True, tempSuffix='pch')
+CHeaderType = InputType('c-header', CHeaderNoPPType, onlyPrecompile=True)
+ObjCHeaderNoPPType = InputType('objective-c-header-cpp-output', onlyPrecompile=True, tempSuffix='pch')
+ObjCHeaderType = InputType('objective-c-header', ObjCHeaderNoPPType, onlyPrecompile=True)
+CXXHeaderNoPPType = InputType('c++-header-cpp-output', onlyPrecompile=True, tempSuffix='pch')
+CXXHeaderType = InputType('c++-header', CXXHeaderNoPPType, onlyPrecompile=True)
+ObjCXXHeaderNoPPType = InputType('objective-c++-header-cpp-output', onlyPrecompile=True, tempSuffix='pch')
+ObjCXXHeaderType = InputType('objective-c++-header', ObjCXXHeaderNoPPType, onlyPrecompile=True)
+
+# Other languages.
+AdaType = InputType('ada')
+AsmTypeNoPP = InputType('assembler', onlyAssemble=True, tempSuffix='s')
+AsmType = InputType('assembler-with-cpp', AsmTypeNoPP, onlyAssemble=True)
+FortranTypeNoPP = InputType('fortran')
+FortranType = InputType('fortran', FortranTypeNoPP)
+JavaType = InputType('java')
+
+# Misc.
+PCHType = InputType('precompiled-header')
+ObjectType = InputType('object', tempSuffix='o')
+TreelangType = InputType('treelang')
+ImageType = InputType('image', tempSuffix='out')
+NothingType = InputType('nothing')
+
+###
+
+kDefaultOutput = "a.out"
+kTypeSuffixMap = {
+    '.c' : CType,
+    '.i' : CTypeNoPP,
+    '.ii' : CXXTypeNoPP,
+    '.m' : ObjCType,
+    '.mi' : ObjCTypeNoPP,
+    '.mm' : ObjCXXType,
+    '.M' : ObjCXXType,
+    '.mii' : ObjCXXTypeNoPP,
+    '.h' : CHeaderType,
+    '.cc' : CXXType,
+    '.cc' : CXXType,
+    '.cp' : CXXType,
+    '.cxx' : CXXType,
+    '.cpp' : CXXType,
+    '.CPP' : CXXType,
+    '.cXX' : CXXType,
+    '.C' : CXXType,
+    '.hh' : CXXHeaderType,
+    '.H' : CXXHeaderType,
+    '.f' : FortranTypeNoPP,
+    '.for' : FortranTypeNoPP,
+    '.FOR' : FortranTypeNoPP,
+    '.F' : FortranType,
+    '.fpp' : FortranType,
+    '.FPP' : FortranType,
+    '.f90' : FortranTypeNoPP,
+    '.f95' : FortranTypeNoPP,
+    '.F90' : FortranType,
+    '.F95' : FortranType,
+    # Apparently the Ada F-E hardcodes these suffixes in many
+    # places. This explains why there is only one -x option for ada.
+    '.ads' : AdaType,
+    '.adb' : AdaType,
+    # FIXME: Darwin always uses a preprocessor for asm input. Where
+    # does this fit?
+    '.s' : AsmTypeNoPP,
+    '.S' : AsmType,
+}
+kTypeSpecifierMap = {
+    'none' : None,
+
+    'c' : CType,
+    'c-header' : CHeaderType,
+    # NOTE: gcc.info claims c-cpp-output works but the actual spelling
+    # is cpp-output. Nice.
+    'cpp-output' : CTypeNoPP,
+    'c++' : CXXType, 
+    'c++-header' : CXXHeaderType,
+    'c++-cpp-output' : CXXTypeNoPP,
+    'objective-c' : ObjCType,
+    'objective-c-header' : ObjCHeaderType,
+    'objective-c-cpp-output' : ObjCTypeNoPP,
+    'objective-c++' : ObjCXXType,
+    'objective-c++-header' : ObjCXXHeaderType,
+    'objective-c++-cpp-output' : ObjCXXTypeNoPP,
+    'assembler' : AsmTypeNoPP,
+    'assembler-with-cpp' : AsmType,
+    'ada' : AdaType,
+    'f95' : FortranType, 
+    'f95-cpp-input' : FortranTypeNoPP,
+    'java' : JavaType,
+    'treelang' : TreelangType,
+}

Added: cfe/trunk/tools/ccc/ccclib/Util.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/ccc/ccclib/Util.py?rev=61739&view=auto

==============================================================================
--- cfe/trunk/tools/ccc/ccclib/Util.py (added)
+++ cfe/trunk/tools/ccc/ccclib/Util.py Mon Jan  5 13:53:30 2009
@@ -0,0 +1,52 @@
+def any_true(list, predicate):
+    for i in list:
+        if predicate(i):
+            return True
+    return False
+
+def any_false(list, predicate):
+    return any_true(list, lambda x: not predicate(x))
+
+def all_true(list, predicate):
+    return not any_false(list, predicate)
+
+def all_false(list, predicate):
+    return not any_true(list, predicate)
+
+def prependLines(prependStr, str):
+    return ('\n'+prependStr).join(str.splitlines())
+
+def pprint(object, useRepr=True):
+    def recur(ob):
+        return pprint(ob, useRepr)
+    def wrapString(prefix, string, suffix):
+        return '%s%s%s' % (prefix, 
+                           prependLines(' ' * len(prefix),
+                                        string),
+                           suffix)
+    def pprintArgs(name, args):
+        return wrapString(name + '(', ',\n'.join(map(recur,args)), ')')
+                            
+    if isinstance(object, tuple):
+        return wrapString('(', ',\n'.join(map(recur,object)), 
+                          [')',',)'][len(object) == 1])
+    elif isinstance(object, list):
+        return wrapString('[', ',\n'.join(map(recur,object)), ']')
+    elif isinstance(object, set):
+        return pprintArgs('set', list(object))
+    elif isinstance(object, dict):
+        elts = []
+        for k,v in object.items():
+            kr = recur(k)
+            vr = recur(v)
+            elts.append('%s : %s' % (kr, 
+                                     prependLines(' ' * (3 + len(kr.splitlines()[-1])),
+                                                  vr)))
+        return wrapString('{', ',\n'.join(elts), '}')
+    else:
+        if useRepr:
+            return repr(object)
+        return str(object)
+
+def prefixAndPPrint(prefix, object, useRepr=True):
+    return prefix + prependLines(' '*len(prefix), pprint(object, useRepr))

Added: cfe/trunk/tools/ccc/ccclib/__init__.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/ccc/ccclib/__init__.py?rev=61739&view=auto

==============================================================================
    (empty)

Added: cfe/trunk/tools/ccc/test/ccc/Xarch.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/ccc/test/ccc/Xarch.c?rev=61739&view=auto

==============================================================================
--- cfe/trunk/tools/ccc/test/ccc/Xarch.c (added)
+++ cfe/trunk/tools/ccc/test/ccc/Xarch.c Mon Jan  5 13:53:30 2009
@@ -0,0 +1,8 @@
+// RUN: xcc -### -fsyntax-only -Xarch_i386 -Wall -Xarch_ppc -Wunused -arch i386 -arch ppc %s > %t &&
+// RUN: grep '"-Xarch"' %t | count 0 &&
+// RUN: grep '"-Wall"' %t | count 1 &&
+// RUN: grep '"-arch" "i386"' %t | count 1 &&
+// RUN: grep '"-Wall"' %t | grep '"-arch" "i386"' | count 1 &&
+// RUN: grep '"-Wunused"' %t | count 1 &&
+// RUN: grep '"-arch" "ppc"' %t | count 1 &&
+// RUN: grep '"-Wunused"' %t | grep '"-arch" "ppc"' | count 1

Added: cfe/trunk/tools/ccc/test/ccc/argument-types.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/ccc/test/ccc/argument-types.c?rev=61739&view=auto

==============================================================================
--- cfe/trunk/tools/ccc/test/ccc/argument-types.c (added)
+++ cfe/trunk/tools/ccc/test/ccc/argument-types.c Mon Jan  5 13:53:30 2009
@@ -0,0 +1,16 @@
+// Input argument:
+// RUN: xcc -ccc-print-options %s | grep 'Name: "<input>", Values: {"%s"}' | count 1 &&
+
+// Joined or separate arguments:
+// RUN: xcc -ccc-print-options -xc -x c | grep 'Name: "-x", Values: {"c"}' | count 2 &&
+
+// Joined and separate arguments:
+// RUN: xcc -ccc-print-options -Xarch_mips -run | grep 'Name: "-Xarch_", Values: {"mips", "-run"}' | count 1 &&
+
+// Multiple arguments:
+// RUN: xcc -ccc-print-options -sectorder 1 2 3 | grep 'Name: "-sectorder", Values: {"1", "2", "3"}' | count 1 &&
+
+// Unknown argument:
+// RUN: xcc -ccc-print-options -=== | grep 'Name: "<unknown>", Values: {"-==="}' | count 1 && 
+
+// RUN: true

Added: cfe/trunk/tools/ccc/test/ccc/hello.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/ccc/test/ccc/hello.c?rev=61739&view=auto

==============================================================================
--- cfe/trunk/tools/ccc/test/ccc/hello.c (added)
+++ cfe/trunk/tools/ccc/test/ccc/hello.c Mon Jan  5 13:53:30 2009
@@ -0,0 +1,7 @@
+// RUN: xcc %s -o %t &&
+// RUN: %t | grep "Hello, World"
+
+int main() {
+  printf("Hello, World!\n");
+  return 0;
+}

Added: cfe/trunk/tools/ccc/test/ccc/integrated-cpp.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/ccc/test/ccc/integrated-cpp.c?rev=61739&view=auto

==============================================================================
--- cfe/trunk/tools/ccc/test/ccc/integrated-cpp.c (added)
+++ cfe/trunk/tools/ccc/test/ccc/integrated-cpp.c Mon Jan  5 13:53:30 2009
@@ -0,0 +1,3 @@
+// RUN: xcc -fsyntax-only -### %s | count 1 &&
+// RUN: xcc -fsyntax-only -### %s -no-integrated-cpp | count 2 &&
+// RUN: xcc -fsyntax-only -### %s -save-temps | count 2

Added: cfe/trunk/tools/ccc/test/ccc/invalid.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/ccc/test/ccc/invalid.c?rev=61739&view=auto

==============================================================================
--- cfe/trunk/tools/ccc/test/ccc/invalid.c (added)
+++ cfe/trunk/tools/ccc/test/ccc/invalid.c Mon Jan  5 13:53:30 2009
@@ -0,0 +1 @@
+// RUN: not xcc -### -c -o %t %s %s

Added: cfe/trunk/tools/ccc/test/ccc/phases.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/ccc/test/ccc/phases.c?rev=61739&view=auto

==============================================================================
--- cfe/trunk/tools/ccc/test/ccc/phases.c (added)
+++ cfe/trunk/tools/ccc/test/ccc/phases.c Mon Jan  5 13:53:30 2009
@@ -0,0 +1,46 @@
+// One C file.
+// RUN: xcc -ccc-no-driver-driver -ccc-print-phases a.c > %t &&
+// RUN: grep '0: input, "a.c", c' %t &&
+// RUN: grep '1: preprocessor, {0}, cpp-output' %t &&
+// RUN: grep '2: compiler, {1}, assembler' %t &&
+// RUN: grep '3: assembler, {2}, object' %t &&
+// RUN: grep '4: linker, {3}, image' %t &&
+
+// PCH.
+// RUN: xcc -ccc-no-driver-driver -ccc-print-phases -x c-header a.h > %t &&
+// RUN: grep '0: input, "a.h", c-header' %t &&
+// RUN: grep '1: preprocessor, {0}, c-header-cpp-output' %t &&
+// RUN: grep '2: precompiler, {1}, precompiled-header' %t &&
+
+// Assembler w/ and w/o preprocessor.
+// RUN: xcc -ccc-no-driver-driver -ccc-print-phases -x assembler a.s > %t &&
+// RUN: grep '0: input, "a.s", assembler' %t &&
+// RUN: grep '1: assembler, {0}, object' %t &&
+// RUN: grep '2: linker, {1}, image' %t &&
+// RUN: xcc -ccc-no-driver-driver -ccc-print-phases -x assembler-with-cpp a.s > %t &&
+// RUN: grep '0: input, "a.s", assembler-with-cpp' %t &&
+// RUN: grep '1: preprocessor, {0}, assembler' %t &&
+// RUN: grep '2: assembler, {1}, object' %t &&
+// RUN: grep '3: linker, {2}, image' %t &&
+
+// Check the various ways of early termination.
+// RUN: xcc -ccc-no-driver-driver -ccc-print-phases -E a.c > %t &&
+// RUN: not grep ': compiler, ' %t &&
+// RUN: xcc -ccc-no-driver-driver -ccc-print-phases -fsyntax-only a.c > %t &&
+// RUN: grep ': compiler, {1}, nothing' %t &&
+// RUN: not grep ': assembler, ' %t &&
+// RUN: xcc -ccc-no-driver-driver -ccc-print-phases -S a.c > %t &&
+// RUN: not grep ': assembler, ' %t &&
+// RUN: xcc -ccc-no-driver-driver -ccc-print-phases -c a.c > %t &&
+// RUN: not grep ': linker, ' %t &&
+
+// Multiple output files.
+// RUN: xcc -ccc-no-driver-driver -ccc-print-phases -c a.c b.c > %t &&
+// RUN: grep ': assembler,' %t | count 2 &&
+
+// FIXME: Only for darwin.
+// Treat -filelist as a linker input.
+// RUN: xcc -ccc-no-driver-driver -ccc-print-phases -filelist /dev/null > %t &&
+// RUN: grep '1: linker, {0}, image' %t &&
+
+// RUN: true

Added: cfe/trunk/tools/ccc/test/ccc/universal-hello.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/ccc/test/ccc/universal-hello.c?rev=61739&view=auto

==============================================================================
--- cfe/trunk/tools/ccc/test/ccc/universal-hello.c (added)
+++ cfe/trunk/tools/ccc/test/ccc/universal-hello.c Mon Jan  5 13:53:30 2009
@@ -0,0 +1,7 @@
+// RUN: xcc -arch ppc -arch i386 -arch x86_64 %s -o %t &&
+// RUN: %t | grep "Hello, World"
+
+int main() {
+  printf("Hello, World!\n");
+  return 0;
+}

Added: cfe/trunk/tools/ccc/xcc
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/ccc/xcc?rev=61739&view=auto

==============================================================================
--- cfe/trunk/tools/ccc/xcc (added)
+++ cfe/trunk/tools/ccc/xcc Mon Jan  5 13:53:30 2009
@@ -0,0 +1,12 @@
+#!/usr/bin/python
+
+import sys
+from ccclib import Driver
+        
+def main():
+    d = Driver.Driver()
+    # FIXME: We should pass program name here as well.
+    d.run(sys.argv[1:])
+
+if __name__=='__main__':
+    main()

Propchange: cfe/trunk/tools/ccc/xcc

------------------------------------------------------------------------------
    svn:executable = *





More information about the cfe-commits mailing list