Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ All notable changes to this project will be documented in this file.
preserved, per-edge resolution-marker forks use their variant-specific marker
expressions, and locked versions use exact (`===`) matching so local version
tags (e.g. `2.12.0` vs `2.12.0+cpu`) do not collide.
- **Missing `uv` conflict variant.** Fails with a message naming the `_variants`
flags, at execution time under `unavailable_package_mode=fail_at_execution`.

## [2.0.0]

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -805,11 +805,13 @@ The transition applies to the repo's whole closure, including sdist builds from

## Handling Unavailable Packages

A package is unavailable in the selected target environment when it has no compatible wheel (and no sdist to fall back to), when no resolution-marker fork matches, or when the selected conflict variant doesn't include it. (Selecting no variant from a conflict set that has no default is still an analysis error; see [Default Groups](#default-groups).) The `--@rules_pycross//pycross/settings:unavailable_package_mode` flag controls how such packages behave:
A package is unavailable in the selected target environment when it has no compatible wheel (and no sdist to fall back to), when no resolution-marker fork matches, or when the selected conflict variant doesn't include it. The `--@rules_pycross//pycross/settings:unavailable_package_mode` flag controls how such packages behave:

* **`incompatible` (default)** — unavailable packages are marked [incompatible](https://bazel.build/extending/platforms#skipping-incompatible-targets): targets that depend on them are skipped by `bazel build //...` and `bazel test //...`, and fail analysis when requested explicitly. (`all_requirements` and other platform-conditional `:maybe` aggregates exclude such packages instead.)
* **`fail_at_execution`** — unavailable packages analyze successfully and provide the usual providers (`PyInfo`, etc.), registering an action that fails only when executed. Building anything that actually needs the package fails with a `No compatible wheel is available ...` error.

Leaving a `uv` conflict set without a default unselected (see [Default Groups](#default-groups)) always fails with a message naming the `_variants` flags to set—at analysis time by default (so `bazel build //...` does not silently skip conflicted targets), or at execution time under `fail_at_execution`.

`fail_at_execution` is useful when running consumers that only *analyze* dependencies—such as type-checking aspects that attach stub packages via implicit attributes, which otherwise see a target without the usual providers and fail even when the package is never built—or when you want wildcard `bazel build //...` / `bazel test //...` runs to fail loudly instead of silently skipping targets with unavailable dependencies:

```
Expand Down
2 changes: 2 additions & 0 deletions pycross/private/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ bzl_library(
name = "variant_resolver",
srcs = ["variant_resolver.bzl"],
deps = [
":deferred_failure",
"@bazel_skylib//rules:common_settings",
"@rules_python//python:py_info_bzl",
],
)

Expand Down
31 changes: 25 additions & 6 deletions pycross/private/package_repo.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,13 @@ def _package_repo_impl(rctx):
"",
]
if variant_items:
lock_build_lines.extend([
'load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")',
'load("@rules_pycross//pycross/private:variant_resolver.bzl", "variant_resolver")',
"",
])
has_unselected_fallback = any([not vset["default"] for vset in variant_sets])
lock_build_lines.append('load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")')
if has_unselected_fallback:
lock_build_lines.append('load("@rules_pycross//pycross/private:variant_resolver.bzl", "pycross_no_variant_selected", "variant_resolver")')
else:
lock_build_lines.append('load("@rules_pycross//pycross/private:variant_resolver.bzl", "variant_resolver")')
lock_build_lines.append("")

# Check if we need config_setting_group for items in multiple sets.
needs_config_setting_group = False
Expand Down Expand Up @@ -243,7 +245,8 @@ def _package_repo_impl(rctx):

# Emit variant_resolver targets (one per conflict set).
for vset in variant_sets:
resolver_name = "_resolver_" + "_".join(sorted(vset["qnames"]))
set_suffix = "_".join(sorted(vset["qnames"]))
resolver_name = "_resolver_" + set_suffix
lock_build_lines.extend([
"variant_resolver(",
' name = "{}",'.format(resolver_name),
Expand All @@ -266,6 +269,22 @@ def _package_repo_impl(rctx):
")",
"",
])
if not vset["default"]:
lock_build_lines.extend([
"config_setting(",
' name = "_unselected_{}",'.format(set_suffix),
' flag_values = {{":{resolver}": ""}},'.format(resolver = resolver_name),
")",
"",
"pycross_no_variant_selected(",
' name = "_no_variant_{}",'.format(set_suffix),
' message = "No uv conflict variant selected. Set one of the _variants flags on your pycross repo: {} (e.g. --@<repo>//_variants:{}=True)",'.format(
", ".join(vset["qnames"]),
vset["qnames"][0],
),
")",
"",
])

# Emit config_settings referencing the resolver(s).
for qname in sorted(variant_items.keys()):
Expand Down
Loading
Loading