示例#1
0
 def test_gen_and_regen(self):
     content = "foo"
     output = self.from_build_dir("out")
     manifest_path = self.create_simple_echo_manifest(content, output)
     self.assertFalse(os.path.exists(output))
     ### 1st run. Generates the manifest
     self.take_fs_snapshot()
     samurai = self.samurai("-p", manifest_path, self.build_dir())
     self.assertProcReturn(samurai, 0)
     self.pdbg("========= build.ninja")
     self.pdbg(read_file(self.build_ninja_file()))
     self.assertFSDiff({self.build_ninja_file():FSDiffOp.CREATED,
                        self.decl_ninja_file():FSDiffOp.CREATED},
                       "ninja files not generated")
     ### 2nd run. Build outputs.
     ninja = self.ninja("-C", self.build_dir())
     self.assertProcReturn(ninja, 0)
     self.assertEqual(read_file(output), content+"\n")
     self.assertFSDiff({output:FSDiffOp.CREATED}, "output file not built")
     ### 3rd run: nothing to do
     ninja = self.ninja("-C", self.build_dir())
     self.assertEqual(ninja.returncode, 0)
     self.assertFSDiff({}, "ninja modified something")
     ### 4th run: touched manifest trigger regeneration by ninja
     time.sleep(1)
     touch(manifest_path)
     self.assertFSDiff({manifest_path:FSDiffOp.UPDATED},
                       "samurai manifest not touched")
     ninja = self.ninja("-C", self.build_dir())
     self.assertProcReturn(ninja, 0)
     self.pdbg("========= ninja output")
     self.pdbg(ninja.stdout)
     self.assertFSDiff({self.build_ninja_file():FSDiffOp.UPDATED,
                        self.decl_ninja_file():FSDiffOp.UPDATED},
                       "ninja manifest not regenerated")
示例#2
0
 def test_build_cascade_targets(self):
     input1 = os.path.join(self.tmpdir, "f.in")
     output1 = self.from_build_dir("f.out1")
     output2 = self.from_build_dir("f.out2")
     fs = self.create_fs_tree({
         "f.in": "line1\n",
         self.MANIFEST_FILENAME: \
             textwrap.dedent("""\
             from samurai.sys import context as samurai_ctxt
             from samurai.test.fixture import Fixture
             Prefix = Fixture.Prefix()
             samurai_ctxt.add(Prefix(input={input1!r}, output={output1!r}))
             samurai_ctxt.add(Prefix(input={output1!r}, output={output2!r}))
             """.format(input1=input1,
                        output1=output1,
                        output2=output2)),
     })
     ### 1st run. Generates the manifest
     self.take_fs_snapshot()
     samurai = self.samurai("-p", fs[self.MANIFEST_FILENAME],
                            self.build_dir())
     self.assertProcReturn(samurai, 0)
     self.pdbg("========= build.ninja")
     self.pdbg(read_file(self.build_ninja_file()))
     self.assertFSDiff({self.build_ninja_file():FSDiffOp.CREATED,
                        self.decl_ninja_file():FSDiffOp.CREATED},
                       "ninja files not generated")
     ### 2nd run. Build outputs.
     ninja = self.ninja("-C", self.build_dir())
     self.assertProcReturn(ninja, 0)
     self.assertFSDiff({output1:FSDiffOp.CREATED,
                        output2:FSDiffOp.CREATED},
                       "output files not built")
     self.assertEqual(read_file(output1), "prefix:line1\n")
     self.assertEqual(read_file(output2), "prefix:prefix:line1\n")
     ### 3rd run: nothing to do
     ninja = self.ninja("-C", self.build_dir())
     self.assertEqual(ninja.returncode, 0)
     self.assertFSDiff({}, "ninja modified something")
     ### 4th run: touch input file triggers rebuild of two outputs.
     time.sleep(1)
     touch(input1)
     self.assertFSDiff({input1:FSDiffOp.UPDATED},
                       "input1 file not touched")
     ninja = self.ninja("-C", self.build_dir())
     self.assertEqual(ninja.returncode, 0)
     self.assertFSDiff({output1:FSDiffOp.UPDATED,
                        output2:FSDiffOp.UPDATED},
                       "output files not re-built")
示例#3
0
 def test_relative_to_build_dir(self):
     self.dump(Fixture.Filter()(
         input=os.path.join(self.build_dir(), "file.in"),
         output=os.path.join(self.build_dir(), "file.out")))
     build_content = read_file(self.dumper.build_pathname())
     self.assertRegex(
         build_content,
         re.compile(r"^build file.out: r0 file.in | /path/to/filter$",
                    re.MULTILINE))
示例#4
0
 def test_reload_after_regen(self):
     content1 = "foo1"
     output1 = self.from_build_dir("out1")
     manifest_path = self.create_simple_echo_manifest(content1, output1)
     ### 1st run. Generates the manifest
     self.take_fs_snapshot()
     samurai = self.samurai("-p", manifest_path, self.build_dir())
     self.assertProcReturn(samurai, 0)
     self.pdbg("========= build.ninja")
     self.pdbg(read_file(self.build_ninja_file()))
     self.assertFSDiff({self.build_ninja_file():FSDiffOp.CREATED,
                        self.decl_ninja_file():FSDiffOp.CREATED},
                       "ninja files not generated")
     ### 2nd run. Build outputs.
     ninja = self.ninja("-C", self.build_dir())
     self.assertProcReturn(ninja, 0)
     self.assertFSDiff({output1:FSDiffOp.CREATED}, "output1 file not built")
     self.assertEqual(read_file(output1), content1+"\n")
     ### 3rd run: nothing to do
     ninja = self.ninja("-C", self.build_dir())
     self.assertEqual(ninja.returncode, 0)
     self.assertFSDiff({}, "ninja modified something")
     ### 4th run: add new target in manifest trigger regeneration by ninja
     ###          and build the new target
     time.sleep(1)
     content2 = "foo2"
     output2 = self.from_build_dir("out2")
     with open(manifest_path, "a") as stream:
         stream.write(
             "samurai_ctxt.add(Echo(text={text!r}, output={output!r}))\n"
             .format(text=content2, output=output2))
     self.assertFSDiff({manifest_path:FSDiffOp.UPDATED},
                       "samurai manifest not touched")
     ninja = self.ninja("-C", self.build_dir())
     self.assertProcReturn(ninja, 0)
     self.pdbg("========= ninja output")
     self.pdbg(ninja.stdout)
     self.assertFSDiff({self.build_ninja_file():FSDiffOp.UPDATED,
                        self.decl_ninja_file():FSDiffOp.UPDATED,
                        output2:FSDiffOp.CREATED},
                       "ninja manifest not regenerated or "
                       "new target not rebuilt")
     self.assertEqual(read_file(output2), content2+"\n")
示例#5
0
 def test_relative_to_top_build_dir(self):
     assert self.build_dir().startswith(self.tmpdir)
     self.dumper = NinjaDumper(self.build_dir(), top_build_dir=self.tmpdir)
     self.dump(Fixture.Filter()(
         input=os.path.join(self.build_dir(), "file.in"),
         output=os.path.join(self.build_dir(), "file.out")))
     build_content = read_file(self.dumper.build_pathname())
     self.assertRegex(
         build_content,
         re.compile(r"^build _build/file.out: r0 _build/file.in "
                    "| /path/to/filter$",
                    re.MULTILINE))
示例#6
0
 def test_interrupted_regen(self):
     content = "foo"
     output = self.from_build_dir("out")
     manifest_path = self.create_simple_echo_manifest(content, output)
     self.assertFalse(os.path.exists(output))
     ### 1st: Generates the manifest.
     self.take_fs_snapshot()
     samurai = self.samurai("-p", manifest_path, self.build_dir())
     self.assertProcReturn(samurai, 0)
     self.assertFSDiff({self.build_ninja_file():FSDiffOp.CREATED,
                        self.decl_ninja_file():FSDiffOp.CREATED},
                       "ninja files not generated")
     self.assertFalse(os.path.exists(output))
     ### 2nd: Generates the output.
     ninja = self.ninja("-C", self.build_dir())
     self.assertEqual(read_file(output), content+"\n")
     self.assertFSDiff({output:FSDiffOp.CREATED}, "output file not built")
     ### 3nd: Ninja has nothing to do
     ninja = self.ninja("-C", self.build_dir())
     self.assertEqual(ninja.returncode, 0)
     self.assertFSDiff({}, "nothing has changed")
     ### 4th: an error is introduced in the manifest, triggering the
     ###      regeneration which fails.
     time.sleep(1) # ninja mtime use second precision
     errmsg = "on-purpose error from manifest"
     with open(manifest_path, "a") as stream:
         stream.write(textwrap.dedent("""
         raise RuntimeError({errmsg!r})
         """.format(errmsg=errmsg)))
     self.assertFSDiff({manifest_path:FSDiffOp.UPDATED},
                       "samurai manifest not modified")
     # exception will be not raised because samurai is triggered by ninja,
     # not by this process.
     self.ninja("-C", self.build_dir())
     self.assertFSDiff({self.build_ninja_file()+".part":FSDiffOp.CREATED,
                        self.decl_ninja_file()+".part":FSDiffOp.CREATED},
                       "ninja part files are generated")
     ### 5th: Fix the error by regenerating the original samurai's manifest.
     ###      Ninja must regenerate its manifest successfully
     time.sleep(1) # ninja mtime use second precision
     manifest_path = self.create_simple_echo_manifest(content, output)
     self.assertFSDiff({manifest_path:FSDiffOp.UPDATED},
                       "samurai manifest not modified")
     self.ninja("-C", self.build_dir())
     self.assertFSDiff({self.build_ninja_file():FSDiffOp.UPDATED,
                        self.decl_ninja_file():FSDiffOp.UPDATED,
                        self.build_ninja_file()+".part":FSDiffOp.DELETED,
                        self.decl_ninja_file()+".part":FSDiffOp.DELETED},
                       "ninja files not regenerated")
     ### 6th: Ninja has nothing to do
     ninja = self.ninja("-C", self.build_dir())
     self.assertFSDiff({}, "ninja modified something")
示例#7
0
 def test_basic(self):
     manifest = os.path.join(self.tmpdir, "sub", "manifest")
     mkdir_p(os.path.dirname(manifest))
     write_file(manifest, "")
     build_dir = os.path.join(self.build_dir(), "sub")
     s = SubSamurai(manifest, build_dir)
     s.generate = MagicMock()
     self.dump(s)
     s.generate.assert_called_once_with()
     build_content = read_file(self.dumper.build_pathname())
     self.assertRegex(build_content,
                      re.compile(r"^# SubSamurai sub-project from",
                                 re.MULTILINE))
     self.assertRegex(build_content,
                      re.compile(r"^#.*'{manifest}'$"
                                 .format(manifest=manifest),
                                 re.MULTILINE))
     self.assertRegex(build_content,
                      re.compile(r"^subninja {build_dir}/build.ninja\n"
                                 .format(build_dir=build_dir),
                                 re.MULTILINE))
     self.assertEqual(self.dumper.rule_count, 0)
     self.assertEqual(self.dumper.build_count, 0)
     self.assertEqual(self.dumper.subgenerator_count, 1)
示例#8
0
 def print_decl_ninja_file(self):
     """Useful for debugging."""
     print(read_file(self.decl_ninja_file()))
示例#9
0
 def print_build_ninja_file(self):
     """Useful for debugging."""
     print(read_file(self.build_ninja_file()))
示例#10
0
 def test_subgen(self):
     content1 = "foo1"
     sub_content1 = "sub foo1"
     output1 = self.from_build_dir("out1")
     sub_output1 = self.from_build_dir("sub", "out1")
     fs = self.create_fs_tree({
         self.MANIFEST_FILENAME: \
             textwrap.dedent("""\
             from samurai.sys import context as samurai_ctxt
             from samurai.subgenerator import SubSamurai
             from samurai.test.fixture import Fixture
             samurai_ctxt.add(SubSamurai("{manifest}", "{build_dir}/sub"))
             Echo = Fixture.Echo()
             samurai_ctxt.add(Echo(text={text!r}, output={output!r}))
             """.format(text=content1, output=output1,
                        manifest=os.path.join(self.tmpdir, "sub",
                                              self.MANIFEST_FILENAME),
                        build_dir=self.build_dir())),
         "sub": {
             self.MANIFEST_FILENAME: \
                 textwrap.dedent("""\
                 from samurai.sys import context as samurai_ctxt
                 from samurai.test.fixture import Fixture
                 Echo = Fixture.Echo()
                 samurai_ctxt.add(Echo(text={text!r}, output={output!r}))
                 """.format(text=sub_content1, output=sub_output1)),
         },
     })
     sub_build_ninja_file = self.from_build_dir("sub",
                                                config.BUILD_FILENAME)
     sub_decl_ninja_file = self.from_build_dir("sub",
                                               config.DECL_FILENAME)
     self.pdbg("====== manifest")
     self.pdbg(read_file(fs[self.MANIFEST_FILENAME]))
     ### 1st run: generates both projects.
     self.pdbg("====== #1")
     self.take_fs_snapshot()
     samurai = self.samurai("-C", self.tmpdir, "-p",
                            self.MANIFEST_FILENAME, self.BUILD_DIR)
     self.assertProcReturn(samurai, 0)
     self.pdbg("====== build.ninja")
     self.pdbg(read_file(self.build_ninja_file()))
     self.pdbg("====== sub/build.ninja")
     self.pdbg(read_file(sub_build_ninja_file))
     self.assertFSDiff({self.build_ninja_file():FSDiffOp.CREATED,
                        self.decl_ninja_file():FSDiffOp.CREATED,
                        sub_build_ninja_file:FSDiffOp.CREATED,
                        sub_decl_ninja_file:FSDiffOp.CREATED},
                       "all ninja files not generated")
     ### 2nd run: build outputs
     self.pdbg("====== #2")
     ninja = self.ninja("-C", self.build_dir())
     self.assertProcReturn(ninja, 0)
     self.assertFSDiff({output1:FSDiffOp.CREATED,
                        sub_output1:FSDiffOp.CREATED},
                       "output1 and sub_output1 not built")
     self.assertEqual(read_file(output1), content1+"\n")
     self.assertEqual(read_file(sub_output1), sub_content1+"\n")
     ### 3rd run: nothing to do
     self.pdbg("====== #3")
     ninja = self.ninja("-C", self.build_dir())
     self.assertEqual(ninja.returncode, 0)
     self.assertFSDiff({}, "nothing has changed")
     ### 4th run: change sub output name
     self.pdbg("====== #4")
     sub_content2 = "sub foo2"
     sub_output2 = self.from_build_dir("sub", "out2")
     time.sleep(1)
     write_file(fs['sub'][self.MANIFEST_FILENAME],
                textwrap.dedent("""\
                from samurai.sys import context as samurai_ctxt
                from samurai.test.fixture import Fixture
                Echo = Fixture.Echo()
                samurai_ctxt.add(Echo(text={text!r}, output={output!r}))
                """.format(text=sub_content2, output=sub_output2)))
     self.assertFSDiff({fs['sub'][self.MANIFEST_FILENAME]:FSDiffOp.UPDATED},
                       "sub samurai's manifest not updated")
     ### 5th run: rebuild outputs
     self.pdbg("====== #5")
     ninja = self.ninja("-C", self.build_dir())
     self.pdbg("====== ninja output")
     self.pdbg(ninja.stdout)
     self.assertProcReturn(ninja, 0)
     self.assertFSDiff({sub_output1:FSDiffOp.DELETED,
                        sub_output2:FSDiffOp.CREATED,
                        sub_build_ninja_file:FSDiffOp.UPDATED,
                        sub_decl_ninja_file:FSDiffOp.UPDATED},
                       "rebuild failed")
     ### 6th run: change both output file name
     self.pdbg("====== #6")
     content2 = "foo2"
     output2 = self.from_build_dir("out2")
     sub_content3 = "sub foo3"
     sub_output3 = self.from_build_dir("sub", "out3")
     time.sleep(1)
     write_file(fs[self.MANIFEST_FILENAME],
                textwrap.dedent("""\
                from samurai.sys import context as samurai_ctxt
                from samurai.subgenerator import SubSamurai
                from samurai.test.fixture import Fixture
                samurai_ctxt.add(SubSamurai("{manifest}", "{build_dir}/sub"))
                Echo = Fixture.Echo()
                samurai_ctxt.add(Echo(text={text!r}, output={output!r}))
                """.format(text=content2, output=output2,
                           manifest=os.path.join(self.tmpdir, "sub",
                                                 self.MANIFEST_FILENAME),
                           build_dir=self.build_dir())))
     write_file(fs['sub'][self.MANIFEST_FILENAME],
                textwrap.dedent("""\
                from samurai.sys import context as samurai_ctxt
                from samurai.test.fixture import Fixture
                Echo = Fixture.Echo()
                samurai_ctxt.add(Echo(text={text!r}, output={output!r}))
                """.format(text=sub_content3, output=sub_output3)))
     self.assertFSDiff({fs[self.MANIFEST_FILENAME]:FSDiffOp.UPDATED,
                        fs['sub'][self.MANIFEST_FILENAME]:FSDiffOp.UPDATED},
                       "both samurai's manifest not updated")
     ### 7th run: rebuild outputs
     self.pdbg("====== #7")
     ninja = self.ninja("-C", self.build_dir())
     self.pdbg("====== ninja output")
     self.pdbg(ninja.stdout)
     self.assertProcReturn(ninja, 0)
     self.assertFSDiff({sub_output2:FSDiffOp.DELETED,
                        sub_output3:FSDiffOp.CREATED,
                        output1:FSDiffOp.DELETED,
                        output2:FSDiffOp.CREATED,
                        self.build_ninja_file():FSDiffOp.UPDATED,
                        self.decl_ninja_file():FSDiffOp.UPDATED,
                        sub_build_ninja_file:FSDiffOp.UPDATED,
                        sub_decl_ninja_file:FSDiffOp.UPDATED},
                       "rebuild failed")
示例#11
0
 def test_reload_after_regen(self):
     content1 = "foo1"
     sub_content1 = "sub foo1"
     output1 = self.from_build_dir("out1")
     sub_output1 = self.from_build_dir("sub", "out1")
     fs = self.create_fs_tree({
         self.MANIFEST_FILENAME: \
             textwrap.dedent("""\
             from samurai.sys import context as samurai_ctxt
             from samurai.subgenerator import SubSamurai
             from samurai.test.fixture import Fixture
             samurai_ctxt.add(SubSamurai("{manifest}", "{build_dir}/sub"))
             Echo = Fixture.Echo()
             samurai_ctxt.add(Echo(text={text!r}, output={output!r}))
             """.format(text=content1, output=output1,
                        manifest=os.path.join(self.tmpdir, "sub",
                                              self.MANIFEST_FILENAME),
                        build_dir=self.build_dir())),
         "sub": {
             self.MANIFEST_FILENAME: \
                 textwrap.dedent("""\
                 from samurai.sys import context as samurai_ctxt
                 from samurai.test.fixture import Fixture
                 Echo = Fixture.Echo()
                 samurai_ctxt.add(Echo(text={text!r}, output={output!r}))
                 """.format(text=sub_content1, output=sub_output1)),
         },
     })
     sub_build_ninja_file = self.from_build_dir("sub",
                                                config.BUILD_FILENAME)
     sub_decl_ninja_file = self.from_build_dir("sub",
                                               config.DECL_FILENAME)
     self.pdbg("====== manifest")
     self.pdbg(read_file(fs[self.MANIFEST_FILENAME]))
     ### 1st run: generates and build both projects.
     self.pdbg("====== #1")
     self.take_fs_snapshot()
     samurai = self.samurai("-C", self.tmpdir, "-p",
                            self.MANIFEST_FILENAME, self.BUILD_DIR)
     self.assertProcReturn(samurai, 0)
     self.pdbg("====== build.ninja")
     self.pdbg(read_file(self.build_ninja_file()))
     self.pdbg("====== sub/build.ninja")
     self.pdbg(read_file(sub_build_ninja_file))
     self.assertFSDiff({self.build_ninja_file():FSDiffOp.CREATED,
                        self.decl_ninja_file():FSDiffOp.CREATED,
                        sub_build_ninja_file:FSDiffOp.CREATED,
                        sub_decl_ninja_file:FSDiffOp.CREATED},
                       "all ninja files not generated")
     ### 2nd run: build outputs
     self.pdbg("====== #2")
     ninja = self.ninja("-C", self.build_dir())
     self.assertProcReturn(ninja, 0)
     self.assertFSDiff({output1:FSDiffOp.CREATED,
                        sub_output1:FSDiffOp.CREATED},
                       "output1 and sub_output1 not built")
     self.assertEqual(read_file(output1), content1+"\n")
     self.assertEqual(read_file(sub_output1), sub_content1+"\n")
     ### 3rd run: nothing to do
     self.pdbg("====== #3")
     ninja = self.ninja("-C", self.build_dir())
     self.assertEqual(ninja.returncode, 0)
     self.assertFSDiff({}, "nothing has changed")
     ### 4th run: add new target in sub-manifest makes ninja regenerates
     ###          only the sub-project and build the new target
     self.pdbg("====== #4")
     time.sleep(1)
     sub_content2 = "sub foo2"
     sub_output2 = self.from_build_dir("sub", "out2")
     with open(fs["sub"][self.MANIFEST_FILENAME], "a") as stream:
         stream.write(
             "samurai_ctxt.add(Echo(text={text!r}, output={output!r}))\n"
             .format(text=sub_content2, output=sub_output2))
     self.assertFSDiff({fs["sub"][self.MANIFEST_FILENAME]:FSDiffOp.UPDATED},
                       "samurai sub-manifest not touched")
     ninja = self.ninja("-C", self.build_dir())
     self.assertProcReturn(ninja, 0)
     self.assertFSDiff({sub_build_ninja_file:FSDiffOp.UPDATED,
                        sub_decl_ninja_file:FSDiffOp.UPDATED,
                        sub_output2:FSDiffOp.CREATED},
                       "sub ninja files not re-generated or "
                       "new target not built")
     self.assertEqual(read_file(sub_output2), sub_content2+"\n")
     ### 5th run: add new target in top manifest makes ninja regenerates
     ###          only its top manifest but not the one of the sub-projects
     ###          and build the new target.
     self.pdbg("====== #5")
     time.sleep(1)
     content2 = "foo2"
     output2 = self.from_build_dir("out2")
     with open(fs[self.MANIFEST_FILENAME], "a") as stream:
         stream.write(
             "samurai_ctxt.add(Echo(text={text!r}, output={output!r}))\n"
             .format(text=content2, output=output2))
     self.assertFSDiff({fs[self.MANIFEST_FILENAME]:FSDiffOp.UPDATED},
                       "samurai top manifest not touched")
     ninja = self.ninja("-C", self.build_dir())
     self.assertProcReturn(ninja, 0)
     self.assertFSDiff({self.build_ninja_file():FSDiffOp.UPDATED,
                        self.decl_ninja_file():FSDiffOp.UPDATED,
                        output2:FSDiffOp.CREATED},
                       "ninja files not re-generated or "
                       "new target not built")
     self.assertEqual(read_file(output2), content2+"\n")
     ### 6th run: touched both manifest makes ninja regenerates both
     self.pdbg("====== #6")
     time.sleep(1)
     content3 = "foo3"
     output3 = self.from_build_dir("out3")
     with open(fs[self.MANIFEST_FILENAME], "a") as stream:
         stream.write(
             "samurai_ctxt.add(Echo(text={text!r}, output={output!r}))\n"
             .format(text=content3, output=output3))
     sub_content3 = "sub foo3"
     sub_output3 = self.from_build_dir("sub", "out3")
     with open(fs["sub"][self.MANIFEST_FILENAME], "a") as stream:
         stream.write(
             "samurai_ctxt.add(Echo(text={text!r}, output={output!r}))\n"
             .format(text=sub_content3, output=sub_output3))
     self.assertFSDiff({fs[self.MANIFEST_FILENAME]:FSDiffOp.UPDATED,
                        fs["sub"][self.MANIFEST_FILENAME]:FSDiffOp.UPDATED},
                       "all samurai manifest not touched")
     ninja = self.ninja("-C", self.build_dir())
     self.assertProcReturn(ninja, 0)
     self.assertFSDiff({self.build_ninja_file():FSDiffOp.UPDATED,
                        self.decl_ninja_file():FSDiffOp.UPDATED,
                        sub_build_ninja_file:FSDiffOp.UPDATED,
                        sub_decl_ninja_file:FSDiffOp.UPDATED,
                        output3:FSDiffOp.CREATED,
                        sub_output3:FSDiffOp.CREATED},
                       "all ninja files not re-generated or "
                       "new targets not built")
     self.assertEqual(read_file(output3), content3+"\n")
     self.assertEqual(read_file(sub_output3), sub_content3+"\n")
示例#12
0
 def test_gen_and_regen(self):
     content = "foo"
     sub_content = "sub foo"
     output = self.from_build_dir("out")
     sub_output = self.from_build_dir("sub", "out")
     fs = self.create_fs_tree({
         self.MANIFEST_FILENAME: \
             textwrap.dedent("""\
             from samurai.sys import context as samurai_ctxt
             from samurai.subgenerator import SubSamurai
             from samurai.test.fixture import Fixture
             samurai_ctxt.add(SubSamurai("{manifest}", "{build_dir}/sub"))
             Echo = Fixture.Echo()
             samurai_ctxt.add(Echo(text={text!r}, output={output!r}))
             """.format(text=content, output=output,
                        manifest=os.path.join(self.tmpdir, "sub",
                                              self.MANIFEST_FILENAME),
                        build_dir=self.build_dir())),
         "sub": {
             self.MANIFEST_FILENAME: \
                 textwrap.dedent("""\
                 from samurai.sys import context as samurai_ctxt
                 from samurai.test.fixture import Fixture
                 Echo = Fixture.Echo()
                 samurai_ctxt.add(Echo(text={text!r}, output={output!r}))
                 """.format(text=sub_content, output=sub_output)),
         },
     })
     sub_build_ninja_file = self.from_build_dir("sub",
                                                config.BUILD_FILENAME)
     sub_decl_ninja_file = self.from_build_dir("sub",
                                               config.DECL_FILENAME)
     self.pdbg("====== manifest")
     self.pdbg(read_file(fs[self.MANIFEST_FILENAME]))
     ### 1st run: generates and build both projects.
     self.pdbg("====== #1")
     self.take_fs_snapshot()
     samurai = self.samurai("-C", self.tmpdir, "-p",
                            self.MANIFEST_FILENAME, self.BUILD_DIR)
     self.assertProcReturn(samurai, 0)
     self.pdbg("====== build.ninja")
     self.pdbg(read_file(self.build_ninja_file()))
     self.pdbg("====== sub/build.ninja")
     self.pdbg(read_file(sub_build_ninja_file))
     self.assertFSDiff({self.build_ninja_file():FSDiffOp.CREATED,
                        self.decl_ninja_file():FSDiffOp.CREATED,
                        sub_build_ninja_file:FSDiffOp.CREATED,
                        sub_decl_ninja_file:FSDiffOp.CREATED},
                       "all ninja files not generated")
     ### 2nd run: build outputs
     self.pdbg("====== #2")
     ninja = self.ninja("-C", self.build_dir())
     self.assertProcReturn(ninja, 0)
     self.assertFSDiff({output:FSDiffOp.CREATED,
                        sub_output:FSDiffOp.CREATED},
                       "output files not built")
     self.assertEqual(read_file(output), content+"\n")
     self.assertEqual(read_file(sub_output), sub_content+"\n")
     ### 3rd run: nothing to do
     self.pdbg("====== #3")
     ninja = self.ninja("-C", self.build_dir())
     self.assertEqual(ninja.returncode, 0)
     self.assertFSDiff({}, "nothing has changed")
     ### 4th run: touched sub-manifest makes ninja regenerates only the
     ###          sub-project.
     self.pdbg("====== #4")
     time.sleep(1)
     touch(fs["sub"][self.MANIFEST_FILENAME])
     self.assertFSDiff({fs["sub"][self.MANIFEST_FILENAME]:FSDiffOp.UPDATED},
                       "sub samurai manifest not touched")
     ninja = self.ninja("-C", self.build_dir())
     self.assertProcReturn(ninja, 0)
     self.assertFSDiff({sub_build_ninja_file:FSDiffOp.UPDATED,
                        sub_decl_ninja_file:FSDiffOp.UPDATED},
                       "sub ninja files not re-generated")
     ### 5th run: touched manifest makes ninja regenerates only its
     ###          manifest but not the one of the sub-projects.
     self.pdbg("====== #5")
     time.sleep(1)
     touch(fs[self.MANIFEST_FILENAME])
     self.assertFSDiff({fs[self.MANIFEST_FILENAME]:FSDiffOp.UPDATED},
                       "samurai manifest not touched")
     ninja = self.ninja("-C", self.build_dir())
     self.assertProcReturn(ninja, 0)
     self.assertFSDiff({self.build_ninja_file():FSDiffOp.UPDATED,
                        self.decl_ninja_file():FSDiffOp.UPDATED},
                       "ninja files not re-generated")
     ### 6th run: touched both manifest makes ninja regenerates both
     self.pdbg("====== #6")
     time.sleep(1)
     touch(fs[self.MANIFEST_FILENAME])
     touch(fs["sub"][self.MANIFEST_FILENAME])
     self.assertFSDiff({fs[self.MANIFEST_FILENAME]:FSDiffOp.UPDATED,
                        fs["sub"][self.MANIFEST_FILENAME]:FSDiffOp.UPDATED},
                       "all samurai manifest not touched")
     ninja = self.ninja("-C", self.build_dir())
     self.assertProcReturn(ninja, 0)
     self.assertFSDiff({self.build_ninja_file():FSDiffOp.UPDATED,
                        self.decl_ninja_file():FSDiffOp.UPDATED,
                        sub_build_ninja_file:FSDiffOp.UPDATED,
                        sub_decl_ninja_file:FSDiffOp.UPDATED},
                       "all ninja files not re-generated")