diff --git a/src/python/pants/backend/jvm/subsystems/BUILD b/src/python/pants/backend/jvm/subsystems/BUILD index d5775aca860..6f49b860622 100644 --- a/src/python/pants/backend/jvm/subsystems/BUILD +++ b/src/python/pants/backend/jvm/subsystems/BUILD @@ -149,6 +149,7 @@ python_library( sources = ['zinc_language_mixin.py'], dependencies = [ '3rdparty/python:future', + 'src/python/pants/build_graph', 'src/python/pants/subsystem', ] ) diff --git a/src/python/pants/backend/jvm/subsystems/dependency_context.py b/src/python/pants/backend/jvm/subsystems/dependency_context.py index 5ee8c221589..0903bce34f1 100644 --- a/src/python/pants/backend/jvm/subsystems/dependency_context.py +++ b/src/python/pants/backend/jvm/subsystems/dependency_context.py @@ -50,7 +50,7 @@ def all_dependencies(self, target): def create_fingerprint_strategy(self, classpath_products): return ResolvedJarAwareFingerprintStrategy(classpath_products, self) - def defaulted_property(self, target, selector): + def defaulted_property(self, target, option_name): """Computes a language property setting for the given JvmTarget. :param selector A function that takes a target or platform and returns the boolean value of the @@ -60,19 +60,17 @@ def defaulted_property(self, target, selector): If the target does not override the language property, returns true iff the property is true for any of the matched languages for the target. """ - target_property_selected = selector(target) - if target_property_selected is not None: - return target_property_selected - - prop = None if target.has_sources('.java'): - prop = prop or selector(Java.global_instance()) - if target.has_sources('.scala'): - prop = prop or selector(ScalaPlatform.global_instance()) - return prop + matching_subsystem = Java.global_instance() + elif target.has_sources('.scala'): + matching_subsystem = ScalaPlatform.global_instance() + else: + return getattr(target, option_name) + + return matching_subsystem.get_scalar_mirrored_target_option(option_name, target) def dependencies_respecting_strict_deps(self, target): - if self.defaulted_property(target, lambda x: x.strict_deps): + if self.defaulted_property(target, 'strict_deps'): dependencies = target.strict_dependencies(self) else: dependencies = self.all_dependencies(target) @@ -107,7 +105,7 @@ def compute_fingerprint(self, target): return hasher.hexdigest() if PY3 else hasher.hexdigest().decode('utf-8') def direct(self, target): - return self._dep_context.defaulted_property(target, lambda x: x.strict_deps) + return self._dep_context.defaulted_property(target, 'strict_deps') def dependencies(self, target): if self.direct(target): diff --git a/src/python/pants/backend/jvm/subsystems/zinc_language_mixin.py b/src/python/pants/backend/jvm/subsystems/zinc_language_mixin.py index 229b2c1ffb1..c9ca00940b3 100644 --- a/src/python/pants/backend/jvm/subsystems/zinc_language_mixin.py +++ b/src/python/pants/backend/jvm/subsystems/zinc_language_mixin.py @@ -4,12 +4,18 @@ from __future__ import absolute_import, division, print_function, unicode_literals -from builtins import object +from pants.build_graph.mirrored_target_option_mixin import MirroredTargetOptionMixin -class ZincLanguageMixin(object): +class ZincLanguageMixin(MirroredTargetOptionMixin): """A mixin for subsystems for languages compiled with Zinc.""" + mirrored_target_option_actions = { + 'strict_deps': lambda tgt: tgt.strict_deps, + 'compiler_option_sets': lambda tgt: tgt.compiler_option_sets, + 'zinc_file_manager': lambda tgt: tgt.zinc_file_manager, + } + @classmethod def register_options(cls, register): super(ZincLanguageMixin, cls).register_options(register) diff --git a/src/python/pants/backend/jvm/tasks/jvm_compile/jvm_compile.py b/src/python/pants/backend/jvm/tasks/jvm_compile/jvm_compile.py index da4fb97f65c..bc4d91332c1 100644 --- a/src/python/pants/backend/jvm/tasks/jvm_compile/jvm_compile.py +++ b/src/python/pants/backend/jvm/tasks/jvm_compile/jvm_compile.py @@ -908,8 +908,8 @@ def _default_work_for_vts(self, vts, ctx, input_classpath_product_key, counter, dep_context = DependencyContext.global_instance() tgt, = vts.targets - compiler_option_sets = dep_context.defaulted_property(tgt, lambda x: x.compiler_option_sets) - zinc_file_manager = dep_context.defaulted_property(tgt, lambda x: x.zinc_file_manager) + compiler_option_sets = dep_context.defaulted_property(tgt, 'compiler_option_sets') + zinc_file_manager = dep_context.defaulted_property(tgt, 'zinc_file_manager') with Timer() as timer: directory_digest = self._compile_vts(vts, ctx, diff --git a/src/python/pants/backend/jvm/tasks/jvm_compile/rsc/rsc_compile.py b/src/python/pants/backend/jvm/tasks/jvm_compile/rsc/rsc_compile.py index c4efed2b474..f5daa11f590 100644 --- a/src/python/pants/backend/jvm/tasks/jvm_compile/rsc/rsc_compile.py +++ b/src/python/pants/backend/jvm/tasks/jvm_compile/rsc/rsc_compile.py @@ -14,9 +14,7 @@ from pants.backend.jvm.subsystems.dependency_context import DependencyContext # noqa from pants.backend.jvm.subsystems.shader import Shader -from pants.backend.jvm.targets.junit_tests import JUnitTests from pants.backend.jvm.targets.jvm_target import JvmTarget -from pants.backend.jvm.targets.scala_library import ScalaLibrary from pants.backend.jvm.tasks.classpath_entry import ClasspathEntry from pants.backend.jvm.tasks.jvm_compile.compile_context import CompileContext from pants.backend.jvm.tasks.jvm_compile.execution_graph import Job @@ -24,11 +22,13 @@ from pants.base.build_environment import get_buildroot from pants.base.exceptions import TaskError from pants.base.workunit import WorkUnitLabel +from pants.build_graph.mirrored_target_option_mixin import MirroredTargetOptionMixin from pants.engine.fs import (EMPTY_DIRECTORY_DIGEST, Digest, DirectoryToMaterialize, PathGlobs, PathGlobsAndRoot) from pants.engine.isolated_process import ExecuteProcessRequest, FallibleExecuteProcessResult from pants.java.jar.jar_dependency import JarDependency from pants.reporting.reporting_utils import items_to_report_element +from pants.util.collections import assert_single_element from pants.util.contextutil import Timer from pants.util.dirutil import fast_relpath, fast_relpath_optional, safe_mkdir from pants.util.memo import memoized_method, memoized_property @@ -89,50 +89,6 @@ def add_for_target(self, *args, **kwargs): product.add_for_target(*args, **kwargs) -class _CompileWorkflowChoice(enum(['zinc-only', 'guess'])): - """Enum covering the values for the default workflow option. - - guess - Try to classify compile targets based on whether they are Scala/Java or test targets. - zinc-only - Always use zinc.""" - - -class _JvmCompileWorkflowType(enum(['zinc-only', 'rsc-then-zinc'])): - """Target classifications used to correctly schedule Zinc and Rsc jobs. - - There are some limitations we have to work around before we can compile everything through Rsc - and followed by Zinc. - - rsc is not able to outline all scala code just yet (this is also being addressed through - automated rewrites). - - javac is unable to consume rsc's jars just yet. - - rsc is not able to outline all java code just yet (this is likely to *not* require rewrites, - just some more work on rsc). - - As we work on improving our Rsc integration, we'll need to create more workflows to more closely - map the supported features of Rsc. This enum class allows us to do that. - - - zinc-only: compiles targets just with Zinc and uses the Zinc products of their dependencies. - - rsc-then-zinc: compiles targets with Rsc to create "header" jars, then runs Zinc against the - Rsc products of their dependencies. The Rsc compile uses the Rsc products of Rsc compatible - targets and the Zinc products of zinc-only targets. - """ - - @classmethod - def classify_target(cls, target): - # NB: Java targets, Scala+Java targets, and some test targets are not currently supported for - # compile with Rsc. - # TODO: Rsc's produced header jars don't yet work with javac. Once this is resolved, we may add - # additional workflow types. - if target.has_sources('.java') or \ - isinstance(target, JUnitTests) or \ - (isinstance(target, ScalaLibrary) and tuple(target.java_sources)): - target_type = _JvmCompileWorkflowType.zinc_only - elif target.has_sources('.scala'): - target_type = _JvmCompileWorkflowType.rsc_then_zinc - else: - target_type = None - return target_type - - class RscCompileContext(CompileContext): def __init__(self, target, @@ -153,26 +109,50 @@ def ensure_output_dirs_exist(self): safe_mkdir(os.path.dirname(self.rsc_jar_file)) -class RscCompile(ZincCompile): +class RscCompile(ZincCompile, MirroredTargetOptionMixin): """Compile Scala and Java code to classfiles using Rsc.""" _name = 'mixed' # noqa compiler_name = 'rsc' + @memoized_property + def mirrored_target_option_actions(self): + return { + 'workflow': self._identify_workflow_tags, + } + @classmethod def implementation_version(cls): return super(RscCompile, cls).implementation_version() + [('RscCompile', 172)] - def __init__(self, *args, **kwargs): - super(RscCompile, self).__init__(*args, **kwargs) + class JvmCompileWorkflowType(enum(['zinc-only', 'rsc-then-zinc'])): + """Target classifications used to correctly schedule Zinc and Rsc jobs. + + There are some limitations we have to work around before we can compile everything through Rsc + and followed by Zinc. + - rsc is not able to outline all scala code just yet (this is also being addressed through + automated rewrites). + - javac is unable to consume rsc's jars just yet. + - rsc is not able to outline all java code just yet (this is likely to *not* require rewrites, + just some more work on rsc). + + As we work on improving our Rsc integration, we'll need to create more workflows to more closely + map the supported features of Rsc. This enum class allows us to do that. - self._rsc_compat_tag = self.get_options().force_compiler_tag_prefix - self._compiler_tags = {'{}:{}'.format(self._rsc_compat_tag, workflow.value): workflow - for workflow in _JvmCompileWorkflowType.all_variants} - self._default_workflow = self.get_options().default_workflow + - zinc-only: compiles targets just with Zinc and uses the Zinc products of their dependencies. + - rsc-then-zinc: compiles targets with Rsc to create "header" jars, then runs Zinc against the + Rsc products of their dependencies. The Rsc compile uses the Rsc products of Rsc compatible + targets and the Zinc products of zinc-only targets. + """ - # If the default workflow is conveyed through a flag, override tag values. - self._ignore_tags_when_calculating_workflow = self.get_options().is_flagged('default_workflow') + @memoized_property + def _compiler_tags(self): + return { + '{prefix}:{workflow_name}'.format( + prefix=self.get_options().force_compiler_tag_prefix, + workflow_name=workflow.value): workflow + for workflow in self.JvmCompileWorkflowType.all_variants + } @classmethod def register_options(cls, register): @@ -180,9 +160,9 @@ def register_options(cls, register): register('--force-compiler-tag-prefix', default='use-compiler', metavar='', help='Always compile targets marked with this tag with rsc, unless the workflow is ' 'specified on the cli.') - register('--default-workflow', type=_CompileWorkflowChoice, - default=_CompileWorkflowChoice.guess, metavar='', - help='Defines how a compile workflow is chosen when a tag is not present.') + register('--workflow', type=cls.JvmCompileWorkflowType, + default=cls.JvmCompileWorkflowType.zinc_only, metavar='', + help='The workflow to use to compile JVM targets.') cls.register_jvm_tool( register, @@ -278,22 +258,26 @@ def create_empty_extra_products(self): def select(self, target): if not isinstance(target, JvmTarget): return False - return self._classify_compile_target(target) is not None + return self._classify_target_compile_workflow(target) is not None - def _classify_compile_target(self, target): - if not target.has_sources('.java') and not target.has_sources('.scala'): + @memoized_method + def _identify_workflow_tags(self, target): + try: + all_tags = [self._compiler_tags.get(tag) for tag in target.tags] + filtered_tags = filter(None, all_tags) + return assert_single_element(list(filtered_tags)) + except StopIteration: return None + except ValueError as e: + raise ValueError('Multiple compile workflow tags specified for target {}: {}' + .format(target, e)) - if not self._ignore_tags_when_calculating_workflow: - for t in target.tags: - flow = self._compiler_tags.get(t) - if flow: - return _JvmCompileWorkflowType(flow) - - return self._default_workflow.resolve_for_enum_variant({ - 'zinc-only': _JvmCompileWorkflowType.zinc_only, - 'guess': _JvmCompileWorkflowType.classify_target(target) - }) + @memoized_method + def _classify_target_compile_workflow(self, target): + """Return the compile workflow to use for this target.""" + if target.has_sources('.java') or target.has_sources('.scala'): + return self.get_scalar_mirrored_target_option('workflow', target) + return None def _key_for_target_as_dep(self, target, workflow): # used for jobs that are either rsc jobs or zinc jobs run against rsc @@ -489,6 +473,7 @@ def make_zinc_job(target, input_product_key, output_products, dep_keys): output_products=[ runtime_classpath_product, ], + # TODO: remove this dep and fix tests!!! dep_keys=[ # TODO we could remove the dependency on the rsc target in favor of bumping # the cache separately. We would need to bring that dependency back for @@ -526,7 +511,7 @@ def create_compile_context(self, target, target_workdir): rsc_jar_file=os.path.join(rsc_dir, 'm.jar'), log_dir=os.path.join(rsc_dir, 'logs'), sources=sources, - workflow=self._classify_compile_target(target), + workflow=self._classify_target_compile_workflow(target), ), CompileContext( target=target, @@ -659,6 +644,6 @@ def _on_invalid_compile_dependency(self, dep, compile_target, contexts): S1/2 are rsc-then-zinc targets and S2 must be on the classpath to compile J successfully. """ return contexts[compile_target][0].workflow.resolve_for_enum_variant({ - 'zinc-only': lambda : contexts[dep][0].workflow == _JvmCompileWorkflowType.rsc_then_zinc, + 'zinc-only': lambda : contexts[dep][0].workflow == self.JvmCompileWorkflowType.rsc_then_zinc, 'rsc-then-zinc': lambda : False })() diff --git a/src/python/pants/backend/native/subsystems/BUILD b/src/python/pants/backend/native/subsystems/BUILD index 0c811238972..7b40e3c0abe 100644 --- a/src/python/pants/backend/native/subsystems/BUILD +++ b/src/python/pants/backend/native/subsystems/BUILD @@ -8,6 +8,7 @@ python_library( 'src/python/pants/backend/native/subsystems/binaries', 'src/python/pants/backend/native/subsystems/utils', 'src/python/pants/backend/python/subsystems', + 'src/python/pants/build_graph', 'src/python/pants/engine:rules', 'src/python/pants/engine:selectors', 'src/python/pants/subsystem', diff --git a/src/python/pants/backend/native/subsystems/native_build_settings.py b/src/python/pants/backend/native/subsystems/native_build_settings.py index 979ee3496df..51fe1020c2c 100644 --- a/src/python/pants/backend/native/subsystems/native_build_settings.py +++ b/src/python/pants/backend/native/subsystems/native_build_settings.py @@ -4,8 +4,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals -from pants.backend.native.subsystems.utils.mirrored_target_option_mixin import \ - MirroredTargetOptionMixin +from pants.build_graph.mirrored_target_option_mixin import MirroredTargetOptionMixin from pants.subsystem.subsystem import Subsystem @@ -13,8 +12,8 @@ class NativeBuildSettings(Subsystem, MirroredTargetOptionMixin): """Settings which affect both the compile and link phases.""" options_scope = 'native-build-settings' - mirrored_option_to_kwarg_map = { - 'strict_deps': 'strict_deps', + mirrored_target_option_actions = { + 'strict_deps': lambda tgt: tgt.strict_deps, } @classmethod @@ -29,4 +28,4 @@ def register_options(cls, register): "this behavior with the strict_deps keyword argument as well.") def get_strict_deps_value_for_target(self, target): - return self.get_target_mirrored_option('strict_deps', target) + return self.get_scalar_mirrored_target_option('strict_deps', target) diff --git a/src/python/pants/backend/native/subsystems/native_build_step.py b/src/python/pants/backend/native/subsystems/native_build_step.py index 7a9fabdb38a..b469865e7e3 100644 --- a/src/python/pants/backend/native/subsystems/native_build_step.py +++ b/src/python/pants/backend/native/subsystems/native_build_step.py @@ -6,8 +6,7 @@ from abc import abstractmethod -from pants.backend.native.subsystems.utils.mirrored_target_option_mixin import \ - MirroredTargetOptionMixin +from pants.build_graph.mirrored_target_option_mixin import MirroredTargetOptionMixin from pants.option.compiler_option_sets_mixin import CompilerOptionSetsMixin from pants.subsystem.subsystem import Subsystem from pants.util.memo import memoized_property @@ -23,9 +22,9 @@ class NativeBuildStep(CompilerOptionSetsMixin, MirroredTargetOptionMixin, Subsys options_scope = 'native-build-step' - mirrored_option_to_kwarg_map = { - 'compiler_option_sets': 'compiler_option_sets', - 'toolchain_variant': 'toolchain_variant' + mirrored_target_option_actions = { + 'compiler_option_sets': lambda tgt: tgt.compiler_option_sets, + 'toolchain_variant': lambda tgt: tgt.toolchain_variant, } @classmethod @@ -43,10 +42,10 @@ def register_options(cls, register): "linking is done with binutils ld on Linux, and the XCode CLI Tools on MacOS.") def get_compiler_option_sets_for_target(self, target): - return self.get_target_mirrored_option('compiler_option_sets', target) + return self.get_scalar_mirrored_target_option('compiler_option_sets', target) def get_toolchain_variant_for_target(self, target): - return self.get_target_mirrored_option('toolchain_variant', target) + return self.get_scalar_mirrored_target_option('toolchain_variant', target) @classproperty def get_compiler_option_sets_enabled_default_value(cls): diff --git a/src/python/pants/backend/native/subsystems/utils/mirrored_target_option_mixin.py b/src/python/pants/backend/native/subsystems/utils/mirrored_target_option_mixin.py deleted file mode 100644 index 7300dd2f6bd..00000000000 --- a/src/python/pants/backend/native/subsystems/utils/mirrored_target_option_mixin.py +++ /dev/null @@ -1,35 +0,0 @@ -# coding=utf-8 -# Copyright 2018 Pants project contributors (see CONTRIBUTORS.md). -# Licensed under the Apache License, Version 2.0 (see LICENSE). - -from __future__ import absolute_import, division, print_function, unicode_literals - -from abc import abstractmethod - -from pants.util.meta import AbstractClass, classproperty - - -# TODO: consider coalescing existing methods of mirroring options between a target and a subsystem -# -- see pants.backend.jvm.subsystems.dependency_context.DependencyContext#defaulted_property()! -class MirroredTargetOptionMixin(AbstractClass): - """Get option values which may be set in this subsystem or in a Target's keyword argument.""" - - @classproperty - @abstractmethod - def mirrored_option_to_kwarg_map(cls): - """Subclasses should override and return a dict of (subsystem option name) -> (target kwarg). - - This classproperty should return a dict mapping this subsystem's options attribute name (with - underscores) to the corresponding target's keyword argument name. - """ - - def get_target_mirrored_option(self, option_name, target): - field_name = self.mirrored_option_to_kwarg_map[option_name] - return self._get_subsystem_target_mirrored_field_value(option_name, field_name, target) - - def _get_subsystem_target_mirrored_field_value(self, option_name, field_name, target): - """Get the attribute `field_name` from `target` if set, else from this subsystem's options.""" - tgt_setting = getattr(target, field_name) - if tgt_setting is None: - return getattr(self.get_options(), option_name) - return tgt_setting diff --git a/src/python/pants/build_graph/mirrored_target_option_mixin.py b/src/python/pants/build_graph/mirrored_target_option_mixin.py new file mode 100644 index 00000000000..febde9f9658 --- /dev/null +++ b/src/python/pants/build_graph/mirrored_target_option_mixin.py @@ -0,0 +1,103 @@ +# coding=utf-8 +# Copyright 2018 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from __future__ import absolute_import, division, print_function, unicode_literals + +from abc import abstractproperty + +from future.utils import text_type + +from pants.util.memo import memoized_property +from pants.util.meta import AbstractClass +from pants.util.objects import datatype + + +class MirroredTargetOptionDeclaration(datatype([ + 'options', + ('option_name', text_type), + 'accessor', +])): + """An interface for operations to perform on an option which may also be set on a target.""" + + @memoized_property + def is_flagged(self): + """Whether the option was specified on the command line.""" + return self.options.is_flagged(self.option_name) + + def extract_target_value(self, target): + """Get the value of this option from target. + + NB: If this method returns None, that is interpreted as the target not having a value set for + the specified option, and the option value from `self.options` is used. + """ + return self.accessor(target) + + @memoized_property + def option_value(self): + """Get the value of this option, separate from any target.""" + return self.options.get(self.option_name) + + # TODO(#7183): support list/set options in addition to scalars! We should apply the same + # precedence order, but also provide some interface for allowing target fields to append or + # remove an item from a set-valued option instead of completely overwriting it! + def get_mirrored_scalar_option_value(self, target): + # Options specified on the command line take precedence over anything else. + if self.is_flagged: + return self.option_value + + # Retrieve the value from the target, if set. + target_setting = self.extract_target_value(target) + if target_setting is not None: + return target_setting + + # Otherwise, retrieve the value from the environment/pants.ini/hardcoded default. + return self.option_value + + +class MirroredTargetOptionMixin(AbstractClass): + """Get option values which may be set in this subsystem or in a Target's keyword argument. + + A subsystem or task mixing in this class may set e.g.: + + mirrored_target_option_actions = { + 'some_option_name': lambda tgt: tgt.some_option_name, + } + + which declares that '--some-option-name' can be set as a subsystem option which a target may + override, and that the target needs to have a field `.some_option_name` which this subsystem may + access. + + The class mixing this in can then call: + + some_option_value = self.get_scalar_mirrored_target_option('some_option_name', target) + + which will apply the following precedence to obtain `some_option_value`: + 1. If --some-option-name is "flagged" (provided on the command line), use the command-line value. + 2. If `target.some_option_name` is non-None, use that target-level value. + 3. Otherwise, return the option value from the environment, config, or hardcoded default. + """ + + @abstractproperty + def mirrored_target_option_actions(self): + """Subclasses should override and return a dict of (subsystem option name) -> selector function. + + A selector is a 1-argument function accepting a target and returning a value, or None. This + property should return a dict mapping this subsystem's options attribute name (with underscores) + to the corresponding selector. + """ + + @memoized_property + def _mirrored_option_declarations(self): + return { + option_name: MirroredTargetOptionDeclaration( + options=self.get_options(), + option_name=option_name, + accessor=accessor) + for option_name, accessor in self.mirrored_target_option_actions.items() + } + + def get_scalar_mirrored_target_option(self, option_name, target): + """Get the attribute `field_name` from `target` if set, else from this subsystem's options.""" + mirrored_option_declaration = self._mirrored_option_declarations[option_name] + return mirrored_option_declaration.get_mirrored_scalar_option_value(target) diff --git a/src/python/pants/util/objects.py b/src/python/pants/util/objects.py index 9db5e2f880a..be6cb339390 100644 --- a/src/python/pants/util/objects.py +++ b/src/python/pants/util/objects.py @@ -5,7 +5,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals import re -from abc import abstractmethod +from abc import abstractmethod, abstractproperty from builtins import zip from future.utils import binary_type, text_type @@ -228,11 +228,9 @@ class ChoicesMixin(AbstractClass): """A mixin which declares that the type has a fixed set of possible instances.""" @classproperty + @abstractproperty def all_variants(cls): """Return an iterable containing a de-duplicated list of all possible instances of this type.""" - raise NotImplementedError( - "The `all_variants` classproperty must be implemented for subclasses of {}!" - .format(cls.__name__)) def enum(all_values): diff --git a/testprojects/src/scala/org/pantsbuild/testproject/mutual/BUILD b/testprojects/src/scala/org/pantsbuild/testproject/mutual/BUILD index 2286edde136..7c1c30bb4b9 100644 --- a/testprojects/src/scala/org/pantsbuild/testproject/mutual/BUILD +++ b/testprojects/src/scala/org/pantsbuild/testproject/mutual/BUILD @@ -1,4 +1,6 @@ -scala_library() +scala_library( + tags = {"use-compiler:rsc-then-zinc"}, +) jvm_binary( name = "bin", diff --git a/tests/python/pants_test/backend/jvm/tasks/jvm_compile/rsc/test_rsc_compile.py b/tests/python/pants_test/backend/jvm/tasks/jvm_compile/rsc/test_rsc_compile.py index 5d76743dc2a..3d7dc2ae0f7 100644 --- a/tests/python/pants_test/backend/jvm/tasks/jvm_compile/rsc/test_rsc_compile.py +++ b/tests/python/pants_test/backend/jvm/tasks/jvm_compile/rsc/test_rsc_compile.py @@ -15,9 +15,9 @@ from pants.backend.jvm.targets.scala_library import ScalaLibrary from pants.backend.jvm.tasks.classpath_products import ClasspathProducts from pants.backend.jvm.tasks.jvm_compile.execution_graph import ExecutionGraph -from pants.backend.jvm.tasks.jvm_compile.rsc.rsc_compile import (RscCompile, _CompileWorkflowChoice, - _create_desandboxify_fn) +from pants.backend.jvm.tasks.jvm_compile.rsc.rsc_compile import RscCompile, _create_desandboxify_fn from pants.java.jar.jar_dependency import JarDependency +from pants.option.ranked_value import RankedValue from pants.util.contextutil import temporary_dir from pants_test.subsystem.subsystem_util import init_subsystem from pants_test.task_test_base import TaskTestBase @@ -75,6 +75,7 @@ def test_force_compiler_tags(self): dependee_graph = self.construct_dependee_graph_str(jobs, task) print(dependee_graph) + # TODO: remove the dep for zinc_against_rsc from rsc and fix these tests!!! self.assertEqual(dedent(""" rsc(java/classpath:java_lib) -> { zinc_against_rsc(java/classpath:java_lib) @@ -96,7 +97,8 @@ def test_no_dependencies_between_scala_and_java_targets(self): 'scala/classpath:scala_lib', target_type=ScalaLibrary, sources=['com/example/Foo.scala'], - dependencies=[] + dependencies=[], + tags={'use-compiler:rsc-then-zinc'}, ) with temporary_dir() as tmp_dir: @@ -151,6 +153,10 @@ def test_default_workflow_of_zinc_only_zincs_scala(self): dependee_graph) def test_rsc_dep_for_scala_java_and_test_targets(self): + self.set_options(workflow=RankedValue( + value=RscCompile.JvmCompileWorkflowType.rsc_then_zinc, + rank=RankedValue.CONFIG, + )) self.init_dependencies_for_scala_libraries() scala_dep = self.make_target( @@ -162,7 +168,8 @@ def test_rsc_dep_for_scala_java_and_test_targets(self): 'java/classpath:java_lib', target_type=JavaLibrary, sources=['com/example/Foo.java'], - dependencies=[scala_dep] + dependencies=[scala_dep], + tags={'use-compiler:zinc-only'} ) scala_target = self.make_target( 'scala/classpath:scala_lib', @@ -175,7 +182,8 @@ def test_rsc_dep_for_scala_java_and_test_targets(self): 'scala/classpath:scala_test', target_type=JUnitTests, sources=['com/example/Test.scala'], - dependencies=[scala_target] + dependencies=[scala_target], + tags={'use-compiler:zinc-only'} ) with temporary_dir() as tmp_dir: @@ -311,7 +319,7 @@ def init_dependencies_for_scala_libraries(self): def create_task_with_target_roots(self, target_roots, default_workflow=None): if default_workflow: - self.set_options(default_workflow=_CompileWorkflowChoice( default_workflow)) + self.set_options(workflow=RscCompile.JvmCompileWorkflowType(default_workflow)) context = self.context(target_roots=target_roots) self.init_products(context) task = self.create_task(context) diff --git a/tests/python/pants_test/backend/native/tasks/test_cpp_compile.py b/tests/python/pants_test/backend/native/tasks/test_cpp_compile.py index d53625e0eaa..5c561549793 100644 --- a/tests/python/pants_test/backend/native/tasks/test_cpp_compile.py +++ b/tests/python/pants_test/backend/native/tasks/test_cpp_compile.py @@ -10,6 +10,7 @@ from pants.backend.native.targets.native_library import CppLibrary, NativeLibrary from pants.backend.native.tasks.cpp_compile import CppCompile from pants.backend.native.tasks.native_compile import ObjectFiles +from pants.option.ranked_value import RankedValue from pants_test.backend.native.tasks.native_task_test_base import (NativeCompileTestMixin, NativeTaskTestBase) @@ -61,37 +62,37 @@ def test_caching(self): cpp_compile.execute() cpp_compile.execute() - def test_target_level_toolchain_variant_llvm(self): - self.set_options_for_scope('native-build-step', toolchain_variant=ToolchainVariant.gnu) - cpp_lib_target = self.make_target( - '//:cpp_library', - NativeLibrary, - toolchain_variant=ToolchainVariant.llvm, - ) + def _assert_compiler_toolchain_variant(self, path_string, cpp_lib_target): task = self.create_task(self.context(target_roots=[cpp_lib_target])) compiler = task.get_compiler(cpp_lib_target) + # TODO(#6866): test specifically which compiler is selected, traversing the PATH if necessary. - self.assertIn('llvm', compiler.path_entries[0]) + self.assertIn(path_string, compiler.path_entries[0]) - def test_target_level_toolchain_variant_default_llvm(self): - self.set_options_for_scope('native-build-step', toolchain_variant=ToolchainVariant.llvm) - cpp_lib_target = self.make_target( - '//:cpp_library', + def test_target_level_toolchain_variant_llvm(self): + no_toolchain_variant_target = self.make_target( + '//:cpp_lib_no_toolchain_variant', NativeLibrary, ) - task = self.create_task(self.context(target_roots=[cpp_lib_target])) - compiler = task.get_compiler(cpp_lib_target) - self.assertIn('llvm', compiler.path_entries[0]) + # Test that a target-level toolchain_variant overrides a config. + self.set_options_for_scope('native-build-step', toolchain_variant=RankedValue( + rank=RankedValue.CONFIG, + value=ToolchainVariant.gnu)) + self._assert_compiler_toolchain_variant('gcc', no_toolchain_variant_target) - def test_target_level_toolchain_variant_default_gnu(self): - self.set_options_for_scope('native-build-step', toolchain_variant=ToolchainVariant.gnu) + # Test that a target-level toolchain_variant overrides a config. cpp_lib_target = self.make_target( '//:cpp_library', NativeLibrary, + toolchain_variant=ToolchainVariant.llvm, ) - - task = self.create_task(self.context(target_roots=[cpp_lib_target])) - compiler = task.get_compiler(cpp_lib_target) - self.assertIn('gcc', compiler.path_entries[0]) + self._assert_compiler_toolchain_variant('llvm', cpp_lib_target) + + # Test that a toolchain_variant from the command line (a FLAG ranked value) overrides a target + # setting. + self.set_options_for_scope('native-build-step', toolchain_variant=RankedValue( + rank=RankedValue.FLAG, + value=ToolchainVariant.gnu)) + self._assert_compiler_toolchain_variant('gcc', cpp_lib_target)