def test_find_by_path(self): # No source_root is registered yet query = "tests/foo/bar:baz" self.assertIsNone(SourceRoot.find_by_path(query), msg="Query {query} Failed for tree: {dump}" .format(query=query, dump=SourceRoot._dump())) SourceRoot.register("tests/foo", TestTarget) self.assertEquals("tests/foo", SourceRoot.find_by_path(query), msg="Query {query} Failed for tree: {dump}" .format(query=query, dump=SourceRoot._dump())) self.assertIsNone(SourceRoot.find_by_path("tests/bar/foobar:qux"), msg="Failed for tree: {dump}" .format(dump=SourceRoot._dump()))
def test_find_by_path(self): # No source_root is registered yet query = "tests/foo/bar:baz" self.assertIsNone(SourceRoot.find_by_path(query), msg="Query {query} Failed for tree: {dump}" .format(query=query, dump=SourceRoot._dump())) SourceRoot.register("tests/foo", TestTarget) self.assertEquals("tests/foo", SourceRoot.find_by_path(query), msg="Query {query} Failed for tree: {dump}" .format(query=query, dump=SourceRoot._dump())) self.assertIsNone(SourceRoot.find_by_path("tests/bar/foobar:qux"), msg="Failed for tree: {dump}" .format(dump=SourceRoot._dump()))
def _calculate_sources(self, targets): """ Find the appropriate source roots used for sources. :return: mapping of source roots to set of sources under the roots """ gentargets = OrderedSet() def add_to_gentargets(target): if self.is_gentarget(target): gentargets.add(target) self.context.build_graph.walk_transitive_dependency_graph( [target.address for target in targets], add_to_gentargets, postorder=True) sources_by_base = OrderedDict() # TODO(Eric Ayers) Extract this logic for general use? When using jar_source_set it is needed # to get the correct source root for paths outside the current BUILD tree. for target in gentargets: for source in target.sources_relative_to_buildroot(): base = SourceRoot.find_by_path(source) if not base: base, _ = target.target_base, target.sources_relative_to_buildroot() self.context.log.debug('Could not find source root for {source}.' ' Missing call to SourceRoot.register()? Fell back to {base}.' .format(source=source, base=base)) if base not in sources_by_base: sources_by_base[base] = OrderedSet() sources_by_base[base].add(source) return sources_by_base
def add_new_target(self, address, target_type, target_base=None, dependencies=None, derived_from=None, **kwargs): """Creates a new target, adds it to the context and returns it. This method ensures the target resolves files against the given target_base, creating the directory if needed and registering a source root. """ target_base = os.path.join(get_buildroot(), target_base or address.spec_path) if not os.path.exists(target_base): os.makedirs(target_base) if not SourceRoot.find_by_path(target_base): SourceRoot.register(target_base) if dependencies: dependencies = [dep.address for dep in dependencies] self.build_graph.inject_synthetic_target(address=address, target_type=target_type, dependencies=dependencies, derived_from=derived_from, **kwargs) new_target = self.build_graph.get_target(address) if derived_from: self._synthetic_targets[derived_from].append(new_target) return new_target
def _collapse_by_source_root(source_sets): """Collapse SourceSets with common source roots into one SourceSet instance. Use the registered source roots to collapse all source paths under a root. If any test type of target is allowed under the root, the path is determined to be a test path. This method will give unpredictable results if source root entries overlap. :param list source_sets: SourceSets to analyze :returns: list of SourceSets collapsed to the source root paths. There may be duplicate entries in this list which will be removed by dedup_sources() """ collapsed_source_sets = [] for source in source_sets: query = os.path.join(source.source_base, source.path) source_root = SourceRoot.find_by_path(query) if not source_root: collapsed_source_sets.append(source) else: collapsed_source_sets.append( SourceSet(source.root_dir, source_root, "", is_test=source.is_test, resources_only=source.resources_only)) return collapsed_source_sets
def _calculate_sources(self, targets): """ Find the appropriate source roots used for sources. :return: mapping of source roots to set of sources under the roots """ gentargets = OrderedSet() def add_to_gentargets(target): if self.is_gentarget(target): gentargets.add(target) self.context.build_graph.walk_transitive_dependency_graph( [target.address for target in targets], add_to_gentargets, postorder=True) sources_by_base = OrderedDict() # TODO(Eric Ayers) Extract this logic for general use? When using unpacked_jars it is needed # to get the correct source root for paths outside the current BUILD tree. for target in gentargets: for source in target.sources_relative_to_buildroot(): base = SourceRoot.find_by_path(source) if not base: base, _ = target.target_base, target.sources_relative_to_buildroot() self.context.log.debug('Could not find source root for {source}.' ' Missing call to SourceRoot.register()? Fell back to {base}.' .format(source=source, base=base)) if base not in sources_by_base: sources_by_base[base] = OrderedSet() sources_by_base[base].add(source) return sources_by_base
def add_new_target(self, address, target_type, target_base=None, dependencies=None, derived_from=None, **kwargs): """Creates a new target, adds it to the context and returns it. This method ensures the target resolves files against the given target_base, creating the directory if needed and registering a source root. """ target_base = os.path.join(get_buildroot(), target_base or address.spec_path) if not os.path.exists(target_base): os.makedirs(target_base) if not SourceRoot.find_by_path(target_base): SourceRoot.register(target_base) if dependencies: dependencies = [dep.address for dep in dependencies] self.build_graph.inject_synthetic_target(address=address, target_type=target_type, dependencies=dependencies, derived_from=derived_from, **kwargs) new_target = self.build_graph.get_target(address) if derived_from: self._synthetic_targets[derived_from].append(new_target) return new_target
def execute_codegen(self, targets): # Invoke the generator once per target. Because the wire compiler has flags that try to reduce # the amount of code emitted, Invoking them all together will break if one target specifies a # service_writer and another does not, or if one specifies roots and another does not. for target in targets: sources_by_base = self._calculate_sources([target]) if self.codegen_strategy.name() == 'isolated': sources = OrderedSet(target.sources_relative_to_buildroot()) else: sources = OrderedSet( itertools.chain.from_iterable(sources_by_base.values())) if not self.validate_sources_present(sources, [target]): continue relative_sources = OrderedSet() for source in sources: source_root = SourceRoot.find_by_path(source) if not source_root: source_root = SourceRoot.find(target) relative_source = os.path.relpath(source, source_root) relative_sources.add(relative_source) check_duplicate_conflicting_protos(self, sources_by_base, relative_sources, self.context.log) args = ['--java_out={0}'.format(self.codegen_workdir(target))] # Add all params in payload to args if target.payload.get_field_value('no_options'): args.append('--no_options') service_writer = target.payload.service_writer if service_writer: args.append('--service_writer={0}'.format(service_writer)) registry_class = target.payload.registry_class if registry_class: args.append('--registry_class={0}'.format(registry_class)) if target.payload.roots: args.append('--roots={0}'.format(','.join( target.payload.roots))) if target.payload.enum_options: args.append('--enum_options={0}'.format(','.join( target.payload.enum_options))) args.append('--proto_path={0}'.format( os.path.join(get_buildroot(), SourceRoot.find(target)))) args.extend(relative_sources) result = util.execute_java( classpath=self.tool_classpath('wire-compiler'), main='com.squareup.wire.WireCompiler', args=args) if result != 0: raise TaskError( 'Wire compiler exited non-zero ({0})'.format(result))
def collect_proto_paths(dep): if not dep.has_sources(): return for source in dep.sources_relative_to_buildroot(): if source.endswith('.proto'): root = SourceRoot.find_by_path(source) if root: proto_paths.add(os.path.join(get_buildroot(), root))
def test_register_none(self): self._assert_source_root_empty() SourceRoot.register("tests") self.assertEquals({"tests": OrderedSet()}, SourceRoot.all_roots()) self.assertEquals(OrderedSet(), SourceRoot.types("tests")) self.assertEquals("tests", SourceRoot.find(TestTarget("//tests/foo/bar:baz"))) self.assertEquals("tests", SourceRoot.find_by_path("tests/foo/bar"))
def collect_proto_paths(dep): if not dep.has_sources(): return for source in dep.sources_relative_to_buildroot(): if source.endswith(".proto"): root = SourceRoot.find_by_path(source) if root: proto_paths.add(os.path.join(get_buildroot(), root))
def test_register_none(self): self._assert_source_root_empty() SourceRoot.register("tests", ) self.assertEquals({"tests": OrderedSet()}, SourceRoot.all_roots()) self.assertEquals(OrderedSet(), SourceRoot.types("tests")) self.assertEquals("tests", SourceRoot.find(TestTarget("//tests/foo/bar:baz"))) self.assertEquals("tests", SourceRoot.find_by_path("tests/foo/bar"))
def execute_codegen(self, targets): # Invoke the generator once per target. Because the wire compiler has flags that try to reduce # the amount of code emitted, Invoking them all together will break if one target specifies a # service_writer and another does not, or if one specifies roots and another does not. for target in targets: sources_by_base = self._calculate_sources([target]) if self.codegen_strategy.name() == 'isolated': sources = OrderedSet(target.sources_relative_to_buildroot()) else: sources = OrderedSet(itertools.chain.from_iterable(sources_by_base.values())) if not self.validate_sources_present(sources, [target]): continue relative_sources = OrderedSet() for source in sources: source_root = SourceRoot.find_by_path(source) if not source_root: source_root = SourceRoot.find(target) relative_source = os.path.relpath(source, source_root) relative_sources.add(relative_source) check_duplicate_conflicting_protos(self, sources_by_base, relative_sources, self.context.log) args = ['--java_out={0}'.format(self.codegen_workdir(target))] # Add all params in payload to args if target.payload.get_field_value('no_options'): args.append('--no_options') service_writer = target.payload.service_writer if service_writer: args.append('--service_writer={0}'.format(service_writer)) registry_class = target.payload.registry_class if registry_class: args.append('--registry_class={0}'.format(registry_class)) if target.payload.roots: args.append('--roots={0}'.format(','.join(target.payload.roots))) if target.payload.enum_options: args.append('--enum_options={0}'.format(','.join(target.payload.enum_options))) args.append('--proto_path={0}'.format(os.path.join(get_buildroot(), SourceRoot.find(target)))) args.extend(relative_sources) result = util.execute_java(classpath=self.tool_classpath('wire-compiler'), main='com.squareup.wire.WireCompiler', args=args) if result != 0: raise TaskError('Wire compiler exited non-zero ({0})'.format(result))
def genlang(self, lang, targets): # Invoke the generator once per target. Because the wire compiler has flags that try to reduce # the amount of code emitted, Invoking them all together will break if one target specifies a # service_writer and another does not, or if one specifies roots and another does not. for target in targets: sources_by_base = self._calculate_sources([target]) sources = OrderedSet(itertools.chain.from_iterable(sources_by_base.values())) relative_sources = OrderedSet() for source in sources: source_root = SourceRoot.find_by_path(source) if not source_root: source_root = SourceRoot.find(target) relative_source = os.path.relpath(source, source_root) relative_sources.add(relative_source) check_duplicate_conflicting_protos(self, sources_by_base, relative_sources, self.context.log) if lang != "java": raise TaskError("Unrecognized wire gen lang: {0}".format(lang)) args = ["--java_out={0}".format(self.java_out)] # Add all params in payload to args if target.payload.get_field_value("no_options"): args.append("--no_options") service_writer = target.payload.service_writer if service_writer: args.append("--service_writer={0}".format(service_writer)) registry_class = target.payload.registry_class if registry_class: args.append("--registry_class={0}".format(registry_class)) for root in target.payload.roots: args.append("--roots={0}".format(root)) for enum_option in target.payload.enum_options: args.append("--enum_options={0}".format(enum_option)) args.append("--proto_path={0}".format(os.path.join(get_buildroot(), SourceRoot.find(target)))) args.extend(relative_sources) result = util.execute_java( classpath=self.tool_classpath("wire-compiler"), main="com.squareup.wire.WireCompiler", args=args ) if result != 0: raise TaskError("Wire compiler exited non-zero ({0})".format(result))
def _collapse_by_source_root(source_sets): """Collapse SourceSets with common source roots into one SourceSet instance. Use the registered source roots to collapse all source paths under a root. If any test type of target is allowed under the root, the path is determined to be a test path. This method will give unpredictable results if source root entries overlap. :param list source_sets: SourceSets to analyze :returns: list of SourceSets collapsed to the source root paths. There may be duplicate entries in this list which will be removed by dedup_sources() """ collapsed_source_sets = [] for source in source_sets: query = os.path.join(source.source_base, source.path) source_root = SourceRoot.find_by_path(query) if not source_root: collapsed_source_sets.append(source) else: collapsed_source_sets.append(SourceSet(source.root_dir, source_root, "", is_test=source.is_test, resources_only=source.resources_only)) return collapsed_source_sets
def _collapse_by_source_root(source_sets): """Collapse SourceSets with common source roots into one SourceSet instance. Use the registered source roots to collapse all source paths under a root. If any test type of target is allowed under the root, the path is determined to be a test path. This method will give unpredictable results if source root entries overlap. :param list source_sets: SourceSets to analyze :returns: list of SourceSets collapsed to the source root paths. """ roots_found = set() # remember the roots we've already encountered collapsed_source_sets = [] for source in source_sets: query = os.path.join(source.source_base, source.path) source_root = SourceRoot.find_by_path(query) if not source_root: collapsed_source_sets.append(source) elif not source_root in roots_found: roots_found.add(source_root) collapsed_source_sets.append(SourceSet(source.root_dir, source_root, "", source.is_test)) return collapsed_source_sets
def _collapse_by_source_root(source_sets): """Collapse SourceSets with common source roots into one SourceSet instance. Use the registered source roots to collapse all source paths under a root. If any test type of target is allowed under the root, the path is determined to be a test path. This method will give unpredictable results if source root entries overlap. :param list source_sets: SourceSets to analyze :returns: list of SourceSets collapsed to the source root paths. """ roots_found = set() # remember the roots we've already encountered collapsed_source_sets = [] for source in source_sets: query = os.path.join(source.source_base, source.path) source_root = SourceRoot.find_by_path(query) if not source_root: collapsed_source_sets.append(source) elif not source_root in roots_found: roots_found.add(source_root) collapsed_source_sets.append(SourceSet(source.root_dir, source_root, "", source.is_test)) return collapsed_source_sets
def format_args_for_target(self, target): """Calculate the arguments to pass to the command line for a single target.""" sources_by_base = self._calculate_sources([target]) if self.codegen_strategy.name() == "isolated": sources = OrderedSet(target.sources_relative_to_buildroot()) else: sources = OrderedSet(itertools.chain.from_iterable(sources_by_base.values())) if not self.validate_sources_present(sources, [target]): return None relative_sources = OrderedSet() for source in sources: source_root = SourceRoot.find_by_path(source) if not source_root: source_root = SourceRoot.find(target) relative_source = os.path.relpath(source, source_root) relative_sources.add(relative_source) check_duplicate_conflicting_protos(self, sources_by_base, relative_sources, self.context.log) args = ["--java_out={0}".format(self.codegen_workdir(target))] # Add all params in payload to args if target.payload.get_field_value("no_options"): args.append("--no_options") def append_service_opts(service_type_name, service_type_value, options_values): """Append --service_writer or --service_factory args as appropriate. :param str service_type_name: the target parameter/option prefix :param str service_type_value: class passed to the --service_x= option :param list options_values: string options to be passed with --service_x_opt """ if service_type_value: args.append("--{0}={1}".format(service_type_name, service_type_value)) if options_values: for opt in options_values: args.append("--{0}_opt".format(service_type_name)) args.append(opt) # A check is done in the java_wire_library target to make sure only one of --service_writer or # --service_factory is specified. if self.wire_compiler_version < Revision(2, 0): if target.payload.service_factory: raise TaskError( "{spec} used service_factory, which is not available before Wire 2.0. You " "should use service_writer instead.".format(spec=target.address.spec) ) append_service_opts("service_writer", target.payload.service_writer, target.payload.service_writer_options) else: if target.payload.service_writer: raise TaskError( "{spec} used service_writer, which is not available after Wire 2.0. You " "should use service_factory instead.".format(spec=target.address.spec) ) append_service_opts( "service_factory", target.payload.service_factory, target.payload.service_factory_options ) registry_class = target.payload.registry_class if registry_class: args.append("--registry_class={0}".format(registry_class)) if target.payload.roots: args.append("--roots={0}".format(",".join(target.payload.roots))) if target.payload.enum_options: args.append("--enum_options={0}".format(",".join(target.payload.enum_options))) if self.wire_compiler_version < Revision(2, 0): args.append("--proto_path={0}".format(os.path.join(get_buildroot(), SourceRoot.find(target)))) else: # NB(gmalmquist): Support for multiple --proto_paths was introduced in Wire 2.0. for path in self._calculate_proto_paths(target): args.append("--proto_path={0}".format(path)) args.extend(relative_sources) return args
def format_args_for_target(self, target): """Calculate the arguments to pass to the command line for a single target.""" sources_by_base = self._calculate_sources([target]) if self.codegen_strategy.name() == 'isolated': sources = OrderedSet(target.sources_relative_to_buildroot()) else: sources = OrderedSet( itertools.chain.from_iterable(sources_by_base.values())) if not self.validate_sources_present(sources, [target]): return None relative_sources = OrderedSet() for source in sources: source_root = SourceRoot.find_by_path(source) if not source_root: source_root = SourceRoot.find(target) relative_source = os.path.relpath(source, source_root) relative_sources.add(relative_source) check_duplicate_conflicting_protos(self, sources_by_base, relative_sources, self.context.log) args = ['--java_out={0}'.format(self.codegen_workdir(target))] # Add all params in payload to args if target.payload.get_field_value('no_options'): args.append('--no_options') def append_service_opts(service_type_name, service_type_value, options_values): """Append --service_writer or --service_factory args as appropriate. :param str service_type_name: the target parameter/option prefix :param str service_type_value: class passed to the --service_x= option :param list options_values: string options to be passed with --service_x_opt """ if service_type_value: args.append('--{0}={1}'.format(service_type_name, service_type_value)) if options_values: for opt in options_values: args.append('--{0}_opt'.format(service_type_name)) args.append(opt) # A check is done in the java_wire_library target to make sure only one of --service_writer or # --service_factory is specified. if self.wire_compiler_version < Revision(2, 0): if target.payload.service_factory: raise TaskError( '{spec} used service_factory, which is not available before Wire 2.0. You ' 'should use service_writer instead.'.format( spec=target.address.spec)) append_service_opts('service_writer', target.payload.service_writer, target.payload.service_writer_options) else: if target.payload.service_writer: raise TaskError( '{spec} used service_writer, which is not available after Wire 2.0. You ' 'should use service_factory instead.'.format( spec=target.address.spec)) append_service_opts('service_factory', target.payload.service_factory, target.payload.service_factory_options) registry_class = target.payload.registry_class if registry_class: args.append('--registry_class={0}'.format(registry_class)) if target.payload.roots: args.append('--roots={0}'.format(','.join(target.payload.roots))) if target.payload.enum_options: args.append('--enum_options={0}'.format(','.join( target.payload.enum_options))) if self.wire_compiler_version < Revision(2, 0): args.append('--proto_path={0}'.format( os.path.join(get_buildroot(), SourceRoot.find(target)))) else: # NB(gmalmquist): Support for multiple --proto_paths was introduced in Wire 2.0. for path in self._calculate_proto_paths(target): args.append('--proto_path={0}'.format(path)) args.extend(relative_sources) return args