summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/man/scons.130
-rw-r--r--src/CHANGES.txt5
-rw-r--r--src/engine/SCons/Script/SConscript.py17
-rw-r--r--src/engine/SCons/Script/__init__.py25
-rw-r--r--src/engine/SCons/Util.py45
-rw-r--r--src/engine/SCons/UtilTests.py52
-rw-r--r--test/option-c.py40
7 files changed, 197 insertions, 17 deletions
diff --git a/doc/man/scons.1 b/doc/man/scons.1
index 5ef46a8..26215ad 100644
--- a/doc/man/scons.1
+++ b/doc/man/scons.1
@@ -168,6 +168,8 @@ scons -c build export
.EE
to remove target files under build and export.
+Additional files or directories to remove can be specified using the
+Clean() function.
A subset of a hierarchical tree may be built by
remaining at the top-level directory (where the
@@ -277,6 +279,8 @@ Ignored for compatibility with non-GNU versions of
-c, --clean, --remove
Clean up by removing all target files for which a construction
command is specified.
+Also remove any files or directories associated to the construction command
+using the Clean() function.
.\" .TP
.\" --cache-disable, --no-cache
@@ -2287,6 +2291,32 @@ is usually safe, and is always more efficient than
.IR duplicate =1.
.TP
+.RI Clean ( target, files_or_dirs )
+This specifies a list of files or directories which should be removed
+whenever the target is specified with the
+.B -c
+command line option.
+Multiple calls to
+.BR Clean ()
+are legal,
+and create a new target or add files and directories to the
+clean list for the specified target.
+
+Multiple files or directories should be specified
+either as separate arguments to the
+.BR Clean ()
+method, or as a list.
+.BR Clean ()
+will also accept the return value of any of the construction environment
+Builder methods.
+Examples:
+
+.ES
+Clean('foo', ['bar', 'baz'])
+Clean('dist', env.Program('hello', 'hello.c'))
+.EE
+
+.TP
.RI Default( targets )
This specifies a list of default targets,
which will be built by
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 3869feb..f719883 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -19,6 +19,11 @@ RELEASE 0.10 - XXX
- Remove Python bytecode (*.pyc) files from the scons-local packages.
+ From Steve Leblanc:
+
+ - Add a Clean() method to support removing user-specified targets
+ when using the -c option.
+
From Anthony Roach:
- Add SetJobs() and GetJobs() methods to allow configuration of the
diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py
index 15f116f..fcb08cf 100644
--- a/src/engine/SCons/Script/SConscript.py
+++ b/src/engine/SCons/Script/SConscript.py
@@ -51,6 +51,7 @@ def do_nothing(text): pass
HelpFunction = do_nothing
default_targets = []
+clean_targets = {}
arguments = {}
launch_dir = os.path.abspath(os.curdir)
@@ -315,6 +316,21 @@ def SetJobs(num):
except ValueError, x:
raise SCons.Errors.UserError, "A positive integer is required: %s"%repr(num)
+def Clean(target, files):
+ target = str(target)
+ if not SCons.Util.is_List(files):
+ files = [files]
+ nodes = []
+ for f in files:
+ if isinstance(f, SCons.Node.Node):
+ nodes.append(f)
+ else:
+ nodes.extend(SCons.Node.arg2nodes(f, SCons.Node.FS.default_fs.Entry))
+ if clean_targets.has_key(target):
+ clean_targets[target].extend(nodes)
+ else:
+ clean_targets[target] = nodes
+
def BuildDefaultGlobals():
"""
Create a dictionary containing all the default globals for
@@ -326,6 +342,7 @@ def BuildDefaultGlobals():
globals['ARGUMENTS'] = arguments
globals['BuildDir'] = BuildDir
globals['Builder'] = SCons.Builder.Builder
+ globals['Clean'] = Clean
globals['CScan'] = SCons.Defaults.CScan
globals['Default'] = Default
globals['Dir'] = SCons.Node.FS.default_fs.Dir
diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py
index 0068e8c..b4afb3c 100644
--- a/src/engine/SCons/Script/__init__.py
+++ b/src/engine/SCons/Script/__init__.py
@@ -65,6 +65,7 @@ import SCons.Builder
import SCons.Script.SConscript
import SCons.Warnings
from SCons.Optik import OptionParser, SUPPRESS_HELP, OptionValueError
+from SCons.Util import display
#
@@ -146,6 +147,10 @@ class CleanTask(SCons.Taskmaster.Task):
def show(self):
if self.targets[0].builder or self.targets[0].side_effect:
display("Removed " + str(self.targets[0]))
+ if SCons.Script.SConscript.clean_targets.has_key(str(self.targets[0])):
+ files = SCons.Script.SConscript.clean_targets[str(self.targets[0])]
+ for f in files:
+ SCons.Utils.fs_delete(str(f), 0)
def remove(self):
if self.targets[0].builder or self.targets[0].side_effect:
@@ -157,6 +162,10 @@ class CleanTask(SCons.Taskmaster.Task):
else:
if removed:
display("Removed " + str(t))
+ if SCons.Script.SConscript.clean_targets.has_key(str(self.targets[0])):
+ files = SCons.Script.SConscript.clean_targets[str(self.targets[0])]
+ for f in files:
+ SCons.Util.fs_delete(str(f))
execute = remove
@@ -192,11 +201,6 @@ repositories = []
sig_module = None
num_jobs = 1 # this is modifed by SConscript.SetJobs()
-def print_it(text):
- print text
-
-display = print_it
-
# Exceptions for this module
class PrintHelp(Exception):
pass
@@ -408,10 +412,8 @@ class OptParser(OptionParser):
"--touch", action="callback", callback=opt_ignore,
help="Ignored for compatibility.")
- def opt_c(option, opt, value, parser):
- setattr(parser.values, 'clean', 1)
- self.add_option('-c', '--clean', '--remove', action="callback",
- callback=opt_c,
+ self.add_option('-c', '--clean', '--remove', action="store_true",
+ default=0, dest="clean",
help="Remove specified targets and dependencies.")
self.add_option('-C', '--directory', type="string", action = "append",
@@ -662,10 +664,7 @@ def _main():
SCons.Node.FS.execute_actions = None
CleanTask.execute = CleanTask.show
if options.no_progress or options.silent:
- global display
- def dont_print_it(text):
- pass
- display = dont_print_it
+ display.set_mode(0)
if options.silent:
SCons.Action.print_actions = None
if options.directory:
diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py
index 17b2882..5a1c078 100644
--- a/src/engine/SCons/Util.py
+++ b/src/engine/SCons/Util.py
@@ -326,6 +326,22 @@ class CmdStringHolder:
def __cmp__(self, rhs):
return cmp(self.flatdata, str(rhs))
+class DisplayEngine:
+ def __init__(self):
+ self.__call__ = self.print_it
+
+ def print_it(self, text):
+ print text
+
+ def dont_print(self, text):
+ pass
+
+ def set_mode(self, mode):
+ if mode:
+ self.__call__ = self.print_it
+ else:
+ self.__call__ = self.dont_print
+
def scons_subst_list(strSubst, globals, locals, remove=None):
"""
@@ -722,3 +738,32 @@ def ParseConfig(env, command, function=None):
if type(command) is type([]):
command = string.join(command)
return function(env, os.popen(command).read())
+
+def dir_index(directory):
+ files = []
+ for file in os.listdir(directory):
+ fullname = os.path.join(directory, file)
+ files.append(fullname)
+ return files
+
+def fs_delete(path, remove=1):
+ try:
+ if os.path.exists(path):
+ if os.path.isfile(path):
+ if remove: os.unlink(path)
+ display("Removed " + path)
+ elif os.path.isdir(path) and not os.path.islink(path):
+ # delete everything in the dir
+ for p in dir_index(path):
+ if os.path.isfile(p):
+ if remove: os.unlink(p)
+ display("Removed " + p)
+ else:
+ fs_delete(p, remove)
+ # then delete dir itself
+ if remove: os.rmdir(path)
+ display("Removed directory " + path)
+ except OSError, e:
+ print "scons: Could not remove '%s':" % str(t), e.strerror
+
+display = DisplayEngine()
diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py
index 1f609ab..2da792f 100644
--- a/src/engine/SCons/UtilTests.py
+++ b/src/engine/SCons/UtilTests.py
@@ -35,6 +35,15 @@ import SCons.Node.FS
from SCons.Util import *
import TestCmd
+
+class OutBuffer:
+ def __init__(self):
+ self.buffer = ""
+
+ def write(self, str):
+ self.buffer = self.buffer + str
+
+
class UtilTestCase(unittest.TestCase):
def test_subst(self):
"""Test the subst function."""
@@ -496,7 +505,48 @@ class UtilTestCase(unittest.TestCase):
env=DummyEnv()
res=mapPaths('bleh', dir, env)
assert res[0] == os.path.normpath('foo/bar'), res[1]
-
+
+ def test_display(self):
+ old_stdout = sys.stdout
+ sys.stdout = OutBuffer()
+ SCons.Util.display("line1")
+ display.set_mode(0)
+ SCons.Util.display("line2")
+ display.set_mode(1)
+ SCons.Util.display("line3")
+
+ assert sys.stdout.buffer == "line1\nline3\n"
+ sys.stdout = old_stdout
+
+ def test_fs_delete(self):
+ test = TestCmd.TestCmd(workdir = '')
+ base = test.workpath('')
+ xxx = test.workpath('xxx.xxx')
+ sub1_yyy = test.workpath('sub1', 'yyy.yyy')
+ test.subdir('sub1')
+ test.write(xxx, "\n")
+ test.write(sub1_yyy, "\n")
+
+ old_stdout = sys.stdout
+ sys.stdout = OutBuffer()
+
+ exp = "Removed " + os.path.join(base, sub1_yyy) + '\n' + \
+ "Removed directory " + os.path.join(base, 'sub1') + '\n' + \
+ "Removed " + os.path.join(base, xxx) + '\n' + \
+ "Removed directory " + base + '\n'
+
+ SCons.Util.fs_delete(base, remove=0)
+ assert sys.stdout.buffer == exp
+ assert os.path.exists(sub1_yyy)
+
+ sys.stdout.buffer = ""
+ SCons.Util.fs_delete(base, remove=1)
+ assert sys.stdout.buffer == exp
+ assert not os.path.exists(base)
+
+ test._dirlist = None
+ sys.stdout = old_stdout
+
if __name__ == "__main__":
suite = unittest.makeSuite(UtilTestCase, 'test_')
diff --git a/test/option-c.py b/test/option-c.py
index 060ebfb..5d1a714 100644
--- a/test/option-c.py
+++ b/test/option-c.py
@@ -127,12 +127,46 @@ test.fail_test(test.read(test.workpath('foo3.out')) != "foo3.in\n")
test.writable('.', 0)
f = open(test.workpath('foo1.out'))
-
test.run(arguments = '-c foo1.out',
stdout = test.wrap_stdout("scons: Could not remove 'foo1.out': Permission denied\n"))
-
test.fail_test(not os.path.exists(test.workpath('foo1.out')))
-
f.close()
+test.writable('.', 1)
+
+test.subdir('subd')
+test.write(['subd', 'foon.in'], "foon.in\n")
+test.write('aux1.x', "aux1.x\n")
+test.write('aux2.x', "aux2.x\n")
+test.write('SConstruct', """
+B = Builder(action = r'%s build.py $TARGETS $SOURCES')
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = 'foo1.out', source = 'foo1.in')
+env.B(target = 'foo2.out', source = 'foo2.xxx')
+env.B(target = 'foo2.xxx', source = 'foo2.in')
+env.B(target = 'foo3.out', source = 'foo3.in')
+Clean('foo2.xxx', ['aux1.x'])
+Clean('foo2.xxx', ['aux2.x'])
+Clean('.', ['subd'])
+""" % python)
+
+expect = test.wrap_stdout("""Removed foo2.xxx
+Removed aux1.x
+Removed aux2.x
+""")
+test.run(arguments = '-c foo2.xxx', stdout=expect)
+test.fail_test(test.read(test.workpath('foo1.out')) != "foo1.in\n")
+test.fail_test(os.path.exists(test.workpath('foo2.xxx')))
+test.fail_test(test.read(test.workpath('foo2.out')) != "foo2.in\n")
+test.fail_test(test.read(test.workpath('foo3.out')) != "foo3.in\n")
+
+expect = test.wrap_stdout("""Removed foo1.out
+Removed foo2.out
+Removed foo3.out
+Removed %s
+Removed directory subd
+""" % os.path.join('subd','foon.in'))
+test.run(arguments = '-c .', stdout=expect)
+test.fail_test(os.path.exists(test.workpath('subdir', 'foon.in')))
+test.fail_test(os.path.exists(test.workpath('subdir')))
test.pass_test()