Add a QT tool. (Christoph Wiedemann)
[scons.git] / src / engine / SCons / Builder.py
index 08d8f0cf05b5f7ced3638b3d16fc8808dd236afa..2bcd9935bfd47aaa62392d97c82ae4aedb85d721 100644 (file)
@@ -19,7 +19,7 @@ dive any deeper into this subsystem.
 """
 
 #
-# Copyright (c) 2001, 2002 Steven Knight
+# __COPYRIGHT__
 #
 # Permission is hereby granted, free of charge, to any person obtaining
 # a copy of this software and associated documentation files (the
@@ -46,11 +46,10 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 
 import os.path
-import string
-import copy
-from SCons.Errors import UserError
+from SCons.Errors import InternalError, UserError
 
 import SCons.Action
+import SCons.Executor
 import SCons.Node
 import SCons.Node.FS
 import SCons.Util
@@ -84,26 +83,37 @@ class DictCmdGenerator:
         for src in map(str, source):
             my_ext = SCons.Util.splitext(src)[1]
             if ext and my_ext != ext:
-                raise UserError("Cannot build multiple sources with different extensions: %s, %s" % (ext, my_ext))
+                raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s" % (repr(map(str, target)), src, ext, my_ext))
             ext = my_ext
 
-        if ext is None:
-            raise UserError("Cannot deduce file extension from source files: %s" % repr(map(str, source)))
+        if not ext:
+            raise UserError("While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source))))
         try:
-            # XXX Do we need to perform Environment substitution
-            # on the keys of action_dict before looking it up?
             return self.action_dict[ext]
         except KeyError:
-            raise UserError("Don't know how to build a file with suffix %s." % ext)
+            # Before raising the user error, try to perform Environment
+            # substitution on the keys of action_dict.
+            s_dict = {}
+            for (k,v) in self.action_dict.items():
+                s_k = env.subst(k)
+                if s_dict.has_key(s_k):
+                    # XXX Note that we do only raise errors, when variables
+                    # point to the same suffix. If one suffix is a
+                    # literal and a variable suffix contains this literal
+                    # we don't raise an error (cause the literal 'wins')
+                    raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (s_dict[s_k][0], k, s_k))
+                s_dict[s_k] = (k,v)
+            try:
+                return s_dict[ext][1]
+            except KeyError:
+                raise UserError("While building `%s': Don't know how to build a file with suffix %s." % (repr(map(str, target)), repr(ext)))
+
     def __cmp__(self, other):
         return cmp(self.action_dict, other.action_dict)
 
 def Builder(**kw):
     """A factory for builder objects."""
     composite = None
-    if kw.has_key('name'):
-        SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
-                            "The use of the 'name' parameter to Builder() is deprecated.")
     if kw.has_key('generator'):
         if kw.has_key('action'):
             raise UserError, "You must not specify both an action and a generator."
@@ -140,16 +150,12 @@ def _init_nodes(builder, env, overrides, tlist, slist):
     the proper Builder information.
     """
 
-    for s in slist:
-        src_key = s.scanner_key()        # the file suffix
-        scanner = env.get_scanner(src_key)
-        if scanner:
-            s.source_scanner = scanner
-
+    # First, figure out if there are any errors in the way the targets
+    # were specified.
     for t in tlist:
         if t.side_effect:
             raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
-        if t.builder is not None:
+        if t.has_builder():
             if t.env != env:
                 raise UserError, "Two different environments were specified for the same target: %s"%str(t)
             elif t.overrides != overrides:
@@ -166,14 +172,43 @@ def _init_nodes(builder, env, overrides, tlist, slist):
             elif t.sources != slist:
                 raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
 
+    # The targets are fine, so find or make the appropriate Executor to
+    # build this particular list of targets from this particular list of
+    # sources.
+    executor = None
+    if builder.multi:
+        try:
+            executor = tlist[0].get_executor(create = 0)
+        except AttributeError:
+            pass
+        else:
+            executor.add_sources(slist)
+    if executor is None:
+        executor = SCons.Executor.Executor(builder,
+                                           tlist[0].generate_build_env(env),
+                                           overrides,
+                                           tlist,
+                                           slist)
+
+    # Now set up the relevant information in the target Nodes themselves.
+    for t in tlist:
         t.overrides = overrides
         t.cwd = SCons.Node.FS.default_fs.getcwd()
         t.builder_set(builder)
         t.env_set(env)
         t.add_source(slist)
+        t.set_executor(executor)
         if builder.scanner:
             t.target_scanner = builder.scanner
 
+    # Last, add scanners from the Environment to the source Nodes.
+    for s in slist:
+        src_key = s.scanner_key()        # the file suffix
+        scanner = env.get_scanner(src_key)
+        if scanner:
+            s.source_scanner = scanner
+
+
 def _adjust_suffix(suff):
     if suff and not suff[0] in [ '.', '$' ]:
         return '.' + suff
@@ -211,8 +246,7 @@ class BuilderBase:
     nodes (files) from input nodes (files).
     """
 
-    def __init__(self,  name = None,
-                        action = None,
+    def __init__(self,  action = None,
                         prefix = '',
                         suffix = '',
                         src_suffix = '',
@@ -221,12 +255,15 @@ class BuilderBase:
                         source_factory = None,
                         scanner = None,
                         emitter = None,
-                        multi = 0):
-        self.name = name
+                        multi = 0,
+                        env = None,
+                        overrides = {}):
         self.action = SCons.Action.Action(action)
         self.multi = multi
         self.prefix = prefix
         self.suffix = suffix
+        self.env = env
+        self.overrides = overrides
 
         self.set_src_suffix(src_suffix)
 
@@ -236,16 +273,16 @@ class BuilderBase:
 
         self.emitter = emitter
 
+    def __nonzero__(self):
+        raise InternalError, "Do not test for the Node.builder attribute directly; use Node.has_builder() instead"
+
     def get_name(self, env):
         """Attempts to get the name of the Builder.
 
-        If the Builder's name attribute is None, then we will look at
-        the BUILDERS variable of env, expecting it to be a dictionary
-        containing this Builder, and we will return the key of the
+        Look at the BUILDERS variable of env, expecting it to be a
+        dictionary containing this Builder, and return the key of the
         dictionary."""
 
-        if self.name:
-            return self.name
         try:
             index = env['BUILDERS'].values().index(self)
             return env['BUILDERS'].keys()[index]
@@ -267,9 +304,10 @@ class BuilderBase:
 
             for f in files:
                 if SCons.Util.is_String(f):
-                    if pre and f[:len(pre)] != pre:
+                    if pre:
                         path, fn = os.path.split(os.path.normpath(f))
-                        f = os.path.join(path, pre + fn)
+                        if fn[:len(pre)] != pre:
+                            f = os.path.join(path, pre + fn)
                     # Only append a suffix if the file does not have one.
                     if suf and not SCons.Util.splitext(f)[1]:
                         if f[-len(suf):] != suf:
@@ -285,22 +323,47 @@ class BuilderBase:
 
         source = adjustixes(source, None, src_suf)
         if target is None:
-            target = map(lambda x, s=suf:
-                                os.path.splitext(os.path.split(str(x))[1])[0] + s,
-                         source)
+            s = source[0]
+            if isinstance(s, SCons.Node.Node):
+                s = str(s)
+            dir, s = os.path.split(s)
+            target = pre + os.path.splitext(s)[0] + suf
+            if dir:
+                target = [ os.path.join(dir, target) ]
         else:
             target = adjustixes(target, pre, suf)
 
-        if self.emitter:
-            # pass the targets and sources to the emitter as strings
-            # rather than nodes since str(node) doesn't work
-            # properly from any directory other than the top directory,
-            # and emitters are called "in" the SConscript directory:
-            target, source = self.emitter(target=target, source=source, env=env)
-
         slist = SCons.Node.arg2nodes(source, self.source_factory)
         tlist = SCons.Node.arg2nodes(target, self.target_factory)
 
+        if self.emitter:
+            # The emitter is going to do str(node), but because we're
+            # being called *from* a builder invocation, the new targets
+            # don't yet have a builder set on them and will look like
+            # source files.  Fool the emitter's str() calls by setting
+            # up a temporary builder on the new targets.
+            new_targets = []
+            for t in tlist:
+                if not t.is_derived():
+                    t.builder = self
+                    new_targets.append(t)
+        
+            target, source = self.emitter(target=tlist, source=slist, env=env)
+
+            # Now delete the temporary builders that we attached to any
+            # new targets, so that _init_nodes() doesn't do weird stuff
+            # to them because it thinks they already have builders.
+            for t in new_targets:
+                if t.builder is self:
+                    # Only delete the temporary builder if the emitter
+                    # didn't change it on us.
+                    t.builder = None
+
+            # Have to call arg2nodes yet again, since it is legal for
+            # emitters to spit out strings as well as Node instances.
+            slist = SCons.Node.arg2nodes(source, self.source_factory)
+            tlist = SCons.Node.arg2nodes(target, self.target_factory)
+
         return tlist, slist
 
     def __call__(self, env, target = None, source = _null, **overrides):
@@ -317,25 +380,6 @@ class BuilderBase:
 
         return tlist
 
-    def get_actions(self):
-        return self.action.get_actions()
-
-    def execute(self, target, source, env):
-        """Execute a builder's action to create an output object.
-        """
-        return self.action.execute(target, source, env)
-
-    def get_raw_contents(self, target, source, env):
-        """Fetch the "contents" of the builder's action.
-        """
-        return self.action.get_raw_contents(target, source, env)
-
-    def get_contents(self, target, source, env):
-        """Fetch the "contents" of the builder's action
-        (for signature calculation).
-        """
-        return self.action.get_contents(target, source, env)
-
     def src_suffixes(self, env):
         return map(lambda x, e=env: e.subst(_adjust_suffix(x)),
                    self.src_suffix)
@@ -379,21 +423,6 @@ class ListBuilder(SCons.Util.Proxy):
         self.env = env
         self.tlist = tlist
         self.multi = builder.multi
-        self.name = "ListBuilder(%s)"%builder.name
-
-    def execute(self, target, source, env):
-        if hasattr(self, 'status'):
-            return self.status
-        for t in self.tlist:
-            # unlink all targets and make all directories
-            # before building anything
-            t.prepare()
-        target = self.tlist
-        self.status = self.builder.execute(target, source, env)
-        for t in self.tlist:
-            if not t is target:
-                t.build()
-        return self.status
 
     def targets(self, node):
         """Return the list of targets for this builder instance.
@@ -404,12 +433,7 @@ class ListBuilder(SCons.Util.Proxy):
         return cmp(self.__dict__, other.__dict__)
 
     def get_name(self, env):
-        """Attempts to get the name of the Builder.
-
-        If the Builder's name attribute is None, then we will look at
-        the BUILDERS variable of env, expecting it to be a dictionary
-        containing this Builder, and we will return the key of the
-        dictionary."""
+        """Attempts to get the name of the Builder."""
 
         return "ListBuilder(%s)" % self.builder.get_name(env)
 
@@ -425,7 +449,6 @@ class MultiStepBuilder(BuilderBase):
     src_suffix.
     """
     def __init__(self,  src_builder,
-                        name = None,
                         action = None,
                         prefix = '',
                         suffix = '',
@@ -435,7 +458,7 @@ class MultiStepBuilder(BuilderBase):
                         source_factory = None,
                         scanner=None,
                         emitter=None):
-        BuilderBase.__init__(self, name, action, prefix, suffix, src_suffix,
+        BuilderBase.__init__(self, action, prefix, suffix, src_suffix,
                              node_factory, target_factory, source_factory,
                              scanner, emitter)
         if not SCons.Util.is_List(src_builder):
@@ -444,7 +467,11 @@ class MultiStepBuilder(BuilderBase):
         self.sdict = {}
         self.cached_src_suffixes = {} # source suffixes keyed on id(env)
 
-    def __call__(self, env, target = None, source = None, **kw):
+    def __call__(self, env, target = None, source = _null, **kw):
+        if source is _null:
+            source = target
+            target = None
+
         slist = SCons.Node.arg2nodes(source, self.source_factory)
         final_sources = []
 
@@ -465,7 +492,7 @@ class MultiStepBuilder(BuilderBase):
         src_suffixes = self.src_suffixes(env)
 
         for snode in slist:
-            path, ext = SCons.Util.splitext(snode.abspath)
+            path, ext = SCons.Util.splitext(snode.get_abspath())
             if sdict.has_key(ext):
                 src_bld = sdict[ext]
                 tgt = apply(src_bld, (env, path, snode), kw)