|
16 | 16 | load("@bazel_skylib//lib:paths.bzl", "paths") |
17 | 17 | load("@rules_cc//cc/common:cc_common.bzl", "cc_common") |
18 | 18 | load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") |
| 19 | +load("//python/private:py_interpreter_program.bzl", "PyInterpreterProgramInfo") |
| 20 | +load("//python/private:toolchain_types.bzl", "EXEC_TOOLS_TOOLCHAIN_TYPE") |
19 | 21 | load(":cc_helper.bzl", "cc_helper") |
20 | 22 | load(":py_cc_link_params_info.bzl", "PyCcLinkParamsInfo") |
21 | 23 | load(":py_info.bzl", "PyInfo", "PyInfoBuilder") |
@@ -484,3 +486,120 @@ def collect_deps(ctx, extra_deps = []): |
484 | 486 | deps = list(deps) |
485 | 487 | deps.extend(extra_deps) |
486 | 488 | return deps |
| 489 | + |
| 490 | +def maybe_create_repo_mapping(ctx, *, runfiles): |
| 491 | + """Creates a repo mapping manifest if bzlmod is enabled. |
| 492 | +
|
| 493 | + There isn't a way to reference the repo mapping Bazel implicitly |
| 494 | + creates, so we have to manually create it ourselves. |
| 495 | +
|
| 496 | + Args: |
| 497 | + ctx: rule ctx. |
| 498 | + runfiles: runfiles object to generate mapping for. |
| 499 | +
|
| 500 | + Returns: |
| 501 | + File object if the repo mapping manifest was created, None otherwise. |
| 502 | + """ |
| 503 | + if not py_internal.is_bzlmod_enabled(ctx): |
| 504 | + return None |
| 505 | + |
| 506 | + # We have to add `.custom` because `{name}.repo_mapping` is used by Bazel |
| 507 | + # internally. |
| 508 | + repo_mapping_manifest = ctx.actions.declare_file(ctx.label.name + ".custom.repo_mapping") |
| 509 | + py_internal.create_repo_mapping_manifest( |
| 510 | + ctx = ctx, |
| 511 | + runfiles = runfiles, |
| 512 | + output = repo_mapping_manifest, |
| 513 | + ) |
| 514 | + return repo_mapping_manifest |
| 515 | + |
| 516 | +def actions_run( |
| 517 | + ctx, |
| 518 | + *, |
| 519 | + executable, |
| 520 | + toolchain = None, |
| 521 | + **kwargs): |
| 522 | + """Runs a tool as an action, supporting py_interpreter_program targets. |
| 523 | +
|
| 524 | + This is wrapper around `ctx.actions.run()` that sets some useful defaults, |
| 525 | + supports handling `py_interpreter_program` targets, and some other features |
| 526 | + to let the target being run influence the action invocation. |
| 527 | +
|
| 528 | + Args: |
| 529 | + ctx: The rule context. The rule must have the |
| 530 | + `//python:exec_tools_toolchain_type` toolchain available. |
| 531 | + executable: The executable to run. This can be a target that provides |
| 532 | + `PyInterpreterProgramInfo` or a regular executable target. If it |
| 533 | + provides `testing.ExecutionInfo`, the requirements will be added to |
| 534 | + the execution requirements. |
| 535 | + toolchain: The toolchain type to use. Must be None or |
| 536 | + `//python:exec_tools_toolchain_type`. |
| 537 | + **kwargs: Additional arguments to pass to `ctx.actions.run()`. |
| 538 | + `mnemonic` and `progress_message` are required. |
| 539 | + """ |
| 540 | + mnemonic = kwargs.pop("mnemonic", None) |
| 541 | + if not mnemonic: |
| 542 | + fail("actions_run: missing required argument 'mnemonic'") |
| 543 | + |
| 544 | + progress_message = kwargs.pop("progress_message", None) |
| 545 | + if not progress_message: |
| 546 | + fail("actions_run: missing required argument 'progress_message'") |
| 547 | + |
| 548 | + tools = kwargs.pop("tools", None) |
| 549 | + tools = list(tools) if tools else [] |
| 550 | + arguments = kwargs.pop("arguments", []) |
| 551 | + |
| 552 | + action_arguments = [] |
| 553 | + action_env = { |
| 554 | + "PYTHONHASHSEED": "0", # Helps avoid non-deterministic behavior |
| 555 | + "PYTHONNOUSERSITE": "1", # Helps avoid non-deterministic behavior |
| 556 | + "PYTHONSAFEPATH": "1", # Helps avoid incorrect import issues |
| 557 | + } |
| 558 | + default_info = executable[DefaultInfo] |
| 559 | + if PyInterpreterProgramInfo in executable: |
| 560 | + if toolchain and toolchain != EXEC_TOOLS_TOOLCHAIN_TYPE: |
| 561 | + fail(("Action {}: tool {} provides PyInterpreterProgramInfo, which " + |
| 562 | + "requires the `toolchain` arg be " + |
| 563 | + "None or {}, got: {}").format( |
| 564 | + mnemonic, |
| 565 | + executable, |
| 566 | + EXEC_TOOLS_TOOLCHAIN_TYPE, |
| 567 | + toolchain, |
| 568 | + )) |
| 569 | + exec_tools = ctx.toolchains[EXEC_TOOLS_TOOLCHAIN_TYPE].exec_tools |
| 570 | + action_exe = exec_tools.exec_interpreter[DefaultInfo].files_to_run |
| 571 | + |
| 572 | + program_info = executable[PyInterpreterProgramInfo] |
| 573 | + |
| 574 | + interpreter_args = ctx.actions.args() |
| 575 | + interpreter_args.add_all(program_info.interpreter_args) |
| 576 | + interpreter_args.add(default_info.files_to_run.executable) |
| 577 | + action_arguments.append(interpreter_args) |
| 578 | + |
| 579 | + action_env.update(program_info.env) |
| 580 | + tools.append(default_info.files_to_run) |
| 581 | + toolchain = EXEC_TOOLS_TOOLCHAIN_TYPE |
| 582 | + else: |
| 583 | + action_exe = executable[DefaultInfo].files_to_run |
| 584 | + |
| 585 | + execution_requirements = {} |
| 586 | + if testing.ExecutionInfo in executable: |
| 587 | + execution_requirements.update(executable[testing.ExecutionInfo].requirements) |
| 588 | + |
| 589 | + # Give precedence to caller's execution requirements. |
| 590 | + execution_requirements.update(kwargs.pop("execution_requirements", None) or {}) |
| 591 | + |
| 592 | + # Give precedence to caller's env. |
| 593 | + action_env.update(kwargs.pop("env", None) or {}) |
| 594 | + action_arguments.extend(arguments) |
| 595 | + ctx.actions.run( |
| 596 | + executable = action_exe, |
| 597 | + arguments = action_arguments, |
| 598 | + tools = tools, |
| 599 | + env = action_env, |
| 600 | + execution_requirements = execution_requirements, |
| 601 | + toolchain = toolchain, |
| 602 | + mnemonic = mnemonic, |
| 603 | + progress_message = progress_message, |
| 604 | + **kwargs |
| 605 | + ) |
0 commit comments