Finish the Action refactoring by removing Builder.execute() methods and moving except...
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 19 Dec 2002 15:31:04 +0000 (15:31 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 19 Dec 2002 15:31:04 +0000 (15:31 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@526 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/engine/SCons/ActionTests.py
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Node/FSTests.py
src/engine/SCons/Node/NodeTests.py
src/engine/SCons/Node/__init__.py
src/engine/SCons/Script/__init__.py
src/engine/SCons/Taskmaster.py
src/engine/SCons/TaskmasterTests.py

index dfa41ada7cb9bd2f3700e158a4a68de9ba606ebd..2f262bf944b84bff99f669ee82c086230280f5b6 100644 (file)
@@ -30,19 +30,73 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 def Func():
     pass
 
+import os
 import sys
 import types
 import unittest
+import UserDict
 
 import SCons.Action
-import TestCmd
+import SCons.Environment
 import SCons.Errors
 
-import UserDict
+import TestCmd
 
-import SCons.Environment
-def Environment(dict):
-    return apply(SCons.Environment.Environment, (), dict)
+# Initial setup of the common environment for all tests,
+# a temporary working directory containing a
+# script for writing arguments to an output file.
+#
+# We don't do this as a setUp() method because it's
+# unnecessary to create a separate directory and script
+# for each test, they can just use the one.
+test = TestCmd.TestCmd(workdir = '')
+
+test.write('act.py', """import os, string, sys
+f = open(sys.argv[1], 'w')
+f.write("act.py: '" + string.join(sys.argv[2:], "' '") + "'\\n")
+try:
+    if sys.argv[3]:
+        f.write("act.py: '" + os.environ[sys.argv[3]] + "'\\n")
+except:
+    pass
+f.close()
+sys.exit(0)
+""")
+
+act_py = test.workpath('act.py')
+
+outfile = test.workpath('outfile')
+outfile2 = test.workpath('outfile2')
+
+scons_env = SCons.Environment.Environment()
+
+class Environment:
+    def __init__(self, **kw):
+        self.d = {}
+        self.d['SHELL'] = scons_env['SHELL']
+        self.d['SPAWN'] = scons_env['SPAWN']
+        self.d['ESCAPE'] = scons_env['ESCAPE']
+        for k, v in kw.items():
+            self.d[k] = v
+    def subst(self, s):
+        if not SCons.Util.is_String(s):
+            return s
+        try:
+            if s[0] == '$':
+                return self.d.get(s[1:], '')
+        except IndexError:
+            pass
+        return self.d.get(s, s)
+    def __getitem__(self, item):
+        return self.d[item]
+    def has_key(self, item):
+        return self.d.has_key(item)
+    def get(self, key, value):
+        return self.d.get(key, value)
+    def items(self):
+        return self.d.items()
+
+python = sys.executable
 
 class ActionTestCase(unittest.TestCase):
 
@@ -53,6 +107,7 @@ class ActionTestCase(unittest.TestCase):
             pass
         a1 = SCons.Action.Action(foo)
         assert isinstance(a1, SCons.Action.FunctionAction), a1
+        assert a1.function == foo, a1.function
 
         a2 = SCons.Action.Action("string")
         assert isinstance(a2, SCons.Action.CommandAction), a2
@@ -96,6 +151,15 @@ class ActionTestCase(unittest.TestCase):
         assert isinstance(a9.list[2], SCons.Action.CommandAction), a9.list[2]
         assert a9.list[2].cmd_list == ["z"], a9.list[2].cmd_list
 
+        a10 = SCons.Action.Action(["x", foo, "z"])
+        assert isinstance(a10, SCons.Action.ListAction), a10
+        assert isinstance(a10.list[0], SCons.Action.CommandAction), a10.list[0]
+        assert a10.list[0].cmd_list == ["x"], a10.list[0].cmd_list
+        assert isinstance(a10.list[1], SCons.Action.FunctionAction), a10.list[1]
+        assert a10.list[1].function == foo, a10.list[1].function
+        assert isinstance(a10.list[2], SCons.Action.CommandAction), a10.list[2]
+        assert a10.list[2].cmd_list == ["z"], a10.list[2].cmd_list
+
 class ActionBaseTestCase(unittest.TestCase):
 
     def test_cmp(self):
@@ -120,17 +184,19 @@ class ActionBaseTestCase(unittest.TestCase):
         """
         a = SCons.Action.Action("x")
 
-        d = a.subst_dict([],[],Environment({'a' : 'A', 'b' : 'B'}))
+        d = a.subst_dict([], [], Environment(a = 'A', b = 'B'))
         assert d['a'] == 'A', d
         assert d['b'] == 'B', d
 
-        d = a.subst_dict(target = 't', source = 's',env=Environment({}))
+        d = a.subst_dict(target = 't', source = 's', env = Environment())
         assert str(d['TARGETS']) == 't', d['TARGETS']
         assert str(d['TARGET']) == 't', d['TARGET']
         assert str(d['SOURCES']) == 's', d['SOURCES']
         assert str(d['SOURCE']) == 's', d['SOURCE']
 
-        d = a.subst_dict(target = ['t1', 't2'], source = ['s1', 's2'], env=Environment({}))
+        d = a.subst_dict(target = ['t1', 't2'],
+                         source = ['s1', 's2'],
+                         env = Environment())
         TARGETS = map(lambda x: str(x), d['TARGETS'])
         TARGETS.sort()
         assert TARGETS == ['t1', 't2'], d['TARGETS']
@@ -148,7 +214,9 @@ class ActionBaseTestCase(unittest.TestCase):
             def rstr(self):
                 return 'rstr-' + self.name
 
-        d = a.subst_dict(target = [N('t3'), 't4'], source = ['s3', N('s4')], env=Environment({}))
+        d = a.subst_dict(target = [N('t3'), 't4'],
+                         source = ['s3', N('s4')],
+                         env = Environment())
         TARGETS = map(lambda x: str(x), d['TARGETS'])
         TARGETS.sort()
         assert TARGETS == ['t3', 't4'], d['TARGETS']
@@ -165,10 +233,119 @@ class CommandActionTestCase(unittest.TestCase):
         assert a.cmd_list == [ "xyzzy" ], a.cmd_list
 
     def test_execute(self):
-        """Test executing a command Action
+        """Test execution of command Actions
+
         """
-        self.test_set_handler()
-        pass
+        cmd1 = r'%s %s %s xyzzy' % (python, act_py, outfile)
+
+        act = SCons.Action.CommandAction(cmd1)
+        r = act.execute([], [], Environment())
+        assert r == 0
+        c = test.read(outfile, 'r')
+        assert c == "act.py: 'xyzzy'\n", c
+
+        cmd2 = r'%s %s %s $TARGET' % (python, act_py, outfile)
+
+        act = SCons.Action.CommandAction(cmd2)
+        r = act.execute('foo', [], Environment())
+        assert r == 0
+        c = test.read(outfile, 'r')
+        assert c == "act.py: 'foo'\n", c
+
+        cmd3 = r'%s %s %s ${TARGETS}' % (python, act_py, outfile)
+
+        act = SCons.Action.CommandAction(cmd3)
+        r = act.execute(['aaa', 'bbb'], [], Environment())
+        assert r == 0
+        c = test.read(outfile, 'r')
+        assert c == "act.py: 'aaa' 'bbb'\n", c
+
+        cmd4 = r'%s %s %s $SOURCES' % (python, act_py, outfile)
+
+        act = SCons.Action.CommandAction(cmd4)
+        r = act.execute([], ['one', 'two'], Environment())
+        assert r == 0
+        c = test.read(outfile, 'r')
+        assert c == "act.py: 'one' 'two'\n", c
+
+        cmd4 = r'%s %s %s ${SOURCES[:2]}' % (python, act_py, outfile)
+
+        act = SCons.Action.CommandAction(cmd4)
+        r = act.execute([],
+                        source = ['three', 'four', 'five'],
+                        env = Environment())
+        assert r == 0
+        c = test.read(outfile, 'r')
+        assert c == "act.py: 'three' 'four'\n", c
+
+        cmd5 = r'%s %s %s $TARGET XYZZY' % (python, act_py, outfile)
+
+        act = SCons.Action.CommandAction(cmd5)
+        r = act.execute(target = 'out5',
+                        source = [],
+                        env = Environment(ENV = {'XYZZY' : 'xyzzy'}))
+        assert r == 0
+        c = test.read(outfile, 'r')
+        assert c == "act.py: 'out5' 'XYZZY'\nact.py: 'xyzzy'\n", c
+
+        class Obj:
+            def __init__(self, str):
+                self._str = str
+            def __str__(self):
+                return self._str
+
+        cmd6 = r'%s %s %s ${TARGETS[1]} $TARGET ${SOURCES[:2]}' % (python, act_py, outfile)
+
+        act = SCons.Action.CommandAction(cmd6)
+        r = act.execute(target = [Obj('111'), Obj('222')],
+                        source = [Obj('333'), Obj('444'), Obj('555')],
+                        env = Environment())
+        assert r == 0
+        c = test.read(outfile, 'r')
+        assert c == "act.py: '222' '111' '333' '444'\n", c
+
+        cmd7 = '%s %s %s one\n\n%s %s %s two' % (python, act_py, outfile,
+                                                 python, act_py, outfile)
+        expect7 = '%s %s %s one\n%s %s %s two\n' % (python, act_py, outfile,
+                                                    python, act_py, outfile)
+
+        act = SCons.Action.CommandAction(cmd7)
+
+        global show_string
+        show_string = ""
+        def my_show(string):
+            global show_string
+            show_string = show_string + string + "\n"
+        act.show = my_show
+
+        r = act.execute([], [], Environment())
+        assert r == 0
+        assert show_string == expect7, show_string
+
+        if os.name == 'nt':
+            # NT treats execs of directories and non-executable files
+            # as "file not found" errors
+            expect_nonexistent = 1
+            expect_nonexecutable = 1
+        else:
+            expect_nonexistent = 127
+            expect_nonexecutable = 126
+
+        # Test that a nonexistent command returns 127
+        act = SCons.Action.CommandAction(python + "_XyZzY_")
+        r = act.execute([], [], Environment(out = outfile))
+        assert r == expect_nonexistent, "r == %d" % r
+
+        # Test that trying to execute a directory returns 126
+        dir, tail = os.path.split(python)
+        act = SCons.Action.CommandAction(dir)
+        r = act.execute([], [], Environment(out = outfile))
+        assert r == expect_nonexecutable, "r == %d" % r
+
+        # Test that trying to execute a non-executable file returns 126
+        act = SCons.Action.CommandAction(outfile)
+        r = act.execute([], [], Environment(out = outfile))
+        assert r == expect_nonexecutable, "r == %d" % r
 
     def test_set_handler(self):
         """Test setting the command handler...
@@ -200,16 +377,16 @@ class CommandActionTestCase(unittest.TestCase):
             assert 0, "should have gotten user error"
             
         a = SCons.Action.CommandAction(["xyzzy"])
-        a.execute([],[],Environment({'SPAWN':func}))
+        a.execute([], [], Environment(SPAWN = func))
         assert t.executed == [ 'xyzzy' ]
 
         a = SCons.Action.CommandAction(["xyzzy"])
-        a.execute([],[],Environment({'SPAWN':func, 'SHELL':'fake shell'}))
+        a.execute([], [], Environment(SPAWN = func, SHELL = 'fake shell'))
         assert t.executed == [ 'xyzzy' ]
         assert t.shell == 'fake shell'
 
         a = SCons.Action.CommandAction([ LiteralStr("xyzzy") ])
-        a.execute([],[],Environment({'SPAWN':func, 'ESCAPE':escape_func}))
+        a.execute([], [], Environment(SPAWN = func, ESCAPE = escape_func))
         assert t.executed == [ '**xyzzy**' ], t.executed
 
     def test_get_raw_contents(self):
@@ -218,7 +395,7 @@ class CommandActionTestCase(unittest.TestCase):
         a = SCons.Action.CommandAction(["|", "$(", "$foo", "|", "$bar",
                                         "$)", "|"])
         c = a.get_raw_contents(target=[], source=[],
-                               env=Environment({'foo':'FFF', 'bar':'BBB'}))
+                               env=Environment(foo = 'FFF', bar = 'BBB'))
         assert c == "| $( FFF | BBB $) |", c
 
     def test_get_contents(self):
@@ -227,7 +404,7 @@ class CommandActionTestCase(unittest.TestCase):
         a = SCons.Action.CommandAction(["|", "$(", "$foo", "|", "$bar",
                                         "$)", "|"])
         c = a.get_contents(target=[], source=[],
-                           env=Environment({'foo':'FFF', 'bar':'BBB'}))
+                           env=Environment(foo = 'FFF', bar = 'BBB'))
         assert c == "| |", c
 
 class CommandGeneratorActionTestCase(unittest.TestCase):
@@ -247,26 +424,13 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
         def f(target, source, env, for_signature, self=self):
             dummy = env['dummy']
             self.dummy = dummy
-            s = env.subst("$FOO $( bar $) baz")
-            assert s == 'foo baz\nbar ack bar baz', s
-            s = env.subst("$FOO $( bar $) baz", raw=1)
-            assert s == 'foo baz\nbar ack $( bar $) baz', s
-            s = env.subst_list("$FOO $( bar $) baz")
-            assert s == [[ 'foo', 'baz' ], [ 'bar', 'ack', 'bar', 'baz' ]], s
-            s = env.subst_list("$FOO $( bar $) baz", raw=1)
-            assert s == [[ 'foo', 'baz' ],
-                         [ 'bar', 'ack', '$(', 'bar', '$)', 'baz' ]], s
+            s = env.subst("$FOO")
+            assert s == 'foo baz\nbar ack', s
             return "$FOO"
         def func_action(target, source, env, self=self):
             dummy=env['dummy']
-            s = env.subst('$foo $( bar $)')
-            assert s == 'bar bar', s
-            s = env.subst('$foo $( bar $)', raw=1)
-            assert s == 'bar $( bar $)', s
-            s = env.subst_list([ '$foo', '$(', 'bar', '$)' ])
-            assert s == [[ 'bar', 'bar' ]], s
-            s = env.subst_list([ '$foo', '$(', 'bar', '$)' ], raw=1)
-            assert s == [[ 'bar', '$(', 'bar', '$)' ]], s
+            s = env.subst('$foo')
+            assert s == 'bar', s
             self.dummy=dummy
         def f2(target, source, env, for_signature, f=func_action):
             return f
@@ -278,17 +442,17 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
         self.dummy = 0
         self.cmd = []
         self.args = []
-        a.execute([],[],env=Environment({ 'FOO' : 'foo baz\nbar ack',
-                                          'dummy' : 1,
-                                          'SPAWN':ch}))
+        a.execute([], [], env=Environment(FOO = 'foo baz\nbar ack',
+                                          dummy = 1,
+                                          SPAWN = ch))
         assert self.dummy == 1, self.dummy
         assert self.cmd == ['foo', 'bar'], self.cmd
         assert self.args == [[ 'foo', 'baz' ], [ 'bar', 'ack' ]], self.args
 
-        b=SCons.Action.CommandGeneratorAction(f2)
+        b = SCons.Action.CommandGeneratorAction(f2)
         self.dummy = 0
-        b.execute(target=[], source=[], env=Environment({ 'foo' : 'bar',
-                                                          'dummy' : 2 }))
+        b.execute(target=[], source=[], env=Environment(foo =  'bar',
+                                                        dummy =  2 ))
         assert self.dummy==2, self.dummy
         del self.dummy
 
@@ -299,8 +463,8 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
                 self.t.rfile_called = 1
         def f3(target, source, env, for_signature):
             return ''
-        c=SCons.Action.CommandGeneratorAction(f3)
-        c.execute(target=[], source=DummyFile(self), env=Environment({}))
+        c = SCons.Action.CommandGeneratorAction(f3)
+        c.execute(target=[], source=DummyFile(self), env=Environment())
         assert self.rfile_called
 
     def test_get_contents(self):
@@ -314,7 +478,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
 
         a = SCons.Action.CommandGeneratorAction(f)
         c = a.get_contents(target=[], source=[],
-                           env=Environment({'foo':'FFF', 'bar' : 'BBB'}))
+                           env=Environment(foo = 'FFF', bar =  'BBB'))
         assert c == "guux FFF BBB", c
 
 
@@ -337,21 +501,65 @@ class FunctionActionTestCase(unittest.TestCase):
             s.inc = s.inc + 1
             s.target = target
             s.source=source
-            assert env.subst("foo$BAR") == 'foofoo bar', env.subst("foo$BAR")
-            assert env.subst_list("foo$BAR") == [ [ 'foofoo', 'bar' ] ], \
-                   env.subst_list("foo$BAR")
+            assert env.subst("$BAR") == 'foo bar', env.subst("$BAR")
             return 0
         a = SCons.Action.FunctionAction(f)
-        a.execute(target=1, source=2, env=Environment({'BAR':'foo bar','s':self}))
+        a.execute(target=1, source=2, env=Environment(BAR = 'foo bar',
+                                                      s = self))
         assert self.inc == 1, self.inc
         assert self.source == [2], self.source
         assert self.target == [1], self.target
 
+        global count
+        count = 0
+        def function1(target, source, env):
+            global count
+            count = count + 1
+            for t in target:
+                open(t, 'w').write("function1\n")
+            return 1
+
+        act = SCons.Action.FunctionAction(function1)
+        r = None
+        try:
+            r = act.execute(target = [outfile, outfile2],
+                            source=[],
+                            env=Environment())
+        except SCons.Errors.BuildError:
+            pass
+        assert r == 1
+        assert count == 1
+        c = test.read(outfile, 'r')
+        assert c == "function1\n", c
+        c = test.read(outfile2, 'r')
+        assert c == "function1\n", c
+
+        class class1a:
+            def __init__(self, target, source, env):
+                open(env['out'], 'w').write("class1a\n")
+
+        act = SCons.Action.FunctionAction(class1a)
+        r = act.execute([], [], Environment(out = outfile))
+        assert r.__class__ == class1a
+        c = test.read(outfile, 'r')
+        assert c == "class1a\n", c
+
+        class class1b:
+            def __call__(self, target, source, env):
+                open(env['out'], 'w').write("class1b\n")
+                return 2
+
+        act = SCons.Action.FunctionAction(class1b())
+        r = act.execute([], [], Environment(out = outfile))
+        assert r == 2
+        c = test.read(outfile, 'r')
+        assert c == "class1b\n", c
+
     def test_get_contents(self):
         """Test fetching the contents of a function Action
         """
         a = SCons.Action.FunctionAction(Func)
-        c = a.get_contents(target=[], source=[], env=Environment({}))
+        c = a.get_contents(target=[], source=[], env=Environment())
         assert c == "\177\036\000\177\037\000d\000\000S", repr(c)
 
 class ListActionTestCase(unittest.TestCase):
@@ -388,9 +596,29 @@ class ListActionTestCase(unittest.TestCase):
             s = env['s']
             s.inc = s.inc + 1
         a = SCons.Action.ListAction([f, f, f])
-        a.execute([],[],Environment({'s':self}))
+        a.execute([], [], Environment(s = self))
         assert self.inc == 3, self.inc
 
+        cmd2 = r'%s %s %s syzygy' % (python, act_py, outfile)
+
+        def function2(target, source, env):
+            open(env['out'], 'a').write("function2\n")
+            return 0
+
+        class class2a:
+            def __call__(self, target, source, env):
+                open(env['out'], 'a').write("class2a\n")
+                return 0
+
+        class class2b:
+            def __init__(self, target, source, env):
+                open(env['out'], 'a').write("class2b\n")
+        act = SCons.Action.ListAction([cmd2, function2, class2a(), class2b])
+        r = act.execute([], [], Environment(out = outfile))
+        assert r.__class__ == class2b
+        c = test.read(outfile, 'r')
+        assert c == "act.py: 'syzygy'\nfunction2\nclass2a\nclass2b\n", c
+
     def test_get_contents(self):
         """Test fetching the contents of a list of subsidiary Actions
         """
@@ -402,7 +630,7 @@ class ListActionTestCase(unittest.TestCase):
         a = SCons.Action.ListAction(["x",
                                      SCons.Action.CommandGenerator(gen),
                                      "z"])
-        c = a.get_contents(target=[], source=[], env=Environment({'s':self}))
+        c = a.get_contents(target=[], source=[], env=Environment(s = self))
         assert self.foo==1, self.foo
         assert c == "xyz", c
 
@@ -429,7 +657,7 @@ class LazyActionTestCase(unittest.TestCase):
             s.test=1
             return 0
         a = SCons.Action.Action('$BAR')
-        a.execute([],[], env=Environment({'BAR':f,'s':self}))
+        a.execute([], [], env=Environment(BAR = f, s = self))
         assert self.test == 1, self.test
 
     def test_get_contents(self):
@@ -437,7 +665,7 @@ class LazyActionTestCase(unittest.TestCase):
         """
         a = SCons.Action.Action("${FOO}")
         c = a.get_contents(target=[], source=[],
-                           env=Environment({'FOO':[["This", "is", "$(", "a", "$)", "test"]]}))
+                           env = Environment(FOO = [["This", "is", "$(", "a", "$)", "test"]]))
         assert c == "This is test", c
 
 
index 08d8f0cf05b5f7ced3638b3d16fc8808dd236afa..2f5df7f19bd7f69a6e6f05d8be33c202b0f080cd 100644 (file)
@@ -320,11 +320,6 @@ class BuilderBase:
     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.
         """
@@ -381,20 +376,6 @@ class ListBuilder(SCons.Util.Proxy):
         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.
         """
index 3c81d38474accd4c86ec11842182c0067787581a..a2369b9f70727604a216ae6adf0f84b80ac55e00 100644 (file)
@@ -51,25 +51,11 @@ import SCons.Environment
 # for each test, they can just use the one.
 test = TestCmd.TestCmd(workdir = '')
 
-test.write('act.py', """import os, string, sys
-f = open(sys.argv[1], 'w')
-f.write("act.py: '" + string.join(sys.argv[2:], "' '") + "'\\n")
-try:
-    if sys.argv[3]:
-        f.write("act.py: '" + os.environ[sys.argv[3]] + "'\\n")
-except:
-    pass
-f.close()
-sys.exit(0)
-""")
-
-act_py = test.workpath('act.py')
 outfile = test.workpath('outfile')
 outfile2 = test.workpath('outfile2')
 
 show_string = None
 env_scanner = None
-count = 0
 
 scons_env = SCons.Environment.Environment()
 
@@ -199,6 +185,11 @@ class BuilderTestCase(unittest.TestCase):
         builder = SCons.Builder.Builder(name="builder", action="foo")
         assert builder.action.cmd_list == ["foo"]
 
+        def func():
+            pass
+        builder = SCons.Builder.Builder(name="builder", action=func)
+        assert builder.action.function == func
+
     def test_generator(self):
         """Test Builder creation given a generator function."""
 
@@ -218,204 +209,19 @@ class BuilderTestCase(unittest.TestCase):
         assert b1 != b3
         assert b2 != b3
 
-    def test_execute(self):
-        """Test execution of simple Builder objects
-        
-        One Builder is a string that executes an external command,
-        one is an internal Python function, one is a list
-        containing one of each.
-        """
-
-        def MyBuilder(**kw):
-            builder = apply(SCons.Builder.Builder, (), kw)
-            def no_show(str):
-                pass
-            builder.action.show = no_show
-            return builder
-
-        python = sys.executable
-
-        cmd1 = r'%s %s %s xyzzy' % (python, act_py, outfile)
-
-        builder = MyBuilder(action = cmd1, name = "cmd1")
-        r = builder.execute([],[],Environment())
-        assert r == 0
-        c = test.read(outfile, 'r')
-        assert c == "act.py: 'xyzzy'\n", c
-
-        cmd2 = r'%s %s %s $TARGET' % (python, act_py, outfile)
-
-        builder = MyBuilder(action = cmd2, name = "cmd2")
-        r = builder.execute('foo', [], Environment())
-        assert r == 0
-        c = test.read(outfile, 'r')
-        assert c == "act.py: 'foo'\n", c
-
-        cmd3 = r'%s %s %s ${TARGETS}' % (python, act_py, outfile)
-
-        builder = MyBuilder(action = cmd3, name = "cmd3")
-        r = builder.execute(['aaa', 'bbb'], [], Environment())
-        assert r == 0
-        c = test.read(outfile, 'r')
-        assert c == "act.py: 'aaa' 'bbb'\n", c
-
-        cmd4 = r'%s %s %s $SOURCES' % (python, act_py, outfile)
-
-        builder = MyBuilder(action = cmd4, name = "cmd4")
-        r = builder.execute([], ['one', 'two'], Environment())
-        assert r == 0
-        c = test.read(outfile, 'r')
-        assert c == "act.py: 'one' 'two'\n", c
-
-        cmd4 = r'%s %s %s ${SOURCES[:2]}' % (python, act_py, outfile)
-
-        builder = MyBuilder(action = cmd4, name = "cmd4")
-        r = builder.execute([], source = ['three', 'four', 'five'], env=Environment())
-        assert r == 0
-        c = test.read(outfile, 'r')
-        assert c == "act.py: 'three' 'four'\n", c
-
-        cmd5 = r'%s %s %s $TARGET XYZZY' % (python, act_py, outfile)
-
-        builder = MyBuilder(action = cmd5, name = "cmd5")
-        r = builder.execute(target = 'out5', source = [], env = Environment(ENV={'XYZZY' : 'xyzzy'}))
-        assert r == 0
-        c = test.read(outfile, 'r')
-        assert c == "act.py: 'out5' 'XYZZY'\nact.py: 'xyzzy'\n", c
-
-        class Obj:
-            def __init__(self, str):
-                self._str = str
-            def __str__(self):
-                return self._str
-
-        cmd6 = r'%s %s %s ${TARGETS[1]} $TARGET ${SOURCES[:2]}' % (python, act_py, outfile)
-
-        builder = MyBuilder(action = cmd6, name = "cmd6")
-        r = builder.execute(target = [Obj('111'), Obj('222')],
-                            source = [Obj('333'), Obj('444'), Obj('555')],
-                            env = Environment())
-        assert r == 0
-        c = test.read(outfile, 'r')
-        assert c == "act.py: '222' '111' '333' '444'\n", c
-
-        cmd7 = '%s %s %s one\n\n%s %s %s two' % (python, act_py, outfile,
-                                                 python, act_py, outfile)
-        expect7 = '%s %s %s one\n%s %s %s two\n' % (python, act_py, outfile,
-                                                    python, act_py, outfile)
-
-        builder = MyBuilder(action = cmd7, name = "cmd7")
-
-        global show_string
-        show_string = ""
-        def my_show(string):
-            global show_string
-            show_string = show_string + string + "\n"
-        for action in builder.action.list:
-            action.show = my_show
-
-        r = builder.execute([],[],Environment())
-        assert r == 0
-        assert show_string == expect7, show_string
-
-        global count
-        count = 0
-        def function1(target, source, env):
-            global count
-            count = count + 1
-            for t in target:
-                open(t, 'w').write("function1\n")
-            return 1
-
-        builder = MyBuilder(action = function1, name = "function1")
-        try:
-            r = builder.execute(target = [outfile, outfile2], source=[], env=Environment())
-        except SCons.Errors.BuildError:
-            pass
-        assert r == 1
-        assert count == 1
-        c = test.read(outfile, 'r')
-        assert c == "function1\n", c
-        c = test.read(outfile2, 'r')
-        assert c == "function1\n", c
-
-        class class1a:
-            def __init__(self, target, source, env):
-                open(env['out'], 'w').write("class1a\n")
-
-        builder = MyBuilder(action = class1a, name = "class1a")
-        r = builder.execute([],[],Environment(out = outfile))
-        assert r.__class__ == class1a
-        c = test.read(outfile, 'r')
-        assert c == "class1a\n", c
-
-        class class1b:
-            def __call__(self, target, source, env):
-                open(env['out'], 'w').write("class1b\n")
-                return 2
-
-        builder = MyBuilder(action = class1b(), name = "class1b")
-        r = builder.execute([],[],Environment(out = outfile))
-        assert r == 2
-        c = test.read(outfile, 'r')
-        assert c == "class1b\n", c
-
-        cmd2 = r'%s %s %s syzygy' % (python, act_py, outfile)
-
-        def function2(target, source, env):
-            open(env['out'], 'a').write("function2\n")
-            return 0
-
-        class class2a:
-            def __call__(self, target, source, env):
-                open(env['out'], 'a').write("class2a\n")
-                return 0
-
-        class class2b:
-            def __init__(self, target, source, env):
-                open(env['out'], 'a').write("class2b\n")
-
-        builder = MyBuilder(action = SCons.Action.ListAction([cmd2, function2, class2a(), class2b]), name = "clist")
-        r = builder.execute([],[],Environment(out = outfile))
-        assert r.__class__ == class2b
-        c = test.read(outfile, 'r')
-        assert c == "act.py: 'syzygy'\nfunction2\nclass2a\nclass2b\n", c
-
-        if os.name == 'nt':
-            # NT treats execs of directories and non-executable files
-            # as "file not found" errors
-            expect_nonexistent = 1
-            expect_nonexecutable = 1
-        else:
-            expect_nonexistent = 127
-            expect_nonexecutable = 126
-
-        # Test that a nonexistent command returns 127
-        builder = MyBuilder(action = python + "_XyZzY_", name="badcmd")
-        r = builder.execute([],[],Environment(out = outfile))
-        assert r == expect_nonexistent, "r == %d" % r
-
-        # Test that trying to execute a directory returns 126
-        dir, tail = os.path.split(python)
-        builder = MyBuilder(action = dir, name = "dir")
-        r = builder.execute([],[],Environment(out = outfile))
-        assert r == expect_nonexecutable, "r == %d" % r
-
-        # Test that trying to execute a non-executable file returns 126
-        builder = MyBuilder(action = outfile, name = "badfile")
-        r = builder.execute([],[],Environment(out = outfile))
-        assert r == expect_nonexecutable, "r == %d" % r
-
     def test_get_actions(self):
         """Test fetching the Builder's Action list
-
-        Verify that we call the underlying Action's method
         """
-        builder = SCons.Builder.Builder(name="builder", action=SCons.Action.ListAction(["x", "y", "z"]))
+        def func():
+            pass
+        builder = SCons.Builder.Builder(name="builder",
+                                        action=SCons.Action.ListAction(["x",
+                                                                        func,
+                                                                        "z"]))
         a = builder.get_actions()
         assert len(a) == 3, a
         assert isinstance(a[0], SCons.Action.CommandAction), a[0]
-        assert isinstance(a[1], SCons.Action.CommandAction), a[1]
+        assert isinstance(a[1], SCons.Action.FunctionAction), a[1]
         assert isinstance(a[2], SCons.Action.CommandAction), a[2]
 
     def test_get_contents(self):
@@ -542,11 +348,7 @@ class BuilderTestCase(unittest.TestCase):
 
     def test_ListBuilder(self):
         """Testing ListBuilder class."""
-        global count
-        count = 0
         def function2(target, source, env, tlist = [outfile, outfile2], **kw):
-            global count
-            count = count + 1
             for t in target:
                 open(str(t), 'w').write("function2\n")
             for t in tlist:
@@ -556,25 +358,21 @@ class BuilderTestCase(unittest.TestCase):
 
         builder = SCons.Builder.Builder(action = function2, name = "function2")
         tgts = builder(env, target = [outfile, outfile2], source = 'foo')
+        for t in tgts:
+            t.prepare()
         try:
-            r = tgts[0].builder.execute(tgts, 'foo', env)
+            tgts[0].build()
         except SCons.Errors.BuildError:
             pass
         c = test.read(outfile, 'r')
         assert c == "function2\n", c
         c = test.read(outfile2, 'r')
         assert c == "function2\n", c
-        r = tgts[1].builder.execute(tgts[1], 'foo', env)
-        assert r == 1, r
-        assert count == 1, count
 
         sub1_out = test.workpath('sub1', 'out')
         sub2_out = test.workpath('sub2', 'out')
 
-        count = 0
         def function3(target, source, env, tlist = [sub1_out, sub2_out]):
-            global count
-            count = count + 1
             for t in target:
                 open(str(t), 'w').write("function3\n")
             for t in tlist:
@@ -584,11 +382,12 @@ class BuilderTestCase(unittest.TestCase):
 
         builder = SCons.Builder.Builder(action = function3, name = "function3")
         tgts = builder(env, target = [sub1_out, sub2_out], source = 'foo')
+        for t in tgts:
+            t.prepare()
         try:
-            r = tgts[0].builder.execute(tgts, 'foo', env)
-        except:
+            tgts[0].build()
+        except SCons.Errors.BuildError:
             pass
-        assert r == 1, r
         c = test.read(sub1_out, 'r')
         assert c == "function3\n", c
         c = test.read(sub2_out, 'r')
@@ -705,7 +504,6 @@ class BuilderTestCase(unittest.TestCase):
             flag = 1
         assert flag, "UserError should be thrown when we build targets with files of different suffixes."
 
-
     def test_build_scanner(self):
         """Testing ability to set a target scanner through a builder."""
         global instanced
@@ -743,7 +541,7 @@ class BuilderTestCase(unittest.TestCase):
         assert src.source_scanner == env_scanner
 
     def test_Builder_Args(self):
-        """Testing passing extra agrs to a builder."""
+        """Testing passing extra args to a builder."""
         def buildFunc(target, source, env, s=self):
             s.foo=env['foo']
             s.bar=env['bar']
index 5917044061f066baed3624c78dbfc3667810ca69..b5c96f9472744d29033d3fe725217e49a55c77e3 100644 (file)
@@ -40,10 +40,16 @@ class Builder:
     def __init__(self, factory):
         self.factory = factory
 
-    def execute(self, target, source, env):
-        global built_it
-        built_it = 1
-        return 0
+    def get_actions(self):
+        class Action:
+            def execute(self, targets, sources, env):
+                global built_it
+                built_it = 1
+                return 0
+        return [Action()]
+
+    def targets(self, t):
+        return [t]
     
     def source_factory(self, name):
         return self.factory(name)
@@ -554,6 +560,8 @@ class FSTestCase(unittest.TestCase):
         fs.chdir(fs.Dir('..'))
 
         # Test scanning
+        f1.builder_set(Builder(fs.File))
+        f1.env_set(Environment())
         f1.target_scanner = Scanner()
         f1.scan()
         assert f1.implicit[0].path_ == os.path.join("d1", "f1")
index 4054bb9fe69a80a939a35eb5b3fd2816ca07bda2..abfbe1c529c373fe1082d79ad49dc0f03f2c5139 100644 (file)
@@ -38,7 +38,7 @@ built_target =  None
 built_source =  None
 cycle_detected = None
 
-class Builder:
+class MyAction:
     def execute(self, target, source, env):
         global built_it, built_target, built_source, built_args
         built_it = 1
@@ -46,8 +46,12 @@ class Builder:
         built_source = source
         built_args = env
         return 0
+
+class Builder:
+    def targets(self, t):
+        return [t]
     def get_actions(self):
-        return 'xyzzy'
+        return [MyAction()]
     def get_contents(self, target, source, env):
         return 7
 
@@ -89,58 +93,14 @@ class Environment:
 
 class NodeTestCase(unittest.TestCase):
 
-    def test_BuildException(self):
-        """Test throwing an exception on build failure.
-        """
-        node = SCons.Node.Node()
-        node.builder_set(FailBuilder())
-        node.env_set(Environment())
-        try:
-            node.build()
-        except SCons.Errors.BuildError:
-            pass
-        else:
-            raise TestFailed, "did not catch expected BuildError"
-
-        node = SCons.Node.Node()
-        node.builder_set(ExceptBuilder())
-        node.env_set(Environment())
-        try:
-            node.build()
-        except SCons.Errors.BuildError:
-            pass
-        else:
-            raise TestFailed, "did not catch expected BuildError"
-
-        node = SCons.Node.Node()
-        node.builder_set(ExceptBuilder2())
-        node.env_set(Environment())
-        try:
-            node.build()
-        except SCons.Errors.BuildError, e:
-            # On a generic (non-BuildError) exception from a Builder,
-            # the Node should throw a BuildError exception with
-            # the args set to the exception value, type, and traceback.
-            assert len(e.args) == 3, `e.args`
-            assert e.args[0] == 'foo', e.args[0]
-            assert e.args[1] is None
-            assert type(e.args[2]) is type(sys.exc_traceback), e.args[2]
-        else:
-            raise TestFailed, "did not catch expected BuildError"
-
     def test_build(self):
         """Test building a node
         """
         global built_it
 
         class MyNode(SCons.Node.Node):
-            def __init__(self, **kw):
-                apply(SCons.Node.Node.__init__, (self,), kw)
-                self.prepare_count = 0
             def __str__(self):
                 return self.path
-            def prepare(self):
-                self.prepare_count = self.prepare_count+ 1
         # Make sure it doesn't blow up if no builder is set.
         node = MyNode()
         node.build()
@@ -153,8 +113,8 @@ class NodeTestCase(unittest.TestCase):
         node.sources = ["yyy", "zzz"]
         node.build()
         assert built_it
-        assert type(built_target) == type(MyNode()), type(built_target)
-        assert str(built_target) == "xxx", str(built_target)
+        assert type(built_target[0]) == type(MyNode()), type(built_target[0])
+        assert str(built_target[0]) == "xxx", str(built_target[0])
         assert built_source == ["yyy", "zzz"], built_source
 
         built_it = None
@@ -166,8 +126,8 @@ class NodeTestCase(unittest.TestCase):
         node.overrides = { "foo" : 1, "bar" : 2 }
         node.build()
         assert built_it
-        assert type(built_target) == type(MyNode()), type(built_target)
-        assert str(built_target) == "qqq", str(built_target)
+        assert type(built_target[0]) == type(MyNode()), type(built_target[0])
+        assert str(built_target[0]) == "qqq", str(built_target[0])
         assert built_source == ["rrr", "sss"], built_source
         assert built_args["foo"] == 1, built_args
         assert built_args["bar"] == 2, built_args
@@ -185,28 +145,6 @@ class NodeTestCase(unittest.TestCase):
         fff.sources = ["hhh", "iii"]
         ggg.sources = ["hhh", "iii"]
 
-        built_it = None
-        fff.build()
-        assert built_it
-        ggg.build()
-        assert ggg.prepare_count== 1, ggg.prepare_count
-        assert type(built_target) == type(MyNode()), type(built_target)
-        assert str(built_target) == "fff", str(built_target)
-        assert built_source == ["hhh", "iii"], built_source
-
-        delattr(lb, 'status')
-        fff.prepare_count = 0
-        ggg.prepare_count = 0
-
-        built_it = None
-        ggg.build()
-        #assert built_it
-        fff.build()
-        assert fff.prepare_count== 1, fff.prepare_count
-        assert type(built_target) == type(MyNode()), type(built_target)
-        assert str(built_target) == "fff", str(built_target)
-        assert built_source == ["hhh", "iii"], built_source
-
     def test_depends_on(self):
         parent = SCons.Node.Node()
         child = SCons.Node.Node()
@@ -249,8 +187,8 @@ class NodeTestCase(unittest.TestCase):
         """
         node = SCons.Node.Node()
         node.builder_set(Builder())
-        a = node.get_actions()
-        assert a == 'xyzzy', a
+        a = node.builder.get_actions()
+        assert isinstance(a[0], MyAction), a[0]
 
     def test_set_bsig(self):
         """Test setting a Node's signature
index 82dfd7e4e32d05e41d6d204f72295bb82ea41ecf..ca6e8eb8004b76e89aeb61e8b4ef2229ebbd34a5 100644 (file)
@@ -46,13 +46,10 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 
 
-import string
-import types
 import copy
-import sys
-import SCons.Sig
+import types
 
-from SCons.Errors import BuildError, UserError
+import SCons.Sig
 import SCons.Util
 
 # Node states
@@ -110,38 +107,25 @@ class Node:
     def generate_build_env(self):
         return self.env.Override(self.overrides)
 
-    def get_actions(self):
-        """Fetch the action list to build."""
-        return self.builder.get_actions()
-
     def build(self):
-        """Actually build the node.   Return the status from the build."""
-        # This method is called from multiple threads in a parallel build,
-        # so only do thread safe stuff here. Do thread unsafe stuff in built().
+        """Actually build the node.
+
+        This method is called from multiple threads in a parallel build,
+        so only do thread safe stuff here. Do thread unsafe stuff in
+        built().
+        """
         if not self.builder:
             return None
-        try:
-            # If this Builder instance has already been called,
-            # there will already be an associated status.
-            stat = self.builder.status
-        except AttributeError:
-            try:
-                stat = self.builder.execute(self, self.sources, self.generate_build_env())
-            except KeyboardInterrupt:
-                raise
-            except UserError:
-                raise
-            except BuildError:
-                raise
-            except:
-                raise BuildError(self, "Exception",
-                                 sys.exc_type,
-                                 sys.exc_value,
-                                 sys.exc_traceback)
-        if stat:
-            raise BuildError(node = self, errstr = "Error %d" % stat)
-
-        return stat
+        action_list = self.builder.get_actions()
+        if not action_list:
+            return
+        targets = self.builder.targets(self)
+        env = self.generate_build_env()
+        for action in action_list:
+            stat = action.execute(targets, self.sources, env)
+            if stat:
+                raise SCons.Errors.BuildError(node = self,
+                                              errstr = "Error %d" % stat)
 
     def built(self):
         """Called just after this node is sucessfully built."""
index add52e4871005a1565da22ba6044499744c22883..0068e8c898c50c96314b2bd3250af833ffe890b8 100644 (file)
@@ -78,29 +78,9 @@ class BuildTask(SCons.Taskmaster.Task):
             if self.top and target.builder:
                 display('scons: "%s" is up to date.' % str(target))
         elif target.builder and not hasattr(target.builder, 'status'):
-            action_list = target.get_actions()
-            if not action_list:
-                return
-            env = target.generate_build_env()
             if print_time:
                 start_time = time.time()
-            try:
-                for action in action_list:
-                    stat = action.execute(self.targets, target.sources, env)
-                    if stat:
-                        raise BuildError(node = target,
-                                         errstr = "Error %d" % stat)
-            except KeyboardInterrupt:
-                raise
-            except UserError:
-                raise
-            except BuildError:
-                raise
-            except:
-                raise BuildError(target, "Exception",
-                                 sys.exc_type,
-                                 sys.exc_value,
-                                 sys.exc_traceback)
+            SCons.Taskmaster.Task.execute(self)
             if print_time:
                 finish_time = time.time()
                 global command_time
@@ -131,7 +111,7 @@ class BuildTask(SCons.Taskmaster.Task):
                 SCons.Taskmaster.Task.executed(self)
         else:
             SCons.Taskmaster.Task.executed(self)
-                
+
         # print the tree here instead of in execute() because
         # this method is serialized, but execute isn't:
         if print_tree and self.top:
@@ -142,14 +122,11 @@ class BuildTask(SCons.Taskmaster.Task):
             print SCons.Util.render_tree(self.targets[0], get_derived_children)
 
     def failed(self):
-        global exit_status
-
         e = sys.exc_value
         if sys.exc_type == BuildError:
             sys.stderr.write("scons: *** [%s] %s\n" % (e.node, e.errstr))
             if e.errstr == 'Exception':
-                traceback.print_exception(e.args[0], e.args[1],
-                                          e.args[2])
+                traceback.print_exception(e.args[0], e.args[1], e.args[2])
         elif sys.exc_type == UserError:
             # We aren't being called out of a user frame, so
             # don't try to walk the stack, just print the error.
index 043ce7da5d294276bfcf8acb376ec7d25625d9d7..eec50bf3afa9f651fa6f9d2ef73fd9d6c05c7a9a 100644 (file)
@@ -29,13 +29,12 @@ Generic Taskmaster.
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
-
-
+import copy
+import string
+import sys
 
 import SCons.Node
-import string
 import SCons.Errors
-import copy
 
 class Task:
     """Default SCons build engine task.
@@ -71,12 +70,24 @@ class Task:
 
     def execute(self):
         """Called to execute the task.
-        
-        This methods is called from multiple threads in
-        a parallel build, so only do thread safe stuff here.
-        Do thread unsafe stuff in prepare(), executed() or failed()."""
-        if self.targets[0].get_state() != SCons.Node.up_to_date:
+
+        This method is called from multiple threads in a parallel build,
+        so only do thread safe stuff here.  Do thread unsafe stuff in
+        prepare(), executed() or failed()."""
+        try:
             self.targets[0].build()
+        except KeyboardInterrupt:
+            raise
+        except SCons.Errors.UserError:
+            raise
+        except SCons.Errors.BuildError:
+            raise
+        except:
+            raise SCons.Errors.BuildError(self.targets[0],
+                                          "Exception",
+                                          sys.exc_type,
+                                          sys.exc_value,
+                                          sys.exc_traceback)
 
     def get_target(self):
         """Fetch the target being built or updated by this task.
index c89f3560c69e773725589d034c4962425666108b..d8bcc4fd4095f022e9099b4eaec96a72ce95b708 100644 (file)
@@ -115,6 +115,10 @@ class Node:
         return self.name
 
 
+class OtherError(Exception):
+    pass
+
+
 class TaskmasterTestCase(unittest.TestCase):
 
     def test_next_task(self):
@@ -337,8 +341,10 @@ class TaskmasterTestCase(unittest.TestCase):
         assert not tm.next_task()
         t.executed()
 
-
     def test_cycle_detection(self):
+        """Test detecting dependency cycles
+
+        """
         n1 = Node("n1")
         n2 = Node("n2", [n1])
         n3 = Node("n3", [n2])
@@ -409,6 +415,8 @@ class TaskmasterTestCase(unittest.TestCase):
         assert tm.next_task() is None
 
     def test_executed(self):
+        """Test when a task has been executed
+        """
         pass
 
     def test_prepare(self):
@@ -430,6 +438,66 @@ class TaskmasterTestCase(unittest.TestCase):
         assert n1.prepared
         assert n2.prepared
 
+    def test_execute(self):
+        """Test executing a task
+
+        """
+        global built_text
+
+        n1 = Node("n1")
+        tm = SCons.Taskmaster.Taskmaster([n1])
+        t = tm.next_task()
+        t.execute()
+        assert built_text == "n1 built", built_text
+
+        def raise_UserError():
+            raise SCons.Errors.UserError
+        n2 = Node("n2")
+        n2.build = raise_UserError
+        tm = SCons.Taskmaster.Taskmaster([n2])
+        t = tm.next_task()
+        try:
+            t.execute()
+        except SCons.Errors.UserError:
+            pass
+        else:
+            raise TestFailed, "did not catch expected UserError"
+
+        def raise_BuildError():
+            raise SCons.Errors.BuildError
+        n3 = Node("n3")
+        n3.build = raise_BuildError
+        tm = SCons.Taskmaster.Taskmaster([n3])
+        t = tm.next_task()
+        try:
+            t.execute()
+        except SCons.Errors.BuildError:
+            pass
+        else:
+            raise TestFailed, "did not catch expected BuildError"
+
+        def raise_OtherError():
+            raise OtherError
+        n4 = Node("n4")
+        n4.build = raise_OtherError
+        tm = SCons.Taskmaster.Taskmaster([n4])
+        t = tm.next_task()
+        try:
+            t.execute()
+        except SCons.Errors.BuildError, e:
+            # On a generic (non-BuildError) exception from a Builder,
+            # the target should throw a BuildError exception with the
+            # args set to the exception value, instance, and traceback.
+            assert e.node == n4, e.node
+            assert e.errstr == "Exception", e.errstr
+            assert len(e.args) == 3, `e.args`
+            assert e.args[0] == OtherError, e.args[0]
+            assert isinstance(e.args[1], OtherError), type(e.args[1])
+            assert type(e.args[2]) == type(sys.exc_traceback), e.args[2]
+        else:
+            raise TestFailed, "did not catch expected BuildError"
+
+
 
 
 if __name__ == "__main__":