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

[internal] Introduce new BuiltinGoal subsystem type. #13991

Merged
merged 11 commits into from
Jan 11, 2022
48 changes: 22 additions & 26 deletions src/python/pants/bin/local_pants_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,13 @@
WorkunitsCallback,
WorkunitsCallbackFactories,
)
from pants.engine.target import RegisteredTargetTypes
from pants.engine.unions import UnionMembership
from pants.goal.builtin_goal import BuiltinGoal
from pants.goal.run_tracker import RunTracker
from pants.help.help_info_extracter import HelpInfoExtracter
from pants.help.help_printer import HelpPrinter
from pants.init.engine_initializer import EngineInitializer, GraphScheduler, GraphSession
from pants.init.logging import stdio_destination_use_color
from pants.init.options_initializer import OptionsInitializer
from pants.init.specs_calculator import calculate_specs
from pants.option.arg_splitter import HelpRequest
from pants.option.global_options import DynamicRemoteOptions
from pants.option.options import Options
from pants.option.options_bootstrapper import OptionsBootstrapper
Expand Down Expand Up @@ -126,8 +123,10 @@ def create(
# Option values are usually computed lazily on demand, but command line options are
# eagerly computed for validation.
with options_initializer.handle_unknown_flags(options_bootstrapper, env, raise_=True):
for scope in options.scope_to_flags.keys():
options.for_scope(scope)
for scope, values in options.scope_to_flags.items():
if values:
# Only compute values if there were any command line options presented.
options.for_scope(scope)

# Verify configs.
global_bootstrap_options = options_bootstrapper.bootstrap_options.for_global_scope()
Expand Down Expand Up @@ -198,24 +197,6 @@ def _perform_run_body(self, goals: tuple[str, ...], poll: bool) -> ExitCode:
def _finish_run(self, code: ExitCode) -> None:
"""Cleans up the run tracker."""

def _print_help(self, request: HelpRequest) -> ExitCode:
global_options = self.options.for_global_scope()

all_help_info = HelpInfoExtracter.get_all_help_info(
self.options,
self.union_membership,
self.graph_session.goal_consumed_subsystem_scopes,
RegisteredTargetTypes.create(self.build_config.target_types),
self.build_config,
)
help_printer = HelpPrinter(
bin_name=global_options.pants_bin_name,
help_request=request,
all_help_info=all_help_info,
color=global_options.colors,
)
return help_printer.print_help()

def _get_workunits_callbacks(self) -> tuple[WorkunitsCallback, ...]:
# Load WorkunitsCallbacks by requesting WorkunitsCallbackFactories, and then constructing
# a per-run instance of each WorkunitsCallback.
Expand All @@ -224,10 +205,25 @@ def _get_workunits_callbacks(self) -> tuple[WorkunitsCallback, ...]:
)
return tuple(filter(bool, (wcf.callback_factory() for wcf in workunits_callback_factories)))

def _run_builtin_goal(self, builtin_goal: str) -> ExitCode:
scope_info = self.options.known_scope_to_info[builtin_goal]
assert scope_info.subsystem_cls
scoped_options = self.options.for_scope(builtin_goal)
goal = scope_info.subsystem_cls(scoped_options)
assert isinstance(goal, BuiltinGoal)
return goal.run(
build_config=self.build_config,
graph_session=self.graph_session,
options=self.options,
specs=self.specs,
union_membership=self.union_membership,
)

def _run_inner(self) -> ExitCode:
if self.options.builtin_goal:
return self._run_builtin_goal(self.options.builtin_goal)

goals = tuple(self.options.goals)
if self.options.help_request:
return self._print_help(self.options.help_request)
if not goals:
return PANTS_SUCCEEDED_EXIT_CODE

Expand Down
42 changes: 42 additions & 0 deletions src/python/pants/goal/builtin_goal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from __future__ import annotations

from abc import ABC, abstractmethod
from typing import ClassVar

from pants.base.exiter import ExitCode
from pants.base.specs import Specs
from pants.build_graph.build_configuration import BuildConfiguration
from pants.engine.goal import GoalSubsystem
from pants.engine.unions import UnionMembership
from pants.init.engine_initializer import GraphSession
from pants.option.options import Options
from pants.option.scope import ScopeInfo


class BuiltinGoal(ABC, GoalSubsystem):
"""Builtin goals have precedence over regular goal rules.

If a builtin goal is invoked, any remaining arguments are passed unaltered to the builtin goal.
"""

# Used by `pants.option.arg_splitter.ArgSplitter()` to optionally allow aliasing builtin goals.
aliases: ClassVar[tuple[str, ...]] = ()

@classmethod
def create_scope_info(cls, **scope_info_kwargs) -> ScopeInfo:
return super().create_scope_info(is_builtin=True, **scope_info_kwargs)

@abstractmethod
def run(
self,
*,
build_config: BuildConfiguration,
graph_session: GraphSession,
options: Options,
specs: Specs,
union_membership: UnionMembership,
) -> ExitCode:
pass
23 changes: 23 additions & 0 deletions src/python/pants/goal/builtins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from __future__ import annotations

from pants.build_graph.build_configuration import BuildConfiguration
from pants.goal import help
from pants.goal.builtin_goal import BuiltinGoal


def register_builtin_goals(build_configuration: BuildConfiguration.Builder) -> None:
build_configuration.register_subsystems("pants.goal", builtin_goals())


def builtin_goals() -> tuple[type[BuiltinGoal], ...]:
kaos marked this conversation as resolved.
Show resolved Hide resolved
return (
help.AllHelpBuiltinGoal,
help.NoGoalHelpBuiltinGoal,
help.ThingHelpBuiltinGoal,
help.ThingHelpAdvancedBuiltinGoal,
help.UnknownGoalHelpBuiltinGoal,
help.VersionHelpBuiltinGoal,
)
119 changes: 119 additions & 0 deletions src/python/pants/goal/help.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
kaos marked this conversation as resolved.
Show resolved Hide resolved
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from __future__ import annotations

from abc import abstractmethod

from pants.base.exiter import ExitCode
from pants.base.specs import Specs
from pants.build_graph.build_configuration import BuildConfiguration
from pants.engine.target import RegisteredTargetTypes
from pants.engine.unions import UnionMembership
from pants.goal.builtin_goal import BuiltinGoal
from pants.help.help_info_extracter import HelpInfoExtracter
from pants.help.help_printer import HelpPrinter
from pants.init.engine_initializer import GraphSession
from pants.option.arg_splitter import (
AllHelp,
HelpRequest,
NoGoalHelp,
ThingHelp,
UnknownGoalHelp,
VersionHelp,
)
kaos marked this conversation as resolved.
Show resolved Hide resolved
from pants.option.options import Options


class HelpBuiltinGoalBase(BuiltinGoal):
def run(
self,
build_config: BuildConfiguration,
graph_session: GraphSession,
options: Options,
specs: Specs,
union_membership: UnionMembership,
) -> ExitCode:
all_help_info = HelpInfoExtracter.get_all_help_info(
options,
union_membership,
graph_session.goal_consumed_subsystem_scopes,
RegisteredTargetTypes.create(build_config.target_types),
build_config,
)
global_options = options.for_global_scope()
help_printer = HelpPrinter(
bin_name=global_options.pants_bin_name,
help_request=self.create_help_request(options),
all_help_info=all_help_info,
color=global_options.colors,
)
return help_printer.print_help()

@abstractmethod
def create_help_request(self, options: Options) -> HelpRequest:
raise NotImplementedError


class AllHelpBuiltinGoal(HelpBuiltinGoalBase):
name = "help-all"
help = "Print a JSON object containing all help info."

def create_help_request(self, options: Options) -> HelpRequest:
return AllHelp()


class NoGoalHelpBuiltinGoal(HelpBuiltinGoalBase):
name = "__no_goal"
help = "(internal goal not presented on the CLI)"

def create_help_request(self, options: Options) -> HelpRequest:
return NoGoalHelp()


class ThingHelpBuiltinGoal(HelpBuiltinGoalBase):
name = "help"
help = "Display usage message."
aliases = (
"-h",
"--help",
)

def create_help_request(self, options: Options) -> HelpRequest:
return ThingHelp(
advanced=False,
things=tuple(options.builtin_goal_args)
+ tuple(options.goals)
+ tuple(options.unknown_goals),
)


class ThingHelpAdvancedBuiltinGoal(HelpBuiltinGoalBase):
name = "help-advanced"
help = "Help for advanced options."
aliases = ("--help-advanced",)

def create_help_request(self, options: Options) -> HelpRequest:
return ThingHelp(
advanced=True,
things=tuple(options.builtin_goal_args)
+ tuple(options.goals)
+ tuple(options.unknown_goals),
)


class UnknownGoalHelpBuiltinGoal(HelpBuiltinGoalBase):
name = "__unknown_goal"
help = "(internal goal not presented on the CLI)"

def create_help_request(self, options: Options) -> HelpRequest:
return UnknownGoalHelp(tuple(options.unknown_goals))


class VersionHelpBuiltinGoal(HelpBuiltinGoalBase):
name = "version"
help = "Display Pants version."
aliases = ("-v", "-V", "--version")

def create_help_request(self, options: Options) -> HelpRequest:
return VersionHelp()
3 changes: 3 additions & 0 deletions src/python/pants/help/help_info_extracter.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ def get_all_help_info(
scope_to_help_info = {}
name_to_goal_info = {}
for scope_info in sorted(options.known_scope_to_info.values(), key=lambda x: x.scope):
if scope_info.scope.startswith("_"):
# Exclude "private" subsystems.
continue
kaos marked this conversation as resolved.
Show resolved Hide resolved
options.for_scope(scope_info.scope) # Force parsing.
subsystem_cls = scope_info.subsystem_cls
if not scope_info.description:
Expand Down
2 changes: 2 additions & 0 deletions src/python/pants/init/extension_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from pants.base.exceptions import BackendConfigurationError
from pants.build_graph.build_configuration import BuildConfiguration
from pants.goal.builtins import register_builtin_goals
from pants.util.ordered_set import FrozenOrderedSet


Expand Down Expand Up @@ -40,6 +41,7 @@ def load_backends_and_plugins(
bc_builder = bc_builder or BuildConfiguration.Builder()
load_build_configuration_from_source(bc_builder, backends)
load_plugins(bc_builder, plugins, working_set)
register_builtin_goals(bc_builder)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicitly register our built in goals. Not configurable/extensible.

return bc_builder.create()


Expand Down
Loading