Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for v2-only goals, and replace list with a @console_rule #6880

Merged
1 change: 1 addition & 0 deletions build-support/bin/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ function execute_packaged_pants_with_internal_backends() {
--no-verify-config \
--pythonpath="['pants-plugins/src/python']" \
--backend-packages="[\
'pants.rules.core',\
'pants.backend.codegen',\
'pants.backend.docgen',\
'pants.backend.graph_info',\
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,26 @@

from __future__ import absolute_import, division, print_function, unicode_literals

from builtins import str

from pants.engine.addressable import BuildFileAddresses
from pants.engine.console import Console
from pants.engine.goal import Goal
from pants.engine.rules import console_rule
from pants.engine.selectors import Get
from pants.option.scope import Scope, ScopedOptions
from pants.rules.core.exceptions import GracefulTerminationException


@console_rule('list-and-die-for-testing', [Console, BuildFileAddresses])
def fast_list_and_die_for_testing(console, addresses):
class ListAndDieForTesting(Goal):
"""A fast and deadly variant of `./pants list`."""
options = yield Get(ScopedOptions, Scope(str('list')))
console.print_stderr('>>> Got an interesting option: {}'.format(options.options.documented))

name = 'list-and-die-for-testing'


@console_rule(ListAndDieForTesting, [Console, BuildFileAddresses])
def fast_list_and_die_for_testing(console, addresses):
stuhood marked this conversation as resolved.
Show resolved Hide resolved
for address in addresses.dependencies:
console.print_stdout(address.spec)
raise GracefulTerminationException(exit_code=42)
yield ListAndDieForTesting(exit_code=42)


def rules():
return (fast_list_and_die_for_testing,)
return [
fast_list_and_die_for_testing,
]
3 changes: 1 addition & 2 deletions pants.ini
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,5 @@ compiler_option_sets_disabled_args: {
[libc]
enable_libc_search: True

[sourcefile_validation]
[sourcefile-validation]
config: @build-support/regexes/config.yaml
detail_level: nonmatching
2 changes: 0 additions & 2 deletions src/python/pants/backend/graph_info/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@
from pants.backend.graph_info.tasks.dependees import ReverseDepmap
from pants.backend.graph_info.tasks.filemap import Filemap
from pants.backend.graph_info.tasks.filter import Filter
from pants.backend.graph_info.tasks.list_targets import ListTargets
from pants.backend.graph_info.tasks.minimal_cover import MinimalCover
from pants.backend.graph_info.tasks.paths import Path, Paths
from pants.backend.graph_info.tasks.sort_targets import SortTargets
from pants.goal.task_registrar import TaskRegistrar as task


def register_goals():
task(name='list', action=ListTargets).install()
task(name='path', action=Path).install()
task(name='paths', action=Paths).install()
task(name='dependees', action=ReverseDepmap).install()
Expand Down
76 changes: 0 additions & 76 deletions src/python/pants/backend/graph_info/tasks/list_targets.py

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@

from future.utils import text_type

from pants.base.exiter import PANTS_FAILED_EXIT_CODE, PANTS_SUCCEEDED_EXIT_CODE
from pants.engine.console import Console
from pants.engine.fs import Digest, FilesContent
from pants.engine.goal import Goal
from pants.engine.legacy.graph import HydratedTarget, HydratedTargets
from pants.engine.objects import Collection
from pants.engine.rules import console_rule, optionable_rule, rule
from pants.engine.selectors import Get
from pants.rules.core.exceptions import GracefulTerminationException
from pants.subsystem.subsystem import Subsystem
from pants.util.memo import memoized_method
from pants.util.objects import datatype, enum
Expand All @@ -32,8 +33,18 @@ class DetailLevel(enum(['none', 'summary', 'nonmatching', 'all'])):
pass


class Validate(Goal):
name = 'validate'

@classmethod
def register_options(cls, register):
super(Validate, cls).register_options(register)
register('--detail-level', type=DetailLevel, default=DetailLevel.nonmatching,
help='How much detail to emit to the console.')


class SourceFileValidation(Subsystem):
options_scope = 'sourcefile_validation'
options_scope = 'sourcefile-validation'

@classmethod
def register_options(cls, register):
Expand Down Expand Up @@ -68,8 +79,6 @@ def register_options(cls, register):
register('--config', type=dict, fromfile=True,
# TODO: Replace "See documentation" with actual URL, once we have some.
help='Source file regex matching config. See documentation for config schema.')
register('--detail-level', type=DetailLevel, default=DetailLevel.nonmatching,
help='How much detail to emit to the console.')

@memoized_method
def get_multi_matcher(self):
Expand Down Expand Up @@ -201,12 +210,12 @@ def get_applicable_content_pattern_names(self, path):

# TODO: Switch this to `lint` once we figure out a good way for v1 tasks and v2 rules
# to share goal names.
@console_rule('validate', [Console, HydratedTargets, SourceFileValidation])
def validate(console, hydrated_targets, source_file_validation):
@console_rule(Validate, [Console, HydratedTargets, Validate.Options])
def validate(console, hydrated_targets, validate_options):
per_tgt_rmrs = yield [Get(RegexMatchResults, HydratedTarget, ht) for ht in hydrated_targets]
regex_match_results = list(itertools.chain(*per_tgt_rmrs))

detail_level = source_file_validation.get_options().detail_level
detail_level = validate_options.values.detail_level
regex_match_results = sorted(regex_match_results, key=lambda x: x.path)
num_matched_all = 0
num_nonmatched_some = 0
Expand All @@ -232,7 +241,11 @@ def validate(console, hydrated_targets, source_file_validation):
num_nonmatched_some))

if num_nonmatched_some:
raise GracefulTerminationException('Files failed validation.')
console.print_stderr('Files failed validation.')
exit_code = PANTS_FAILED_EXIT_CODE
else:
exit_code = PANTS_SUCCEEDED_EXIT_CODE
yield Validate(exit_code)


@rule(RegexMatchResults, [HydratedTarget, SourceFileValidation])
Expand Down
41 changes: 33 additions & 8 deletions src/python/pants/bin/daemon_pants_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@

from pants.base.build_environment import get_buildroot
from pants.base.exception_sink import ExceptionSink, SignalHandler
from pants.base.exiter import PANTS_SUCCEEDED_EXIT_CODE, Exiter
from pants.base.exiter import PANTS_FAILED_EXIT_CODE, PANTS_SUCCEEDED_EXIT_CODE, Exiter
from pants.bin.local_pants_runner import LocalPantsRunner
from pants.init.util import clean_global_runtime_state
from pants.java.nailgun_io import NailgunStreamStdinReader, NailgunStreamWriter
from pants.java.nailgun_protocol import ChunkType, NailgunProtocol
from pants.pantsd.process_manager import ProcessManager
from pants.rules.core.exceptions import GracefulTerminationException
from pants.util.contextutil import hermetic_environment_as, stdio_as
from pants.util.socket import teardown_socket

Expand Down Expand Up @@ -77,6 +76,31 @@ def exit(self, result=0, msg=None, *args, **kwargs):
super(DaemonExiter, self).exit(result=result, *args, **kwargs)


class _GracefulTerminationException(Exception):
"""Allows for deferring the returning of the exit code of prefork work until post fork.

TODO: Once the fork boundary is removed in #7390, this class can be replaced by directly exiting
with the relevant exit code.
"""

def __init__(self, exit_code=PANTS_FAILED_EXIT_CODE):
"""
:param int exit_code: an optional exit code (defaults to PANTS_FAILED_EXIT_CODE)
"""
super(_GracefulTerminationException, self).__init__('Terminated with {}'.format(exit_code))

if exit_code == PANTS_SUCCEEDED_EXIT_CODE:
raise ValueError(
"Cannot create _GracefulTerminationException with a successful exit code of {}"
.format(PANTS_SUCCEEDED_EXIT_CODE))

self._exit_code = exit_code

@property
def exit_code(self):
return self._exit_code


class DaemonPantsRunner(ProcessManager):
"""A daemonizing PantsRunner that speaks the nailgun protocol to a remote client.

Expand All @@ -92,8 +116,8 @@ def create(cls, sock, args, env, services, scheduler_service):
with cls.nailgunned_stdio(sock, env, handle_stdin=False):
options, _, options_bootstrapper = LocalPantsRunner.parse_options(args, env)
subprocess_dir = options.for_global_scope().pants_subprocessdir
graph_helper, target_roots = scheduler_service.prefork(options, options_bootstrapper)
deferred_exc = None
graph_helper, target_roots, exit_code = scheduler_service.prefork(options, options_bootstrapper)
deferred_exc = None if exit_code == PANTS_SUCCEEDED_EXIT_CODE else _GracefulTerminationException(exit_code)
except Exception:
deferred_exc = sys.exc_info()
graph_helper = None
Expand Down Expand Up @@ -316,9 +340,6 @@ def post_fork_child(self):
# Clean global state.
clean_global_runtime_state(reset_subsystem=True)

# Re-raise any deferred exceptions, if present.
self._raise_deferred_exc()

# Otherwise, conduct a normal run.
runner = LocalPantsRunner.create(
self._exiter,
Expand All @@ -329,10 +350,14 @@ def post_fork_child(self):
self._options_bootstrapper
)
runner.set_start_time(self._maybe_get_client_start_time_from_env(self._env))

# Re-raise any deferred exceptions, if present.
self._raise_deferred_exc()

runner.run()
except KeyboardInterrupt:
self._exiter.exit_and_fail('Interrupted by user.\n')
except GracefulTerminationException as e:
except _GracefulTerminationException as e:
ExceptionSink.log_exception(
'Encountered graceful termination exception {}; exiting'.format(e))
self._exiter.exit(e.exit_code)
Expand Down
22 changes: 10 additions & 12 deletions src/python/pants/bin/goal_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from pants.goal.context import Context
from pants.goal.goal import Goal
from pants.goal.run_tracker import RunTracker
from pants.help.help_printer import HelpPrinter
from pants.java.nailgun_executor import NailgunProcessGroup
from pants.option.ranked_value import RankedValue
from pants.task.task import QuietTaskMixin
Expand Down Expand Up @@ -51,15 +50,11 @@ def __init__(self, root_dir, options, build_config, run_tracker, reporting, grap
self._explain = self._global_options.explain
self._kill_nailguns = self._global_options.kill_nailguns

def _maybe_handle_help(self, help_request):
"""Handle requests for `help` information."""
if help_request:
help_printer = HelpPrinter(self._options)
result = help_printer.print_help()
self._exiter(result)

def _determine_goals(self, address_mapper, requested_goals):
def _determine_v1_goals(self, address_mapper, options):
"""Check and populate the requested goals for a given run."""
v1_goals, ambiguous_goals, _ = options.goals_by_version
requested_goals = v1_goals + ambiguous_goals

spec_parser = CmdLineSpecParser(self._root_dir)

for goal in requested_goals:
Expand Down Expand Up @@ -95,7 +90,7 @@ def _setup_context(self):
self._root_dir
)

goals = self._determine_goals(address_mapper, self._options.goals)
goals = self._determine_v1_goals(address_mapper, self._options)
is_quiet = self._should_be_quiet(goals)

target_root_instances = self._roots_to_targets(build_graph, self._target_roots)
Expand All @@ -122,7 +117,6 @@ def _setup_context(self):
return goals, context

def create(self):
self._maybe_handle_help(self._options.help_request)
goals, context = self._setup_context()
return GoalRunner(context=context,
goals=goals,
Expand All @@ -131,7 +125,11 @@ def create(self):


class GoalRunner(object):
"""Lists installed goals or else executes a named goal."""
"""Lists installed goals or else executes a named goal.

NB: GoalRunner represents a v1-only codepath. v2 goals are registered via `@console_rule` and
the `pants.engine.goal.Goal` class.
"""

Factory = GoalRunnerFactory

Expand Down
Loading