summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2002-06-05 23:35:56 (GMT)
committerSteven Knight <knight@baldmt.com>2002-06-05 23:35:56 (GMT)
commit2f7d4f660fd048edc342a989d25c94d7b52ab13e (patch)
tree20def5c6f0f7b68ab7151cac4ada5c470a3b31e0 /src/engine/SCons
parent42717c855f7cbb73d3017ac243a34491d3cc0c53 (diff)
downloadSCons-2f7d4f660fd048edc342a989d25c94d7b52ab13e.zip
SCons-2f7d4f660fd048edc342a989d25c94d7b52ab13e.tar.gz
SCons-2f7d4f660fd048edc342a989d25c94d7b52ab13e.tar.bz2
Changes from Charles Crain.
Diffstat (limited to 'src/engine/SCons')
-rw-r--r--src/engine/SCons/Builder.py31
-rw-r--r--src/engine/SCons/BuilderTests.py24
-rw-r--r--src/engine/SCons/Defaults.py58
-rw-r--r--src/engine/SCons/Environment.py74
-rw-r--r--src/engine/SCons/EnvironmentTests.py44
-rw-r--r--src/engine/SCons/Scanner/C.py4
-rw-r--r--src/engine/SCons/Scanner/CTests.py13
-rw-r--r--src/engine/SCons/Script/__init__.py102
-rw-r--r--src/engine/SCons/Warnings.py81
-rw-r--r--src/engine/SCons/WarningsTests.py115
10 files changed, 472 insertions, 74 deletions
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index 42c9484..d3feb26 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -40,6 +40,7 @@ import SCons.Action
import SCons.Node
import SCons.Node.FS
import SCons.Util
+import SCons.Warnings
class DictCmdGenerator:
"""This is a callable class that can be used as a
@@ -73,6 +74,9 @@ class DictCmdGenerator:
def Builder(**kw):
"""A factory for builder objects."""
+ 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."
@@ -124,7 +128,7 @@ def _init_nodes(builder, env, args, tlist, slist):
if isinstance(t.builder, ListBuilder) and isinstance(builder, ListBuilder) and t.builder.builder == builder.builder:
raise UserError, "Two different target sets have a target in common: %s"%str(t)
else:
- raise UserError, "Two different builders (%s and %s) were specified for the same target: %s"%(t.builder.name, builder.name, str(t))
+ raise UserError, "Two different builders (%s and %s) were specified for the same target: %s"%(t.builder.get_name(env), builder.get_name(env), str(t))
elif t.sources != slist:
raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
@@ -202,8 +206,6 @@ class BuilderBase:
scanner = None,
emitter = None,
multi = 0):
- if name is None:
- raise UserError, "You must specify a name for the builder."
self.name = name
self.action = SCons.Action.Action(action)
self.multi = multi
@@ -231,6 +233,22 @@ class BuilderBase:
self.emitter = emitter
+ 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."""
+
+ if self.name:
+ return self.name
+ try:
+ index = env['BUILDERS'].values().index(self)
+ return env['BUILDERS'].keys()[index]
+ except (AttributeError, KeyError, ValueError):
+ return str(self.__class__)
+
def __cmp__(self, other):
return cmp(self.__dict__, other.__dict__)
@@ -342,8 +360,11 @@ class ListBuilder:
def __init__(self, builder, env, tlist):
self.builder = builder
- self.tlist = tlist
- self.name = "ListBuilder(%s)"%builder.name
+ self.tlist = tlist
+ self.name = "ListBuilder(%s)"%builder.get_name(env)
+
+ def get_name(self, env):
+ return self.name
def execute(self, **kw):
if hasattr(self, 'status'):
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index d7fd92d..c69aaa6 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -39,6 +39,7 @@ import TestCmd
import SCons.Builder
import SCons.Errors
import SCons.Node.FS
+import SCons.Warnings
# Initial setup of the common environment for all tests,
# a temporary working directory containing a
@@ -158,17 +159,24 @@ class BuilderTestCase(unittest.TestCase):
assert target.sources[1].name == uni('n19')
def test_noname(self):
- """Test error reporting for missing name
+ """Test deprecated warning for Builder name.
- Verify that the Builder constructor gives an error message if the
- name is missing.
+ Using the name argument for Builder() is deprectaed and the
+ user should receive a warning.
"""
+ SCons.Warnings.enableWarningClass(SCons.Warnings.DeprecatedWarning)
+ SCons.Warnings.warningAsException(1)
+
try:
- b = SCons.Builder.Builder()
- except SCons.Errors.UserError:
- pass
- else:
- assert 0
+ try:
+ b = SCons.Builder.Builder(name='foo')
+ except SCons.Warnings.DeprecatedWarning:
+ pass
+ else:
+ assert 0
+ finally:
+ SCons.Warnings.suppressWarningClass(SCons.Warnings.DeprecatedWarning)
+ SCons.Warnings.warningAsException(0)
def test_action(self):
"""Test Builder creation
diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py
index 2259f40..e8bf376 100644
--- a/src/engine/SCons/Defaults.py
+++ b/src/engine/SCons/Defaults.py
@@ -68,8 +68,9 @@ class SharedCmdGenerator:
def __call__(self, target, source, env, shared=0, **kw):
for src in source:
try:
- if src.attributes.shared != shared:
- raise SCons.Errors.UserError("Source file: %s must be built with shared=%s in order to be compatible with the selected target." % (src, str(shared)))
+ if (src.attributes.shared and not shared) or \
+ (shared and not src.attributes.shared):
+ raise SCons.Errors.UserError("Source file: %s must be built with shared=%s in order to be compatible with target: %s" % (src, str(shared), target[0]))
except AttributeError:
pass
for t in target:
@@ -90,15 +91,13 @@ def yaccEmitter(target, source, env, **kw):
'.h')
return (target, source)
-CFile = SCons.Builder.Builder(name = 'CFile',
- action = { '.l' : '$LEXCOM',
+CFile = SCons.Builder.Builder(action = { '.l' : '$LEXCOM',
'.y' : '$YACCCOM',
},
emitter = yaccEmitter,
suffix = '$CFILESUFFIX')
-CXXFile = SCons.Builder.Builder(name = 'CXXFile',
- action = { '.ll' : '$LEXCOM',
+CXXFile = SCons.Builder.Builder(action = { '.ll' : '$LEXCOM',
'.yy' : '$YACCCOM',
},
emitter = yaccEmitter,
@@ -156,9 +155,8 @@ static_obj = SCons.Builder.DictCmdGenerator({ ".C" : C_static,
".FOR" : F77Action,
".fpp" : F77PPAction,
".FPP" : F77PPAction })
-
-Object = SCons.Builder.Builder(name = 'Object',
- generator = \
+
+Object = SCons.Builder.Builder(generator = \
SharedCmdGenerator(static=SCons.Action.CommandGeneratorAction(static_obj),
shared=SCons.Action.CommandGeneratorAction(shared_obj)),
prefix = '$OBJPREFIX',
@@ -166,7 +164,7 @@ Object = SCons.Builder.Builder(name = 'Object',
src_suffix = static_obj.src_suffixes(),
src_builder = [CFile, CXXFile])
-def win32TempFileMunge(env, cmd_list, for_signature):
+def win32TempFileMunge(env, cmd_list, for_signature):
"""Given a list of command line arguments, see if it is too
long to pass to the win32 command line interpreter. If so,
create a temp file, then pass "@tempfile" as the sole argument
@@ -194,8 +192,7 @@ def win32LinkGenerator(env, target, source, for_signature, **kw):
args.extend(map(SCons.Util.to_String, source))
return win32TempFileMunge(env, args, for_signature)
-Program = SCons.Builder.Builder(name='Program',
- action='$LINKCOM',
+Program = SCons.Builder.Builder(action='$LINKCOM',
prefix='$PROGPREFIX',
suffix='$PROGSUFFIX',
src_suffix='$OBJSUFFIX',
@@ -266,8 +263,7 @@ def win32LibEmitter(target, source, env, shared=0,
env.subst("$LIBSUFFIX")))
return (target, source)
-Library = SCons.Builder.Builder(name = 'Library',
- generator = \
+Library = SCons.Builder.Builder(generator = \
SharedCmdGenerator(shared="$SHLINKCOM",
static="$ARCOM"),
emitter="$LIBEMITTER",
@@ -282,8 +278,7 @@ Library = SCons.Builder.Builder(name = 'Library',
LaTeXAction = SCons.Action.Action('$LATEXCOM')
-DVI = SCons.Builder.Builder(name = 'DVI',
- action = { '.tex' : '$TEXCOM',
+DVI = SCons.Builder.Builder(action = { '.tex' : '$TEXCOM',
'.ltx' : LaTeXAction,
'.latex' : LaTeXAction,
},
@@ -295,8 +290,7 @@ DVI = SCons.Builder.Builder(name = 'DVI',
PDFLaTeXAction = SCons.Action.Action('$PDFLATEXCOM')
-PDF = SCons.Builder.Builder(name = 'PDF',
- action = { '.dvi' : '$PDFCOM',
+PDF = SCons.Builder.Builder(action = { '.dvi' : '$PDFCOM',
'.tex' : '$PDFTEXCOM',
'.ltx' : PDFLaTeXAction,
'.latex' : PDFLaTeXAction,
@@ -304,8 +298,7 @@ PDF = SCons.Builder.Builder(name = 'PDF',
prefix = '$PDFPREFIX',
suffix = '$PDFSUFFIX')
-PostScript = SCons.Builder.Builder(name = 'PostScript',
- action = '$PSCOM',
+PostScript = SCons.Builder.Builder(action = '$PSCOM',
prefix = '$PSPREFIX',
suffix = '$PSSUFFIX',
src_suffix = '.dvi',
@@ -316,8 +309,7 @@ CScan = SCons.Scanner.C.CScan()
def alias_builder(env, target, source):
pass
-Alias = SCons.Builder.Builder(name = 'Alias',
- action = alias_builder,
+Alias = SCons.Builder.Builder(action = alias_builder,
target_factory = SCons.Node.Alias.default_ans.Alias,
source_factory = SCons.Node.FS.default_fs.Entry,
multi = 1)
@@ -481,8 +473,15 @@ def make_win32_env_from_paths(include, lib, path):
'PSCOM' : '$DVIPS $DVIPSFLAGS -o $TARGET $SOURCES',
'PSPREFIX' : '',
'PSSUFFIX' : '.ps',
- 'BUILDERS' : [Alias, CFile, CXXFile, DVI, Library, Object,
- PDF, PostScript, Program],
+ 'BUILDERS' : { 'Alias' : Alias,
+ 'CFile' : CFile,
+ 'CXXFile' : CXXFile,
+ 'DVI' : DVI,
+ 'Library' : Library,
+ 'Object' : Object,
+ 'PDF' : PDF,
+ 'PostScript' : PostScript,
+ 'Program' : Program },
'SCANNERS' : [CScan],
'LIBDIRPREFIX' : '/LIBPATH:',
'LIBDIRSUFFIX' : '',
@@ -583,8 +582,15 @@ if os.name == 'posix':
'PSCOM' : '$DVIPS $DVIPSFLAGS -o $TARGET $SOURCES',
'PSPREFIX' : '',
'PSSUFFIX' : '.ps',
- 'BUILDERS' : [Alias, CFile, CXXFile, DVI, Library, Object,
- PDF, PostScript, Program],
+ 'BUILDERS' : { 'Alias' : Alias,
+ 'CFile' : CFile,
+ 'CXXFile' : CXXFile,
+ 'DVI' : DVI,
+ 'Library' : Library,
+ 'Object' : Object,
+ 'PDF' : PDF,
+ 'PostScript' : PostScript,
+ 'Program' : Program },
'SCANNERS' : [CScan],
'LIBDIRPREFIX' : '-L',
'LIBDIRSUFFIX' : '',
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index 8c61ceb..787a66d 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -45,6 +45,8 @@ from SCons.Errors import UserError
import SCons.Node
import SCons.Node.FS
import SCons.Util
+import SCons.Warnings
+from UserDict import UserDict
def installFunc(env, target, source):
try:
@@ -82,6 +84,29 @@ def our_deepcopy(x):
copy = x
return copy
+class BuilderDict(UserDict):
+ """This is a dictionary-like class used by Environment
+ to hold Builders. We need to do this, because every time
+ someone changes the Builders in the Environment's BUILDERS
+ dictionary, we need to update the Environment's attributes."""
+ def setEnvironment(self, env):
+ self.env = env
+
+ def __setitem__(self, item, val):
+ UserDict.__setitem__(self, item, val)
+ try:
+ self.env.Replace() # re-compute Builders
+ except AttributeError:
+ # Have to catch this because sometimes
+ # __setitem__ gets called out of __init__, when
+ # we don't have an env attribute yet, nor do
+ # we want one!
+ pass
+
+ def __delitem__(self, item):
+ UserDict.__delitem__(self, item)
+ self.env.Replace()
+
class Environment:
"""Base class for construction Environments. These are
the primary objects used to communicate dependency and
@@ -155,14 +180,13 @@ class Environment:
"""
apply(self.Replace, (), kw)
- def Replace(self, **kw):
- """Replace existing construction variables in an Environment
- with new construction variables and/or values.
- """
- self._dict.update(our_deepcopy(kw))
- if self._dict.has_key('BUILDERS') and \
- not SCons.Util.is_List(self._dict['BUILDERS']):
- self._dict['BUILDERS'] = [self._dict['BUILDERS']]
+ def __updateBuildersAndScanners(self):
+ """Update attributes for builders and scanners.
+
+ Have to be careful in this function...we can't
+ call functions like __setitem__() or Replace(), or
+ we will have infinite recursion."""
+
if self._dict.has_key('SCANNERS') and \
not SCons.Util.is_List(self._dict['SCANNERS']):
self._dict['SCANNERS'] = [self._dict['SCANNERS']]
@@ -190,11 +214,37 @@ class Environment:
kw['env'] = self.env
apply(self.builder.execute, (), kw)
- for b in self._dict['BUILDERS']:
- setattr(self, b.name, BuilderWrapper(self, b))
+ if self._dict.has_key('BUILDERS'):
+ if SCons.Util.is_Dict(self._dict['BUILDERS']):
+ bd = self._dict['BUILDERS']
+ if not isinstance(bd, BuilderDict):
+ # Convert it to a BuilderDict. This class
+ # Updates our builder attributes every time
+ # someone changes it.
+ bd = BuilderDict(bd)
+ bd.setEnvironment(self)
+ self._dict['BUILDERS'] = bd
+ for name, builder in bd.items():
+ setattr(self, name, BuilderWrapper(self, builder))
+ else:
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
+ "The use of the BUILDERS Environment variable as a list or Builder instance is deprecated. BUILDERS should be a dictionary of name->Builder instead.")
+
+ if not SCons.Util.is_List(self._dict['BUILDERS']):
+ self._dict['BUILDERS'] = [self._dict['BUILDERS']]
+ for b in self._dict['BUILDERS']:
+ setattr(self, b.name, BuilderWrapper(self, b))
for s in self._dict['SCANNERS']:
setattr(self, s.name, s)
+
+
+ def Replace(self, **kw):
+ """Replace existing construction variables in an Environment
+ with new construction variables and/or values.
+ """
+ self._dict.update(our_deepcopy(kw))
+ self.__updateBuildersAndScanners()
def Append(self, **kw):
"""Append values to existing construction variables
@@ -212,6 +262,7 @@ class Environment:
self._dict[key] = [ self._dict[key] ] + kw[key]
else:
self._dict[key] = self._dict[key] + kw[key]
+ self.__updateBuildersAndScanners()
def Depends(self, target, dependency):
"""Explicity specify that 'target's depend on 'dependency'."""
@@ -257,6 +308,7 @@ class Environment:
def __setitem__(self, key, value):
self._dict[key] = value
+ self.__updateBuildersAndScanners()
def __getitem__(self, key):
return self._dict[key]
@@ -272,7 +324,7 @@ class Environment:
source files using the supplied action. Action may
be any type that the Builder constructor will accept
for an action."""
- bld = SCons.Builder.Builder(name="Command", action=action)
+ bld = SCons.Builder.Builder(action=action)
return bld(self, target, source)
def Install(self, dir, source):
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index 3066fc2..6859ee3 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -27,7 +27,7 @@ import sys
import unittest
from SCons.Environment import *
-
+import SCons.Warnings
built_it = {}
@@ -76,6 +76,7 @@ class EnvironmentTestCase(unittest.TestCase):
b1 = Builder(name = 'builder1')
b2 = Builder(name = 'builder2')
+ # BUILDERS as a list or instance, now deprecated...
built_it = {}
env1 = Environment(BUILDERS = b1)
env1.builder1.execute(target = 'out1')
@@ -100,6 +101,47 @@ class EnvironmentTestCase(unittest.TestCase):
assert env4.builder1.env is env4
assert env4.builder2.env is env4
+ # Now test BUILDERS as a dictionary.
+ built_it = {}
+ env5 = Environment(BUILDERS={ 'foo' : b1 })
+ env5['BUILDERS']['bar'] = b2
+ env5.foo.execute(target='out1')
+ env5.bar.execute(target='out2')
+ assert built_it['out1']
+ assert built_it['out2']
+
+ built_it = {}
+ env6 = Environment()
+ env6['BUILDERS'] = { 'foo' : b1,
+ 'bar' : b2 }
+ env6.foo.execute(target='out1')
+ env6.bar.execute(target='out2')
+ assert built_it['out1']
+ assert built_it['out2']
+
+ # Now test deprecated warning for BUILDERS as a list
+ # or instance.
+
+ SCons.Warnings.enableWarningClass(SCons.Warnings.DeprecatedWarning)
+ SCons.Warnings.warningAsException(1)
+ try:
+ try:
+ env=Environment(BUILDERS=b1)
+ except SCons.Warnings.DeprecatedWarning:
+ pass
+ else:
+ assert 0
+
+ try:
+ env=Environment(BUILDERS=[b1, b2])
+ except SCons.Warnings.DeprecatedWarning:
+ pass
+ else:
+ assert 0
+ finally:
+ SCons.Warnings.suppressWarningClass(SCons.Warnings.DeprecatedWarning)
+ SCons.Warnings.warningAsException(0)
+
def test_Scanners(self):
"""Test Scanner execution through different environments
diff --git a/src/engine/SCons/Scanner/C.py b/src/engine/SCons/Scanner/C.py
index c81417a..77b9525 100644
--- a/src/engine/SCons/Scanner/C.py
+++ b/src/engine/SCons/Scanner/C.py
@@ -38,6 +38,7 @@ import SCons.Node
import SCons.Node.FS
import SCons.Scanner
import SCons.Util
+import SCons.Warnings
include_re = re.compile('^[ \t]*#[ \t]*include[ \t]+(<|")([\\w./\\\\]+)(>|")', re.M)
@@ -114,6 +115,9 @@ def scan(node, env, target, fs = SCons.Node.FS.default_fs):
if not n is None:
nodes.append(n)
+ else:
+ SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
+ "No dependency generated for file: %s (included from: %s) -- file not found" % (include[1], node))
node.found_includes[cpppath] = nodes
# Schwartzian transform from the Python FAQ Wizard
diff --git a/src/engine/SCons/Scanner/CTests.py b/src/engine/SCons/Scanner/CTests.py
index f99af18..cc9e116 100644
--- a/src/engine/SCons/Scanner/CTests.py
+++ b/src/engine/SCons/Scanner/CTests.py
@@ -30,6 +30,7 @@ import sys
import os
import os.path
import SCons.Node.FS
+import SCons.Warnings
test = TestCmd.TestCmd(workdir = '')
@@ -230,11 +231,23 @@ class CScannerTestCase8(unittest.TestCase):
class CScannerTestCase9(unittest.TestCase):
def runTest(self):
+ SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning)
+ class TestOut:
+ def __call__(self, x):
+ self.out = x
+
+ to = TestOut()
+ to.out = None
+ SCons.Warnings._warningOut = to
test.write('fa.h','\n')
fs = SCons.Node.FS.FS(test.workpath(''))
s = SCons.Scanner.C.CScan(fs=fs)
env = DummyEnvironment([])
deps = s.scan(fs.File('fa.cpp'), env, DummyTarget())
+
+ # Did we catch the warning associated with not finding fb.h?
+ assert to.out
+
deps_match(self, deps, [ 'fa.h' ])
test.unlink('fa.h')
diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py
index c42a700..d95f8f0 100644
--- a/src/engine/SCons/Script/__init__.py
+++ b/src/engine/SCons/Script/__init__.py
@@ -63,6 +63,7 @@ import SCons.Sig.MD5
from SCons.Taskmaster import Taskmaster
import SCons.Builder
import SCons.Script.SConscript
+import SCons.Warnings
#
@@ -200,24 +201,20 @@ def _scons_syntax_error(e):
def find_deepest_user_frame(tb):
"""
Find the deepest stack frame that is not part of SCons.
+
+ Input is a "pre-processed" stack trace in the form
+ returned by traceback.extract_tb() or traceback.extract_stack()
"""
- stack = [tb]
- while tb.tb_next is not None:
- tb = tb.tb_next
- stack.append(tb)
-
- stack.reverse()
+ tb.reverse()
# find the deepest traceback frame that is not part
# of SCons:
- for frame in stack:
- filename = frame.tb_frame.f_code.co_filename
+ for frame in tb:
+ filename = frame[0]
if string.find(filename, os.sep+'SCons'+os.sep) == -1:
- tb = frame
- break
-
- return tb
+ return frame
+ return tb[0]
def _scons_user_error(e):
"""Handle user errors. Print out a message and a description of the
@@ -226,10 +223,7 @@ def _scons_user_error(e):
not part of SCons itself.
"""
etype, value, tb = sys.exc_info()
- tb = find_deepest_user_frame(tb)
- lineno = traceback.tb_lineno(tb)
- filename = tb.tb_frame.f_code.co_filename
- routine = tb.tb_frame.f_code.co_name
+ filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
sys.stderr.write("\nSCons error: %s\n" % value)
sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
sys.exit(2)
@@ -241,10 +235,15 @@ def _scons_user_warning(e):
not part of SCons itself.
"""
etype, value, tb = sys.exc_info()
- tb = find_deepest_user_frame(tb)
- lineno = traceback.tb_lineno(tb)
- filename = tb.tb_frame.f_code.co_filename
- routine = tb.tb_frame.f_code.co_name
+ filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
+ sys.stderr.write("\nSCons warning: %s\n" % e)
+ sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
+
+def _scons_internal_warning(e):
+ """Slightly different from _scons_user_warning in that we use the
+ *current call stack* rather than sys.exc_info() to get our stack trace.
+ This is used by the warnings framework to print warnings."""
+ filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack())
sys.stderr.write("\nSCons warning: %s\n" % e)
sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
@@ -685,9 +684,62 @@ def options_init():
short = 'W', long = ['what-if', 'new-file', 'assume-new'], arg = 'FILE',
help = "Consider FILE to be changed.")
- Option(func = opt_not_yet, future = 1,
- long = ['warn-undefined-variables'],
- help = "Warn when an undefined variable is referenced.")
+ def opt_warn(opt, arg):
+ """The --warn option. An argument to this option
+ should be of the form <warning-class> or no-<warning-class>.
+ The warning class is munged in order to get an actual class
+ name from the SCons.Warnings module to enable or disable.
+ The supplied <warning-class> is split on hyphens, each element
+ is captialized, then smushed back together. Then the string
+ "SCons.Warnings." is added to the front and "Warning" is added
+ to the back to get the fully qualified class name.
+
+ For example, --warn=deprecated will enable the
+ SCons.Warnings.DeprecatedWarning class.
+
+ --warn=no-dependency will disable the
+ SCons.Warnings.DependencyWarning class.
+
+ As a special case, --warn=all and --warn=no-all
+ will enable or disable (respectively) the base
+ class of all warnings, which is SCons.Warning.Warning."""
+
+ elems = string.split(string.lower(arg), '-')
+ enable = 1
+ if elems[0] == 'no':
+ enable = 0
+ del elems[0]
+
+ if len(elems) == 1 and elems[0] == 'all':
+ class_name = "Warning"
+ else:
+ class_name = string.join(map(string.capitalize, elems), '') + \
+ "Warning"
+ try:
+ clazz = getattr(SCons.Warnings, class_name)
+ except AttributeError:
+ sys.stderr.write("No warning type: '%s'\n" % arg)
+ else:
+ if enable:
+ SCons.Warnings.enableWarningClass(clazz)
+ else:
+ SCons.Warnings.suppressWarningClass(clazz)
+
+ Option(func = opt_warn,
+ long = [ 'warn', 'warning' ], arg='WARNING-SPEC',
+ help = "Enable or disable warnings.")
+
+
+ # We want to preserve the --warn-undefined-variables option for
+ # compatibility with GNU Make. Unfortunately, this conflicts with
+ # the --warn=type option that we're using for our own warning
+ # control. The getopt module reports "--warn not a unique prefix"
+ # when both are defined. We may be able to support both in the
+ # future with a more robust getopt solution.
+ #
+ #Option(func = opt_not_yet, future = 1,
+ # long = ['warn-undefined-variables'],
+ # help = "Warn when an undefined variable is referenced.")
Option(func = opt_not_yet, future = 1,
short = 'Y', long = ['repository'], arg = 'REPOSITORY',
@@ -749,6 +801,10 @@ def _main():
except:
getopt_err = getopt.error
+ # Enable deprecated warnings by default.
+ SCons.Warnings._warningOut = _scons_internal_warning
+ SCons.Warnings.enableWarningClass(SCons.Warnings.DeprecatedWarning)
+
try:
cmd_opts, t = getopt.getopt(string.split(os.environ['SCONSFLAGS']),
short_opts, long_opts)
diff --git a/src/engine/SCons/Warnings.py b/src/engine/SCons/Warnings.py
new file mode 100644
index 0000000..5ba3aec
--- /dev/null
+++ b/src/engine/SCons/Warnings.py
@@ -0,0 +1,81 @@
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""SCons.Warnings
+
+This file implements the warnings framework for SCons.
+
+"""
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import sys
+import copy
+import traceback
+
+import SCons.Errors
+
+class Warning(SCons.Errors.UserError):
+ pass
+
+class DeprecatedWarning(Warning):
+ pass
+
+class DependencyWarning(Warning):
+ pass
+
+_warningAsException = 0
+
+# The below is a list of 2-tuples. The first element is a class object.
+# The second element is true if that class is enabled, false if it is disabled.
+_enabled = []
+
+_warningOut = None
+
+def suppressWarningClass(clazz):
+ """Suppresses all warnings that are of type clazz or
+ derived from clazz."""
+ _enabled.insert(0, (clazz, 0))
+
+def enableWarningClass(clazz):
+ """Suppresses all warnings that are of type clazz or
+ derived from clazz."""
+ _enabled.insert(0, (clazz, 1))
+
+def warningAsException(flag=1):
+ global _warningAsException
+ _warningAsException = flag
+
+def warn(clazz, *args):
+ global _enabled, _warningAsException, _warningOut
+
+ warning = clazz(args)
+ for clazz, flag in _enabled:
+ if isinstance(warning, clazz):
+ if flag:
+ if _warningAsException:
+ raise warning
+
+ if _warningOut:
+ _warningOut(warning)
+ break
diff --git a/src/engine/SCons/WarningsTests.py b/src/engine/SCons/WarningsTests.py
new file mode 100644
index 0000000..efc9adc
--- /dev/null
+++ b/src/engine/SCons/WarningsTests.py
@@ -0,0 +1,115 @@
+#
+# Copyright (c) 2001, 2002 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import sys
+import unittest
+import SCons.Warnings
+
+class TestOutput:
+ def __call__(self, x):
+ self.out = str(x)
+
+class WarningsTestCase(unittest.TestCase):
+ def test_Warning(self):
+ """Test warn function."""
+
+ # Reset global state
+ SCons.Warnings._enabled = []
+ SCons.Warnings._warningAsException = 0
+
+ to = TestOutput()
+ SCons.Warnings._warningOut=to
+ SCons.Warnings.enableWarningClass(SCons.Warnings.Warning)
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
+ "Foo")
+ assert to.out == "Foo", to.out
+ SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
+ "Foo", 1)
+ assert to.out == "('Foo', 1)", to.out
+
+ def test_WarningAsExc(self):
+ """Test warnings as exceptions."""
+
+ # Reset global state
+ SCons.Warnings._enabled = []
+ SCons.Warnings._warningAsException = 0
+
+ SCons.Warnings.enableWarningClass(SCons.Warnings.Warning)
+ SCons.Warnings.warningAsException()
+ try:
+ SCons.Warnings.warn(SCons.Warnings.Warning, "Foo")
+ except:
+ pass
+ else:
+ assert 0
+
+ SCons.Warnings.warningAsException(0)
+ SCons.Warnings.warn(SCons.Warnings.Warning, "Foo")
+
+ def test_Disable(self):
+ """Test disabling/enabling warnings."""
+
+ # Reset global state
+ SCons.Warnings._enabled = []
+ SCons.Warnings._warningAsException = 0
+
+ to = TestOutput()
+ SCons.Warnings._warningOut=to
+ to.out = None
+
+ # No warnings by default
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
+ "Foo")
+ assert to.out is None, to.out
+
+ SCons.Warnings.enableWarningClass(SCons.Warnings.Warning)
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
+ "Foo")
+ assert to.out == "Foo", to.out
+
+ to.out = None
+ SCons.Warnings.suppressWarningClass(SCons.Warnings.DeprecatedWarning)
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
+ "Foo")
+ assert to.out is None, to.out
+
+ # Dependency warnings should still be enabled though
+ SCons.Warnings.enableWarningClass(SCons.Warnings.Warning)
+ SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
+ "Foo")
+ assert to.out == "Foo", to.out
+
+ # Try reenabling all warnings...
+ SCons.Warnings.enableWarningClass(SCons.Warnings.Warning)
+
+ SCons.Warnings.enableWarningClass(SCons.Warnings.Warning)
+ SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
+ "Foo")
+ assert to.out == "Foo", to.out
+
+if __name__ == "__main__":
+ suite = unittest.makeSuite(WarningsTestCase, 'test_')
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)