def test_scan_addresses(self): root_build_file = self.add_to_build_file('BUILD', 'target(name="foo")') subdir_build_file = self.add_to_build_file('subdir/BUILD', 'target(name="bar")') subdir_suffix_build_file = self.add_to_build_file('subdir/BUILD.suffix', 'target(name="baz")') with open(os.path.join(self.build_root, 'BUILD.invalid.suffix'), 'w') as invalid_build_file: invalid_build_file.write('target(name="foobar")') self.assertEquals({BuildFileAddress(root_build_file, 'foo'), BuildFileAddress(subdir_build_file, 'bar'), BuildFileAddress(subdir_suffix_build_file, 'baz')}, self.address_mapper.scan_addresses(root=self.build_root))
def test_get_non_synthetic_scala_targets(self): # Create a custom context so we can manually inject multiple # targets of different source types and synthetic vs non-synthetic # to test the target filtering logic. context = self._create_context() # scala_library - should remain. scala_target_address = BuildFileAddress( self.add_to_build_file( 'a/scala/BUILD', 'scala_library(name="s", sources=["Source.scala"])'), 's') context.build_graph.inject_address_closure(scala_target_address) scala_target = context.build_graph.get_target(scala_target_address) # scala_library but with java sources - should be filtered scala_target_java_source_address = BuildFileAddress( self.add_to_build_file( 'a/scala_java/BUILD', 'scala_library(name="sj", sources=["Source.java"])'), 'sj') context.build_graph.inject_address_closure( scala_target_java_source_address) scala_target_with_java_source = context.build_graph.get_target( scala_target_java_source_address) # java_library - should be filtered java_target_address = BuildFileAddress( self.add_to_build_file( 'a/java/BUILD', 'java_library(name="j", sources=["Source.java"])'), 'j') context.build_graph.inject_address_closure(java_target_address) java_target = context.build_graph.get_target(java_target_address) # synthetic scala_library - should be filtered synthetic_scala_target = self.make_target('a/synthetic_scala:ss', ScalaLibrary) # add all these targets to context. context.replace_targets([ java_target, scala_target, scala_target_with_java_source, synthetic_scala_target ]) # scala_library would bring in 'scala-library-2.9.3 defined in BUILD.tools # so we have an extra target here. self.assertEqual(5, len(context.targets())) # Now create the task and run the non_synthetic scala-only filtering. task = self._create_scalastyle_task_from_context(context) result_targets = task._get_non_synthetic_scala_targets( context.targets()) # Only the scala target should remain self.assertEquals(1, len(result_targets)) self.assertEqual(scala_target, result_targets[0])
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_get_non_excluded_scala_sources(self): # Create a custom context so we can manually inject scala targets # with mixed sources in them to test the source filtering logic. context = self._create_context(config=dedent(''' [scalastyle] config: {config} excludes: {excludes} '''.format(config=self._create_scalastyle_config_file(), excludes=self._create_scalastyle_excludes_file( ['a/scala_2/Source2.scala'])))) # this scala target has mixed *.scala and *.java sources. # the *.java source should filtered out. scala_target_address_1 = BuildFileAddress( self.add_to_build_file( 'a/scala_1/BUILD', 'scala_library(name="s1", sources=["Source1.java", "Source1.scala"])' ), 's1') self.build_graph.inject_address_closure(scala_target_address_1) scala_target_1 = self.build_graph.get_target(scala_target_address_1) # this scala target has single *.scala source but will be excluded out # by the [scalastyle].[excludes] setting. scala_target_address_2 = BuildFileAddress( self.add_to_build_file( 'a/scala_2/BUILD', 'scala_library(name="s2", sources=["Source2.scala"])'), 's2') self.build_graph.inject_address_closure(scala_target_address_2) scala_target_2 = self.build_graph.get_target(scala_target_address_2) context = self._create_context( config=dedent(''' [scalastyle] config: {config} excludes: {excludes} '''.format(config=self._create_scalastyle_config_file(), excludes=self._create_scalastyle_excludes_file( ['a/scala_2/Source2.scala']))), target_roots=[scala_target_1, scala_target_2]) # Remember, we have the extra 'scala-library-2.9.3' dep target. self.assertEqual(3, len(context.targets())) # Now create the task and run the scala source and exclusion filtering. task = self._create_scalastyle_task_from_context(context) result_sources = task._get_non_excluded_scala_sources( task._get_non_synthetic_scala_targets(context.targets())) # Only the scala source from target 1 should remain self.assertEquals(1, len(result_sources)) self.assertEqual('a/scala_1/Source1.scala', result_sources[0])
def __init__(self, name, build_file, build_file_source_lines, target_source_lines, target_interval, dependencies, dependencies_interval): """See BuildFileManipulator.load() for how to construct one as a user.""" self.name = name self.build_file = build_file self.target_address = BuildFileAddress(build_file, name) self._build_file_source_lines = build_file_source_lines self._target_source_lines = target_source_lines self._target_interval = target_interval self._dependencies_interval = dependencies_interval self._dependencies_by_address = {} for dep in dependencies: dep_address = SyntheticAddress.parse(dep.spec, relative_to=build_file.spec_path) if dep_address in self._dependencies_by_address: raise BuildTargetParseError('The address {dep_address} occurred multiple times in the ' 'dependency specs for target {name} in {build_file}. ' .format(dep_address=dep_address.spec, name=name, build_file=build_file)) self._dependencies_by_address[dep_address] = dep
def test_derived_from_chain(self): context = self.context() # add concrete target build_file = self.add_to_build_file( 'y/BUILD', dedent(''' java_library( name='concrete', sources=['SourceA.scala'], ) ''')) concrete_address = BuildFileAddress(build_file, 'concrete') context.build_graph.inject_address_closure(concrete_address) concrete = context.build_graph.get_target(concrete_address) # add synthetic targets syn_one = context.add_new_target(SyntheticAddress('y', 'syn_one'), JavaLibrary, derived_from=concrete, sources=["SourceB.scala"]) syn_two = context.add_new_target(SyntheticAddress('y', 'syn_two'), JavaLibrary, derived_from=syn_one, sources=["SourceC.scala"]) # validate self.assertEquals(list(syn_two.derived_from_chain), [syn_one, concrete]) self.assertEquals(list(syn_one.derived_from_chain), [concrete]) self.assertEquals(list(concrete.derived_from_chain), [])
def test_scan_addresses_with_excludes(self): root_build_file = self.add_to_build_file('BUILD', 'target(name="foo")') self.add_to_build_file('subdir/BUILD', 'target(name="bar")') spec_excludes = [os.path.join(self.build_root, 'subdir')] self.assertEquals({BuildFileAddress(root_build_file, 'foo')}, self.address_mapper.scan_addresses(root=self.build_root, spec_excludes=spec_excludes))
def test_fail(self): # Default scalastyle config (import grouping rule) and no excludes. # Create a scala source that would FAIL ImportGroupingChecker rule. self.create_file(relpath='a/scala/fail.scala', contents=dedent(''' import java.io._ object HelloWorld { def main(args: Array[String]) { println("Hello, world!") } } import java.util._ ''')) scala_target_address = BuildFileAddress( self.add_to_build_file( 'a/scala/BUILD', 'scala_library(name="fail", sources=["fail.scala"])'), 'fail') self.build_graph.inject_address_closure(scala_target_address) scala_target = self.build_graph.get_target(scala_target_address) context = self._create_context(target_roots=[scala_target]) with self.assertRaises(TaskError): self.execute(context)
def test_end_to_end_pass(self): # Default scalastyle config (import grouping rule) and no excludes. # Create a scala source that would PASS ImportGroupingChecker rule. self.create_file(relpath='a/scala/pass.scala', contents=dedent(""" import java.util object HelloWorld { def main(args: Array[String]) { println("Hello, world!") } } """)) scala_target_address = BuildFileAddress( self.add_to_build_file( 'a/scala/BUILD', 'scala_library(name="pass", sources=["pass.scala"])'), 'pass') self.build_graph.inject_address_closure(scala_target_address) scala_target = self.build_graph.get_target(scala_target_address) context = self._create_context( scalastyle_config=self._create_scalastyle_config_file(), target_roots=[scala_target]) self.execute(context)
def _resolve_and_inject(self, dependent_remote_lib, dependency_import_id): """Resolves dependency_import_id's BUILD file and injects it into the build graph. :param GoRemoteLibrary dependent_remote_lib: Injects the resolved target of dependency_import_id as a dependency of this remote library. :param str dependency_import_id: Global import id of the remote library whose BUILD file to look up. :return GoRemoteLibrary: Returns the resulting resolved remote library after injecting it in the build graph. If the resulting library has already been resolved/injected, returns None. :raises UndeclaredRemoteLibError: If no BUILD file exists for dependency_import_id under the same source root of dependent_remote_lib, raises exception. """ remote_source_root = dependent_remote_lib.target_base spec_path = os.path.join(remote_source_root, dependency_import_id) try: build_file = FilesystemBuildFile(get_buildroot(), relpath=spec_path) except FilesystemBuildFile.MissingBuildFileError: raise self.UndeclaredRemoteLibError(spec_path) address = BuildFileAddress(build_file) self.context.build_graph.inject_address_closure(address) self.context.build_graph.inject_dependency( dependent_remote_lib.address, address) return self.context.build_graph.get_target(address)
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 __call__(self, *args, **kwargs): addressable = self._addressable_type(*args, **kwargs) addressable_name = addressable.addressable_name if addressable_name: address = BuildFileAddress(self._build_file, addressable_name) self._registration_callback(address, addressable) return addressable
def dep_address_iter(): for dep_spec in self.dependencies: dep_spec_path, dep_target_name = parse_spec( dep_spec, relative_to=self.build_file.spec_path) dep_build_file = BuildFileCache.spec_path_to_build_file( self.build_file.root_dir, dep_spec_path) dep_address = BuildFileAddress(dep_build_file, dep_target_name) yield dep_address
def test_empty_libraries(self): build_file = self.add_to_build_file( 'BUILD', dedent(''' unpacked_jars(name='foo', )''')) with self.assertRaises(UnpackedJars.ExpectedLibrariesError): self.build_graph.inject_address_closure( BuildFileAddress(build_file, 'foo'))
def test_bad_main_declaration(self): build_file = self.add_to_build_file('BUILD', dedent(''' jvm_binary(name='bar', main=['com.example.Bar'], ) ''')) with self.assertRaisesRegexp(TargetDefinitionException, r'Invalid target JvmBinary.*bar.*main must be a fully'): self.build_graph.inject_address_closure(BuildFileAddress(build_file, 'bar'))
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 test_bad_source_declaration(self): build_file = self.add_to_build_file('BUILD', dedent(''' jvm_binary(name='foo', main='com.example.Foo', source=['foo.py'], ) ''')) with self.assertRaisesRegexp(TargetDefinitionException, r'Invalid target JvmBinary.*foo.*source must be a single'): self.build_graph.inject_address_closure(BuildFileAddress(build_file, 'foo'))
def test_trivial_target(self): self.add_to_build_file('BUILD', 'fake(name="foozle")') build_file = FilesystemBuildFile(self.build_root, 'BUILD') address_map = self.build_file_parser.parse_build_file(build_file) self.assertEqual(len(address_map), 1) address, proxy = address_map.popitem() self.assertEqual(address, BuildFileAddress(build_file, 'foozle')) self.assertEqual(proxy.addressed_name, 'foozle') self.assertEqual(proxy.addressed_type, ErrorTarget)
def _parse_spec(self, spec): def normalize_spec_path(path): is_abs = not path.startswith('//') and os.path.isabs(path) if is_abs: path = os.path.realpath(path) if os.path.commonprefix([self._root_dir, path ]) != self._root_dir: raise self.BadSpecError( 'Absolute address path {0} does not share build root {1}' .format(path, self._root_dir)) else: if path.startswith('//'): path = path[2:] path = os.path.join(self._root_dir, path) normalized = os.path.relpath(path, self._root_dir) if normalized == '.': normalized = '' return normalized if spec.endswith('::'): addresses = set() spec_path = spec[:-len('::')] spec_dir = normalize_spec_path(spec_path) if not os.path.isdir(os.path.join(self._root_dir, spec_dir)): raise self.BadSpecError( 'Can only recursive glob directories and {0} is not a valid dir' .format(spec_dir)) try: for build_file in BuildFile.scan_buildfiles( self._root_dir, spec_dir): addresses.update( self._address_mapper.addresses_in_spec_path( build_file.spec_path)) return addresses except (BuildFile.BuildFileError, AddressLookupError) as e: raise self.BadSpecError(e) elif spec.endswith(':'): spec_path = spec[:-len(':')] spec_dir = normalize_spec_path(spec_path) try: return set( self._address_mapper.addresses_in_spec_path(spec_dir)) except AddressLookupError as e: raise self.BadSpecError(e) else: spec_parts = spec.rsplit(':', 1) spec_parts[0] = normalize_spec_path(spec_parts[0]) spec_path, target_name = parse_spec(':'.join(spec_parts)) try: build_file = BuildFile.from_cache(self._root_dir, spec_path) return set([BuildFileAddress(build_file, target_name)]) except BuildFile.BuildFileError as e: raise self.BadSpecError(e)
def test_resolve(self): build_file = self.add_to_build_file( 'BUILD', dedent(''' target( name = 'foo' ) ''')) address = BuildFileAddress(build_file, 'foo') addressable = self.address_mapper.resolve(address) self.assertEquals(address.target_name, addressable.addressable_name) self.assertEqual(addressable.target_type, Dependencies)
def test_bad_jar_rules(self): build_file = self.add_to_build_file('BUILD', dedent(''' jvm_binary(name='foo', main='com.example.Foo', deploy_jar_rules='invalid', ) ''')) with self.assertRaisesRegexp(TargetDefinitionException, r'Invalid target JvmBinary.*foo.*' r'deploy_jar_rules must be a JarRules specification. got str'): self.build_graph.inject_address_closure(BuildFileAddress(build_file, 'foo'))
def _inject_spec_closure_into_build_graph(self, spec_path, target_name, build_graph, addresses_already_closed=None): addresses_already_closed = addresses_already_closed or set() build_file = BuildFileCache.spec_path_to_build_file( self._root_dir, spec_path) address = BuildFileAddress(build_file, target_name) self.inject_address_closure_into_build_graph(address, build_graph, addresses_already_closed)
def spec_to_address(self, spec, relative_to=''): """A helper method for mapping a spec to the correct BuildFileAddress. :param spec: a spec to lookup in the map. :raises AddressLookupError: if the BUILD file cannot be found in the path specified by the spec :returns a new BuildFileAddress instanace """ spec_path, name = parse_spec(spec, relative_to=relative_to) try: build_file = BuildFile.from_cache(self.root_dir, spec_path) except BuildFile.BuildFileError as e: raise self.InvalidBuildFileReference('{message}\n when translating spec {spec}' .format(message=e, spec=spec)) return BuildFileAddress(build_file, name)
def test_raises_address_not_in_build_file(self): build_file = self.add_to_build_file('BUILD', dedent( ''' target( name = 'foo' ) ''' )) # Create an address that doesn't exist in an existing BUILD file address = BuildFileAddress(build_file, 'bar') with self.assertRaises(BuildFileAddressMapper.AddressNotInBuildFile): self.address_mapper.resolve(address)
def test_bad_basename(self): build_file = self.add_to_build_file( 'BUILD', dedent(''' jvm_app(name='foo', basename='foo', ) ''')) with self.assertRaisesRegexp( TargetDefinitionException, r'Invalid target JvmApp.* foo.*basename must not equal name.'): self.build_graph.inject_address_closure( BuildFileAddress(build_file, 'foo'))
def test_empty_traversable_properties(self): build_file = self.add_to_build_file( 'BUILD', dedent(''' java_library( name='foo', sources=["foo.java"], ) ''')) self.build_graph.inject_address_closure( BuildFileAddress(build_file, 'foo')) target = self.build_graph.get_target(SyntheticAddress.parse('//:foo')) self.assertSequenceEqual([], list(target.traversable_specs)) self.assertSequenceEqual([], list(target.traversable_dependency_specs))
def test_traversable_dependency_specs(self): build_file = self.add_to_build_file('BUILD', dedent(''' jvm_target(name='foo', resources=[':resource_target'], ) resources(name='resource_target', sources=['foo.txt'], ) ''')) self.build_graph.inject_address_closure(BuildFileAddress(build_file, 'foo')) target = self.build_graph.get_target(SyntheticAddress.parse('//:foo')) self.assertSequenceEqual([], list(target.traversable_specs)) self.assertSequenceEqual([':resource_target'], list(target.traversable_dependency_specs))
def __init__(self, target_type, build_file, args, kwargs): if 'name' not in kwargs: raise ValueError( 'name is a required parameter to all Target objects' ' specified within a BUILD file.' ' Target type was: {target_type}.' ' Current BUILD file is: {build_file}.'.format( target_type=target_type, build_file=build_file)) if args: raise ValueError( 'All arguments passed to Targets within BUILD files should' ' use explicit keyword syntax.' ' Target type was: {target_type}.' ' Current BUILD file is: {build_file}.' ' Arguments passed were: {args}'.format( target_type=target_type, build_file=build_file, args=args)) if 'build_file' in kwargs: raise ValueError( 'build_file cannot be passed as an explicit argument to a' ' target within a BUILD file.' ' Target type was: {target_type}.' ' Current BUILD file is: {build_file}.' ' build_file argument passed was: {build_file_arg}'.format( target_type=target_type, build_file=build_file, build_file_arg=kwargs.get('build_file'))) self.target_type = target_type self.build_file = build_file self.kwargs = kwargs self.name = kwargs['name'] self.address = BuildFileAddress(build_file, self.name) self.description = None self.dependencies = self.kwargs.pop('dependencies', []) self._dependency_addresses = None for dep_spec in self.dependencies: if not isinstance(dep_spec, Compatibility.string): msg = ( 'dependencies passed to Target constructors must be strings. {dep_spec} is not' ' a string. Target type was: {target_type}. Current BUILD file is: {build_file}.' .format(target_type=target_type, build_file=build_file, dep_spec=dep_spec)) raise TargetDefinitionException(target=self, msg=msg)
def _add_java_target_for_test(self, context, name, test_java_source): rel_dir = os.path.join('src/java', name) self.create_file( relpath=os.path.join(rel_dir, '{name}.java'.format(name=name)), contents=test_java_source) java_target_address = BuildFileAddress( self.add_to_build_file( os.path.join(rel_dir, 'BUILD'), 'java_library(name="{name}", sources=["{name}.java"])'.format(name=name)), '{name}'.format(name=name)) context.build_graph.inject_address_closure(java_target_address) java_target = context.build_graph.get_target(java_target_address) new_target_roots = [java_target] new_target_roots.extend(context.target_roots if context.target_roots else []) context.replace_targets(new_target_roots)
def test_illegal_kwargs(self): with self.assertRaises(Target.UnknownArguments) as cm: context = self.context() build_file = self.add_to_build_file( 'foo/BUILD', dedent(''' java_library( name='bar', sources=[], foobar='barfoo', ) ''')) address = BuildFileAddress(build_file, 'bar') context.build_graph.inject_address_closure(address) context.build_graph.get_target(address) self.assertTrue('foobar = barfoo' in str(cm.exception)) self.assertTrue('foo:bar' in str(cm.exception))