def test_target_proxy_exceptions(self): self.add_to_build_file('a/BUILD', 'dependencies()') build_file_a = BuildFile(self.build_root, 'a/BUILD') with pytest.raises(ValueError): self.build_file_parser.parse_build_file(build_file_a) self.add_to_build_file('b/BUILD', 'dependencies(name="foo", "bad_arg")') build_file_b = BuildFile(self.build_root, 'b/BUILD') with pytest.raises(SyntaxError): self.build_file_parser.parse_build_file(build_file_b) self.add_to_build_file('c/BUILD', 'dependencies(name="foo", build_file="bad")') build_file_c = BuildFile(self.build_root, 'c/BUILD') with pytest.raises(ValueError): self.build_file_parser.parse_build_file(build_file_c) self.add_to_build_file( 'd/BUILD', dedent(''' dependencies(name="foo", dependencies=[ object(), ] ) ''')) build_file_d = BuildFile(self.build_root, 'd/BUILD') with pytest.raises(TargetDefinitionException): self.build_file_parser.parse_build_file(build_file_d)
def test_addressable_exceptions(self): self.add_to_build_file('a/BUILD', 'target()') build_file_a = BuildFile(self.build_root, 'a/BUILD') with pytest.raises(BuildFileParser.ExecuteError): self.build_file_parser.parse_build_file(build_file_a) self.add_to_build_file('b/BUILD', 'target(name="foo", "bad_arg")') build_file_b = BuildFile(self.build_root, 'b/BUILD') with pytest.raises(BuildFileParser.BuildFileParserError): self.build_file_parser.parse_build_file(build_file_b) self.add_to_build_file( 'd/BUILD', dedent(''' target( name="foo", dependencies=[ object(), ] ) ''')) build_file_d = BuildFile(self.build_root, 'd/BUILD') with pytest.raises(BuildFileParser.BuildFileParserError): self.build_file_parser.parse_build_file(build_file_d)
def testMustExistTrue(self): with self.assertRaises(BuildFile.MissingBuildFileError): BuildFile(BuildFileTest.root_dir, "path-that-does-not-exist/BUILD", must_exist=True) with self.assertRaises(BuildFile.MissingBuildFileError): BuildFile(BuildFileTest.root_dir, "path-that-does-exist/BUILD", must_exist=True) with self.assertRaises(BuildFile.MissingBuildFileError): BuildFile(BuildFileTest.root_dir, "path-that-does-exist/BUILD.invalid.suffix", must_exist=True)
def test_sibling_build_files(self): self.add_to_build_file('BUILD', dedent( ''' fake(name="base", dependencies=[ ':foo', ]) ''')) self.add_to_build_file('BUILD.foo', dedent( ''' fake(name="foo", dependencies=[ ':bat', ]) ''')) self.add_to_build_file('./BUILD.bar', dedent( ''' fake(name="bat") ''')) bar_build_file = BuildFile(self.build_root, 'BUILD.bar') base_build_file = BuildFile(self.build_root, 'BUILD') foo_build_file = BuildFile(self.build_root, 'BUILD.foo') address_map = self.build_file_parser.address_map_from_spec_path(bar_build_file.spec_path) addresses = address_map.keys() self.assertEqual(set([bar_build_file, base_build_file, foo_build_file]), set([address.build_file for address in addresses])) self.assertEqual(set([':base', ':foo', ':bat']), set([address.spec for address in addresses]))
def test_build_file_forms(self): with self.workspace('a/b/c/BUILD') as root_dir: build_file = BuildFile(root_dir, relpath='a/b/c') self.assert_address('a/b/c', 'c', BuildFileAddress(build_file)) self.assert_address('a/b/c', 'foo', BuildFileAddress(build_file, target_name='foo')) self.assertEqual('a/b/c:foo', BuildFileAddress(build_file, target_name='foo').spec) with self.workspace('BUILD') as root_dir: build_file = BuildFile(root_dir, relpath='') self.assert_address('', 'foo', BuildFileAddress(build_file, target_name='foo')) self.assertEqual(':foo', BuildFileAddress(build_file, target_name='foo').spec)
def test_sibling_build_files_duplicates(self): # This workspace is malformed, you can't shadow a name in a sibling BUILD file self.add_to_build_file( 'BUILD', dedent(''' fake(name="base", dependencies=[ ':foo', ]) ''')) self.add_to_build_file( 'BUILD.foo', dedent(''' fake(name="foo", dependencies=[ ':bat', ]) ''')) self.add_to_build_file('./BUILD.bar', dedent(''' fake(name="base") ''')) with pytest.raises(BuildFileParser.SiblingConflictException): base_build_file = BuildFile(self.build_root, 'BUILD') bf_address = BuildFileAddress(base_build_file, 'base') self.build_file_parser.address_map_from_spec_path( bf_address.spec_path)
def test_register_target_alias(self): class Fred(Target): pass self.build_configuration.register_target_alias('fred', Fred) aliases = self.build_configuration.registered_aliases() self.assertEqual({}, aliases.objects) self.assertEqual({}, aliases.context_aware_object_factories) self.assertEqual(dict(fred=Fred), aliases.targets) build_file = BuildFile('/tmp', 'fred', must_exist=False) parse_state = self.build_configuration.initialize_parse_state( build_file) self.assertEqual(0, len(parse_state.registered_addressable_instances)) self.assertEqual(2, len(parse_state.parse_globals)) self.assertEqual(parse_state.parse_globals['__file__'], os.path.realpath('/tmp/fred')) target_call_proxy = parse_state.parse_globals['fred'] target_call_proxy(name='jake') self.assertEqual(1, len(parse_state.registered_addressable_instances)) name, target_proxy = parse_state.registered_addressable_instances.pop() self.assertEqual('jake', target_proxy.name) self.assertEqual(Fred, target_proxy.target_type)
def test_noop_parse(self): with self.workspace('BUILD') as root_dir: parser = BuildFileParser(root_dir=root_dir) build_file = BuildFile(root_dir, '') parser.parse_build_file(build_file) registered_proxies = set(parser._target_proxy_by_address.values()) self.assertEqual(len(registered_proxies), 0)
def test_context_aware_object_factories(self): contents = dedent(""" create_java_libraries(base_name="create-java-libraries", provides_java_name="test-java", provides_scala_name="test-scala") make_lib("com.foo.test", "does_not_exists", "1.0") path_util("baz") """) self.create_file('3rdparty/BUILD', contents) build_file = BuildFile(FileSystemProjectTree(self.build_root), '3rdparty/BUILD') address_map = self.build_file_parser.parse_build_file(build_file) registered_proxies = set(address_map.values()) self.assertEqual(len(registered_proxies), 3) targets_created = {} for target_proxy in registered_proxies: targets_created[target_proxy.addressed_name] = target_proxy.addressed_type self.assertEqual({'does_not_exists', 'create-java-libraries-scala', 'create-java-libraries-java'}, set(targets_created.keys())) self.assertEqual(targets_created['does_not_exists'], self.JarLibrary) self.assertEqual(targets_created['create-java-libraries-java'], self.JavaLibrary) self.assertEqual(targets_created['create-java-libraries-scala'], self.ScalaLibrary) self.assertEqual({'3rdparty/baz'}, self._paths)
def test_build_file_duplicates(self): # This workspace has two targets in the same file with the same name. self.add_to_build_file('BUILD', 'fake(name="foo")\n') self.add_to_build_file('BUILD', 'fake(name="foo")\n') with pytest.raises(BuildFileParser.AddressableConflictException): base_build_file = BuildFile(self.build_root, 'BUILD') self.build_file_parser.parse_build_file(base_build_file)
def test_trivial_target(self): self.add_to_build_file('BUILD', '''fake(name='foozle')''') build_file = BuildFile(self.build_root, 'BUILD') address_map = self.build_file_parser.parse_build_file(build_file) self.assertEqual(len(address_map), 1) address, addressable = address_map.popitem() self.assertEqual(address, BuildFileAddress(build_file, 'foozle')) self.assertEqual(addressable.name, 'foozle') self.assertEqual(addressable.target_type, ErrorTarget)
def add_to_build_file(self, relpath, target): """Adds the given target specification to the BUILD file at relpath. relpath: The relative path to the BUILD file from the build root. target: A string containing the target definition as it would appear in a BUILD file. """ self.create_file(self.build_path(relpath), target, mode='a') return BuildFile(root_dir=self.build_root, relpath=self.build_path(relpath))
def test_sibling_build_files(self): with self.workspace('./BUILD', './BUILD.foo', './BUILD.bar') as root_dir: with open(os.path.join(root_dir, './BUILD'), 'w') as build: build.write( dedent(''' fake(name="base", dependencies=[ ':foo', ]) ''')) with open(os.path.join(root_dir, './BUILD.foo'), 'w') as build: build.write( dedent(''' fake(name="foo", dependencies=[ ':bat', ]) ''')) with open(os.path.join(root_dir, './BUILD.bar'), 'w') as build: build.write(dedent(''' fake(name="bat") ''')) def fake_target(*args, **kwargs): assert False, "This fake target should never be called in this test!" alias_map = {'target_aliases': {'fake': fake_target}} self.build_file_parser.register_alias_groups(alias_map=alias_map) bar_build_file = BuildFile(root_dir, 'BUILD.bar') base_build_file = BuildFile(root_dir, 'BUILD') foo_build_file = BuildFile(root_dir, 'BUILD.foo') self.build_file_parser.parse_build_file_family(bar_build_file) addresses = self.build_file_parser._target_proxy_by_address.keys() self.assertEqual( set([bar_build_file, base_build_file, foo_build_file]), set([address.build_file for address in addresses])) self.assertEqual(set([':base', ':foo', ':bat']), set([address.spec for address in addresses]))
def _candidate_owners(self, path): build_file = BuildFile(get_buildroot(), relpath=os.path.dirname(path), must_exist=False) if build_file.exists(): yield build_file for sibling in build_file.siblings(): yield sibling for ancestor in build_file.ancestors(): yield ancestor
def add_to_build_file(self, relpath, target): """Adds the given target specification to the BUILD file at relpath. :API: public relpath: The relative path to the BUILD file from the build root. target: A string containing the target definition as it would appear in a BUILD file. """ self.create_file(self.build_path(relpath), target, mode='a') return BuildFile(self.address_mapper._project_tree, relpath=self.build_path(relpath))
def test_raises_parse_error(self): self.add_to_build_file('BUILD', 'foo(name = = "baz")') build_file = BuildFile(FileSystemProjectTree(self.build_root), 'BUILD') with self.assertRaises(BuildFileParser.ParseError): self.build_file_parser.parse_build_file(build_file) # Test some corner cases for the context printing # Error at beginning of BUILD file build_file = self.add_to_build_file('begin/BUILD', dedent(""" *?&INVALID! = 'foo' target( name='bar', dependencies= [ ':baz', ], ) """)) with self.assertRaises(BuildFileParser.ParseError): self.build_file_parser.parse_build_file(build_file) # Error at end of BUILD file build_file = self.add_to_build_file('end/BUILD', dedent(""" target( name='bar', dependencies= [ ':baz', ], ) *?&INVALID! = 'foo' """)) with self.assertRaises(BuildFileParser.ParseError): self.build_file_parser.parse_build_file(build_file) # Error in the middle of BUILD file > 6 lines build_file = self.add_to_build_file('middle/BUILD', dedent(""" target( name='bar', *?&INVALID! = 'foo' dependencies = [ ':baz', ], ) """)) with self.assertRaises(BuildFileParser.ParseError): self.build_file_parser.parse_build_file(build_file) # Error in very short build file. build_file = self.add_to_build_file('short/BUILD', dedent(""" target(name='bar', dependencies = [':baz'],) *?&INVALID! = 'foo' """)) with self.assertRaises(BuildFileParser.ParseError): self.build_file_parser.parse_build_file(build_file)
def test_build_file_duplicates(self): # This workspace has two targets in the same file with the same name. self.add_to_build_file('BUILD', 'fake(name="foo")\n') self.add_to_build_file('BUILD', 'fake(name="foo")\n') def fake_target(*args, **kwargs): assert False, "This fake target should never be called in this test!" alias_map = {'target_aliases': {'fake': fake_target}} self.build_file_parser.register_alias_groups(alias_map=alias_map) with pytest.raises(BuildFileParser.TargetConflictException): base_build_file = BuildFile(self.build_root, 'BUILD') self.build_file_parser.parse_build_file(base_build_file)
def parse_addresses(self, spec): if spec.endswith('::'): spec_rel_dir = self._get_dir(spec[:-len('::')]) spec_dir = os.path.join(self._root_dir, spec_rel_dir) for build_file in BuildFile.scan_buildfiles( self._root_dir, spec_dir): self._build_file_parser.parse_build_file(build_file) for address in self._build_file_parser.addresses_by_build_file[ build_file]: yield address elif spec.endswith(':'): spec_rel_dir = self._get_dir(spec[:-len(':')]) spec_dir = os.path.join(self._root_dir, spec_rel_dir) for build_file in BuildFile(self._root_dir, spec_dir).family(): self._build_file_parser.parse_build_file(build_file) for address in self._build_file_parser.addresses_by_build_file[ build_file]: yield address else: spec_path, target_name = parse_spec(spec) build_file = BuildFile(self._root_dir, spec_path) yield BuildFileAddress(build_file, target_name)
def test_exposed_object(self): with self.workspace('BUILD') as root_dir: alias_map = {'exposed_objects': {'fake_object': object()}} self.build_file_parser.register_alias_groups(alias_map=alias_map) with open(os.path.join(root_dir, 'BUILD'), 'w') as build: build.write('''fake_object''') build_file = BuildFile(root_dir, 'BUILD') self.build_file_parser.parse_build_file(build_file) registered_proxies = set( self.build_file_parser._target_proxy_by_address.values()) self.assertEqual(len(registered_proxies), 0)
def test_register_exposed_object(self): self.build_configuration.register_exposed_object('jane', 42) aliases = self.build_configuration.registered_aliases() self.assertEqual({}, aliases.targets) self.assertEqual({}, aliases.context_aware_object_factories) self.assertEqual(dict(jane=42), aliases.objects) build_file = BuildFile('/tmp', 'jane', must_exist=False) parse_state = self.build_configuration.initialize_parse_state(build_file) self.assertEqual(0, len(parse_state.registered_addressable_instances)) self.assertEqual(1, len(parse_state.parse_globals)) self.assertEqual(42, parse_state.parse_globals['jane'])
def test_build_file_forms(self) -> None: with self.workspace("a/b/c/BUILD") as root_dir: build_file = BuildFile(FileSystemProjectTree(root_dir), relpath="a/b/c/BUILD") self.assert_address("a/b/c", "c", BuildFileAddress(build_file=build_file)) self.assert_address( "a/b/c", "foo", BuildFileAddress(build_file=build_file, target_name="foo")) self.assertEqual( "a/b/c:foo", BuildFileAddress(build_file=build_file, target_name="foo").spec) with self.workspace("BUILD") as root_dir: build_file = BuildFile(FileSystemProjectTree(root_dir), relpath="BUILD") self.assert_address( "", "foo", BuildFileAddress(build_file=build_file, target_name="foo")) self.assertEqual( "//:foo", BuildFileAddress(build_file=build_file, target_name="foo").spec)
def test_build_file_forms(self) -> None: with self.workspace('a/b/c/BUILD') as root_dir: build_file = BuildFile(FileSystemProjectTree(root_dir), relpath='a/b/c/BUILD') self.assert_address('a/b/c', 'c', BuildFileAddress(build_file=build_file)) self.assert_address( 'a/b/c', 'foo', BuildFileAddress(build_file=build_file, target_name='foo')) self.assertEqual( 'a/b/c:foo', BuildFileAddress(build_file=build_file, target_name='foo').spec) with self.workspace('BUILD') as root_dir: build_file = BuildFile(FileSystemProjectTree(root_dir), relpath='BUILD') self.assert_address( '', 'foo', BuildFileAddress(build_file=build_file, target_name='foo')) self.assertEqual( '//:foo', BuildFileAddress(build_file=build_file, target_name='foo').spec)
def test_target_creation(self): contents = dedent(''' create_java_libraries(base_name="create-java-libraries", provides_java_name="test-java", provides_scala_name="test-scala") make_lib("com.foo.test", "does_not_exists", "1.0") ''') self.create_file('3rdparty/BUILD', contents) alias_map = { 'target_aliases': { 'jar_library': JarLibrary, 'java_library': JavaLibrary, 'scala_library': ScalaLibrary }, 'target_creation_utils': { 'make_lib': make_lib, 'create_java_libraries': create_java_libraries }, 'exposed_objects': { 'artifact': Artifact, 'jar': JarDependency } } self.build_file_parser.register_alias_groups(alias_map=alias_map) build_file = BuildFile(self.build_root, '3rdparty/BUILD') self.build_file_parser.parse_build_file(build_file) registered_proxies = set( self.build_file_parser._target_proxy_by_address.values()) self.assertEqual(len(registered_proxies), 3) targets_created = {} for target_proxy in registered_proxies: targets_created.update( {target_proxy.name: target_proxy.target_type}) self.assertEquals( set([ 'does_not_exists', 'create-java-libraries-scala', 'create-java-libraries-java' ]), set(targets_created.keys())) self.assertEquals(targets_created['does_not_exists'], JarLibrary) self.assertEquals(targets_created['create-java-libraries-java'], JavaLibrary) self.assertEquals(targets_created['create-java-libraries-scala'], ScalaLibrary)
def __init__(self, run_tracker, root_dir, parser, args, build_file_parser, build_graph): """run_tracker: The (already opened) RunTracker to track this run with root_dir: The root directory of the pants workspace parser: an OptionParser args: the subcommand arguments to parse""" self.run_tracker = run_tracker self.root_dir = root_dir self.build_file_parser = build_file_parser self.build_graph = build_graph config = Config.load() with self.run_tracker.new_workunit(name='bootstrap', labels=[WorkUnit.SETUP]): # construct base parameters to be filled in for BuildGraph for path in config.getlist('goals', 'bootstrap_buildfiles', default=[]): # try: build_file = BuildFile(root_dir=self.root_dir, relpath=path) self.build_file_parser.parse_build_file_family(build_file) # except (TypeError, ImportError): # error(path, include_traceback=True) # except (IOError, SyntaxError): # error(path) # Now that we've parsed the bootstrap BUILD files, and know about the SCM system. self.run_tracker.run_info.add_scm_info() # Override the OptionParser's error with more useful output def error(message=None, show_help=True): if message: print(message + '\n') if show_help: parser.print_help() parser.exit(status=1) parser.error = error self.error = error self.setup_parser(parser, args) self.options, self.args = parser.parse_args(args) self.parser = parser
def do_test_exposed_context_aware_object(self, context_aware_object_factory): self.build_configuration.register_exposed_context_aware_object_factory( 'george', context_aware_object_factory) aliases = self.build_configuration.registered_aliases() self.assertEqual({}, aliases.targets) self.assertEqual({}, aliases.objects) self.assertEqual(dict(george=context_aware_object_factory), aliases.context_aware_object_factories) with temporary_dir() as root: build_file_path = os.path.join(root, 'george', 'BUILD') touch(build_file_path) build_file = BuildFile(root, 'george') parse_state = self.build_configuration.initialize_parse_state(build_file) self.assertEqual(0, len(parse_state.registered_addressable_instances)) self.assertEqual(1, len(parse_state.parse_globals)) yield parse_state.parse_globals['george']
def test_trivial_target(self): with self.workspace('BUILD') as root_dir: def fake_target(*args, **kwargs): assert False, "This fake target should never be called in this test!" alias_map = {'target_aliases': {'fake': fake_target}} self.build_file_parser.register_alias_groups(alias_map=alias_map) with open(os.path.join(root_dir, 'BUILD'), 'w') as build: build.write('''fake(name='foozle')''') build_file = BuildFile(root_dir, 'BUILD') self.build_file_parser.parse_build_file(build_file) registered_proxies = set( self.build_file_parser._target_proxy_by_address.values()) self.assertEqual(len(registered_proxies), 1) proxy = registered_proxies.pop() self.assertEqual(proxy.name, 'foozle') self.assertEqual(proxy.address, BuildFileAddress(build_file, 'foozle')) self.assertEqual(proxy.target_type, fake_target)
def test_path_relative_util(self): with self.workspace('a/b/c/BUILD') as root_dir: def path_relative_util(foozle, rel_path): self.assertEqual(rel_path, 'a/b/c') alias_map = { 'partial_path_relative_utils': { 'fake_util': path_relative_util } } self.build_file_parser.register_alias_groups(alias_map=alias_map) with open(os.path.join(root_dir, 'a/b/c/BUILD'), 'w') as build: build.write('''fake_util("baz")''') build_file = BuildFile(root_dir, 'a/b/c/BUILD') self.build_file_parser.parse_build_file(build_file) registered_proxies = set( self.build_file_parser._target_proxy_by_address.values()) self.assertEqual(len(registered_proxies), 0)
def to_url(m): if m.group(1): return m.group(0) # It's an http(s) url. path = m.group(0) if path.startswith('/'): path = os.path.relpath(path, buildroot) else: # See if it's a reference to a target in a BUILD file. parts = path.split(':') if len(parts) == 2: putative_dir = parts[0] else: putative_dir = path if os.path.isdir(os.path.join(buildroot, putative_dir)): build_file = BuildFile(buildroot, putative_dir, must_exist=False) path = build_file.relpath if os.path.exists(os.path.join(buildroot, path)): # The reporting server serves file content at /browse/<path_from_buildroot>. return '/browse/%s' % path else: return None
def do_test_exposed_context_aware_object(self, context_aware_object_factory): self._register_aliases(context_aware_object_factories={ 'george': context_aware_object_factory }) aliases = self.build_configuration.registered_aliases() self.assertEqual({}, aliases.target_types) self.assertEqual({}, aliases.target_macro_factories) self.assertEqual({}, aliases.objects) self.assertEqual(dict(george=context_aware_object_factory), aliases.context_aware_object_factories) with temporary_dir() as root: build_file_path = os.path.join(root, 'george', 'BUILD') touch(build_file_path) build_file = BuildFile(FileSystemProjectTree(root), 'george/BUILD') parse_state = self.build_configuration.initialize_parse_state( build_file) self.assertEqual(0, len(parse_state.objects)) self.assertEqual(1, len(parse_state.parse_globals)) yield parse_state.parse_globals['george']
def test_sibling_build_files_duplicates(self): # This workspace is malformed, you can't shadow a name in a sibling BUILD file with self.workspace('./BUILD', './BUILD.foo', './BUILD.bar') as root_dir: with open(os.path.join(root_dir, './BUILD'), 'w') as build: build.write( dedent(''' fake(name="base", dependencies=[ ':foo', ]) ''')) with open(os.path.join(root_dir, './BUILD.foo'), 'w') as build: build.write( dedent(''' fake(name="foo", dependencies=[ ':bat', ]) ''')) with open(os.path.join(root_dir, './BUILD.bar'), 'w') as build: build.write(dedent(''' fake(name="base") ''')) def fake_target(*args, **kwargs): assert False, "This fake target should never be called in this test!" alias_map = {'target_aliases': {'fake': fake_target}} self.build_file_parser.register_alias_groups(alias_map=alias_map) with pytest.raises(BuildFileParser.SiblingConflictException): base_build_file = BuildFile(root_dir, 'BUILD') bf_address = BuildFileAddress(base_build_file, 'base') self.build_file_parser._populate_target_proxy_transitive_closure_for_address( bf_address)