class NinjaDumperTestCase(SamuraiBuildTestCase): def setUp(self): super().setUp() mkdir_p(self.build_dir()) self.dumper = NinjaDumper(self.build_dir()) def assertFileContent(self, filename, expected_content): with open(filename) as stream: self.assertMultiLineEqual(stream.read(), expected_content) def assertDumpedFileContent(self, filename, expected_content): """Like assertFileContent but caller should not include file header.""" self.assertFileContent(filename, add_generator_caution(expected_content)) def assertBuildFile(self, expected_build_content): header = textwrap.dedent(""" ninja_required_version = 1.6 include {} """).format(self.dumper.decl_pathname()) self.assertDumpedFileContent(self.dumper.build_pathname(), header + expected_build_content) def assertDeclFile(self, expected_decl_content): self.assertDumpedFileContent(self.dumper.decl_pathname(), expected_decl_content) def dump(self, *args, **kwargs): """Shortcut when the test has to dump only one thing.""" with self.dumper: self.dumper.dump(*args, **kwargs)
def generate(self): samurai_sys.build_dir = self.build_dir samurai_sys.output = self.output top_build_dir = os.getenv("SAMURAI_TOP_BUILD_DIR") dumper = NinjaDumper(self.build_dir, top_build_dir=top_build_dir) with _rm_abandoned_outputs(self.output, top_build_dir=top_build_dir), \ _TerminationLog(dumper), \ _install_sighandlers(_on_interrupt, SIGTERM, SIGHUP), \ dumper: context = Context(dumper) samurai_sys.context = context evaluator = Evaluator(self.manifest) evaluator.evaluate() dumper.dump(AutoRegen(self.manifest, self.build_dir, context, evaluator))
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))
class TestCommand(NinjaDumperTestCase): def test_simple(self): self.dump(Fixture.Filter()(input="file.in", output="file.out")) self.assertDeclFile(RULE0) self.assertBuildFile( textwrap.dedent(""" build file.out: r0 file.in | /path/to/myfilter C = /path/to/myfilter file.in file.out D = Filtering file.out """)) def test_command_with_no_outputs(self): c = Fixture.Sleep()() with self.assertRaises(ValueError): self.dump(c) def test_dump_rule_once(self): Echo = Fixture.Echo() with self.dumper: self.dumper.dump(Echo(text="foo", output="foo.out")) self.dumper.dump(Echo(text="bar", output="bar.out")) self.assertDeclFile(RULE0) self.assertBuildFile( textwrap.dedent(""" build foo.out: r0 C = echo foo > foo.out D = Echo-ing 'foo' to foo.out... build bar.out: r0 C = echo bar > bar.out D = Echo-ing 'bar' to bar.out... """)) self.assertEqual(self.dumper.rule_count, 1) self.assertEqual(self.dumper.build_count, 2) self.assertEqual(self.dumper.subgenerator_count, 0) def test_dump_implicit_inputs(self): Echo = Fixture.Echo() echo = Echo(text="foo", output="foo.out", implicit_inputs="bar") with self.dumper: self.dumper.dump(echo) self.assertDeclFile(RULE0) self.assertBuildFile( textwrap.dedent(""" build foo.out: r0 | bar C = echo foo > foo.out D = Echo-ing 'foo' to foo.out... """)) def test_dump_implicit_outputs(self): Echo = Fixture.Echo() echo = Echo(text="foo", output="foo.out", implicit_outputs="bar") with self.dumper: self.dumper.dump(echo) self.assertDeclFile(RULE0) self.assertBuildFile( textwrap.dedent(""" build bar foo.out: r0 C = echo foo > foo.out D = Echo-ing 'foo' to foo.out... """)) self.assertEqual(self.dumper.rule_count, 1) self.assertEqual(self.dumper.build_count, 1) self.assertEqual(self.dumper.subgenerator_count, 0) def test_dump_generator(self): class Gen(Command): command = "mygen {o@output}" generator = True with self.dumper: self.dumper.dump(Gen(output="foo")) self.assertDeclFile(RULE0) self.assertBuildFile( textwrap.dedent(""" build foo: r0 C = mygen foo G = 1 """)) def test_dump_restat(self): class Restat(Command): command = "myrestat {o@output}" restat = True self.dump(Restat(output="foo")) self.assertDeclFile(RULE0) self.assertBuildFile( textwrap.dedent(""" build foo: r0 C = myrestat foo R = 1 """)) def test_acceptPathLib_object(self): Filter = Fixture.Filter() filter = Filter(input=PathLib("foo"), output=PathLib("bar")) with self.dumper: self.dumper.dump(filter) self.assertDeclFile(RULE0) self.assertBuildFile( textwrap.dedent(""" build bar: r0 foo | /path/to/myfilter C = /path/to/myfilter foo bar D = Filtering bar """)) def test_call_formatter(self): class TrecEval(Command): command = "trec_eval{details} {i@relevants} {i@run} > {o@eval}" description = "Evaluating {details}{run}" def __init__(self, details=True, **kwds): super().__init__(details=details, **kwds) @details.formatter def details(self): return " -q" if self.details else "" with self.dumper: self.dumper.dump(TrecEval(relevants="file.rel", run="file.run", eval="file.eval")) self.assertDeclFile(RULE0) self.assertBuildFile( textwrap.dedent(""" build file.eval: r0 file.rel file.run C = trec_eval -q file.rel file.run > file.eval D = Evaluating -qfile.run """)) def test_escape_filenames(self): MultiIO = Fixture.MultiIO() cmd = MultiIO(threshold="42 5", inp1=PathLib("i 1"), inp2="i 2", out1=PathLib("o 1"), out2="o 2", implicit_inputs=("ii 1", "ii 2"), orderonly_inputs=("ooi 1", "ooi 2")) with self.dumper: self.dumper.dump(cmd) self.assertDeclFile(RULE0) self.assertBuildFile( textwrap.dedent(""" build o$ 1 o$ 2: r0 i$ 1 i$ 2 | ii$ 1 ii$ 2 || ooi$ 1 ooi$ 2 C = multio -p 42 5 'i 1' 'i 2' 'o 1' 'o 2' D = Doing 'o 1' and 'o 2' """)) def test_cannot_unhandled_object(self): with self.assertRaisesRegex(TypeError, r"cannot dump NoneType object: None"): self.dump(None) 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)) 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)) def test_reject_empty_command_string(self): class Empty(Command): command = "" c = Empty(implicit_outputs="o") with self.assertRaisesRegex(ValueError, r"empty command string"): self.dump(c) def test_escape_dollar_in_command(self): class C(Command): command = "cat {i@input} | sed -e 's/ .*$//' > {o@output}" description = "Removing $ in command producing {output}" self.dump(C(input="i", output="o")) self.assertDeclFile(RULE0) self.assertBuildFile( textwrap.dedent(""" build o: r0 i C = cat i | sed -e 's/ .*$$//' > o D = Removing $$ in command producing o """)) def test_format_io_list_value(self): Cat = Fixture.Cat() Dup = Fixture.Dup() with self.dumper: self.dumper.dump(Cat(input_files=("f 0", ["f 1"]), output="j 0")) self.dumper.dump(Dup(input="f 0", dupes=("d 0", ["d 1"]))) self.assertDeclFile(RULE0) self.assertBuildFile( textwrap.dedent(""" build j$ 0: r0 f$ 0 f$ 1 C = cat 'f 0' 'f 1' > 'j 0' build d$ 0 d$ 1: r0 f$ 0 C = dup 'f 0' 'd 0' 'd 1' """)) def test_dump_tuple_command(self): class C(Command): command = ("cmd", "{o@output}") self.dump(C(output="out")) self.assertDeclFile(RULE0) self.assertBuildFile( textwrap.dedent(""" build out: r0 C = cmd out """))
def setUp(self): super().setUp() mkdir_p(self.build_dir()) self.dumper = NinjaDumper(self.build_dir())