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
69 changes: 51 additions & 18 deletions src/fromager/commands/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,17 +346,13 @@ def _build(
pbi = wkctx.package_build_info(req)
prebuilt = pbi.pre_built

wheel_server_urls = wheels.get_wheel_server_urls(
wkctx, req, cache_wheel_server_url=cache_wheel_server_url
)

# See if we can reuse an existing wheel.
if not force:
wheel_filename = _is_wheel_built(
wkctx,
req.name,
resolved_version,
wheel_server_urls,
cache_wheel_server_url=cache_wheel_server_url,
)
if wheel_filename:
logger.info("using existing wheel from %s", wheel_filename)
Expand Down Expand Up @@ -469,23 +465,62 @@ def _is_wheel_built(
wkctx: context.WorkContext,
dist_name: str,
resolved_version: Version,
wheel_server_urls: list[str],
*,
cache_wheel_server_url: str | None = None,
) -> pathlib.Path | None:
req = Requirement(f"{dist_name}=={resolved_version}")
pbi = wkctx.package_build_info(req)
# No package index and no local or job cache means there is nothing to reuse.
if (
not pbi.wheel_server_url
and not wkctx.wheel_server_url
and not cache_wheel_server_url
):
logger.debug(
"no wheel server configured for %s, skipping existing-wheel check",
req,
)
return None
Comment thread
rd4398 marked this conversation as resolved.

try:
# Package wheel_server_url is exclusive: it replaces local and cache.
servers = wheels.get_wheel_server_urls(
wkctx,
req,
cache_wheel_server_url=cache_wheel_server_url,
)
logger.info(
"checking if a suitable wheel for %s was already built on %s",
req,
wheel_server_urls,
)
url, _ = wheels.resolve_prebuilt_wheel(
ctx=wkctx,
req=req,
wheel_server_urls=wheel_server_urls,
servers,
)

url: str | None = None
for server_url in servers:
try:
if pbi.wheel_server_url:
# Upstream pre-built index. Release-age cooldown stays on.
url, _ = wheels.resolve_prebuilt_wheel(
ctx=wkctx,
req=req,
wheel_server_urls=[server_url],
)
else:
# Local server and --cache-wheel-server-url are trusted caches.
url, _ = wheels.resolve_cached_wheel(
ctx=wkctx,
req=req,
cache_server_url=server_url,
)
break
except Exception:
Comment thread
rd4398 marked this conversation as resolved.
logger.debug("wheel not found on %s", server_url, exc_info=True)

if url is None:
logger.info("could not locate existing wheel")
return None

logger.info("found candidate wheel %s", url)
pbi = wkctx.package_build_info(req)
build_tag_from_settings = pbi.build_tag(resolved_version)
build_tag = build_tag_from_settings if build_tag_from_settings else (0, "")
wheel_basename = downloads.extract_filename_from_url(url)
Expand All @@ -505,28 +540,26 @@ def _is_wheel_built(
return None

wheel_filename: pathlib.Path | None = None
if url.startswith(wkctx.wheel_server_url):
if wkctx.wheel_server_url and url.startswith(wkctx.wheel_server_url):
logging.debug("found wheel on local server")
wheel_filename = wkctx.wheels_downloads / wheel_basename
if not wheel_filename.exists():
logger.info("wheel not found in local cache, preparing to download")
wheel_filename = None

if not wheel_filename:
# if the found wheel was on an external server, then download it
logger.info("downloading wheel from %s", url)
wheel_filename = wheels.download_wheel(req, url, wkctx.wheels_downloads)

return wheel_filename
except Exception:
logger.debug(
"could not locate prebuilt wheel %s-%s on %s",
"could not locate existing wheel %s-%s",
dist_name,
resolved_version,
wheel_server_urls,
exc_info=True,
)
logger.info("could not locate prebuilt wheel")
logger.info("could not locate existing wheel")
return None


Expand Down
29 changes: 20 additions & 9 deletions src/fromager/commands/download_sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,13 @@ def download_sequence(
the build order file that have source_url_type as sdist.

"""
if wkctx.wheel_server_url:
wheel_servers = [wkctx.wheel_server_url]
else:
wheel_servers = [sdist_server_url]
# The local wheel server is a trusted cache (wheels built in this or
# prior runs). An external sdist_server_url used as a wheel fallback
# is an upstream index that needs cooldown and override hooks.
use_cache_resolution = bool(wkctx.wheel_server_url)
wheel_server_url = (
wkctx.wheel_server_url if use_cache_resolution else sdist_server_url
)

logger.info("reading build order from %s", build_order_file)
with read.open_file_or_url(build_order_file) as f:
Expand Down Expand Up @@ -82,7 +85,6 @@ def download_one(entry: dict[str, typing.Any]) -> None:
except Exception as err:
logger.error(f"failed to download sdist for {req}: {err}")
if not ignore_missing_sdists:
# Re-raise with package context since context var is lost across threads
raise RuntimeError(f"Failed to download sdist for {req}") from err
else:
logger.info(
Expand All @@ -91,12 +93,21 @@ def download_one(entry: dict[str, typing.Any]) -> None:

if include_wheels:
try:
wheel_url, _ = wheels.resolve_prebuilt_wheel(
ctx=wkctx, req=req, wheel_server_urls=wheel_servers
)
if use_cache_resolution:
resolved_url, _ = wheels.resolve_cached_wheel(
ctx=wkctx,
req=req,
cache_server_url=wheel_server_url,
)
else:
resolved_url, _ = wheels.resolve_prebuilt_wheel(
ctx=wkctx,
req=req,
wheel_server_urls=[wheel_server_url],
)
wheels.download_wheel(
req=req,
wheel_url=wheel_url,
wheel_url=resolved_url,
output_directory=wkctx.wheels_downloads,
)
except Exception as err:
Expand Down
32 changes: 24 additions & 8 deletions src/fromager/wheels.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
dependencies,
downloads,
external_commands,
finders,
metrics,
overrides,
packagesettings,
Expand Down Expand Up @@ -508,6 +509,26 @@ def get_prebuilt_wheel_provider(
)


@metrics.timeit(description="resolve wheel")
def resolve_cached_wheel(
Comment thread
smoparth marked this conversation as resolved.
*,
Comment thread
smoparth marked this conversation as resolved.
ctx: context.WorkContext,
req: Requirement,
cache_server_url: str,
) -> tuple[str, Version]:
"""Resolve a wheel from a trusted cache server (local or remote).

Uses ``PyPICacheProvider`` -- no cooldown, no hooks, no upload-time checks.
"""
provider = finders.PyPICacheProvider(
cache_server_url=cache_server_url,
constraints=ctx.constraints,
)
results = resolver.find_all_matching_from_provider(provider, req)
Comment thread
rd4398 marked this conversation as resolved.
wheel_url, version = results[0]
return str(wheel_url), version


def resolve_all_prebuilt_wheels(
*,
ctx: context.WorkContext,
Expand All @@ -532,11 +553,6 @@ def resolve_all_prebuilt_wheels(
provider.cooldown = resolver.resolve_package_cooldown(
ctx, req, req_type=req_type
)
# The local fromager wheel server is PEP 503-only and serves
# packages that were already resolved and vetted earlier in the
# same run. Don't fail-closed on missing upload_time there.
if ctx.wheel_server_url and url == ctx.wheel_server_url:
provider.supports_upload_time = False

# Get all matching candidates from provider
results = resolver.find_all_matching_from_provider(provider, req)
Expand All @@ -559,10 +575,10 @@ def resolve_prebuilt_wheel(
wheel_server_urls: list[str],
req_type: requirements_file.RequirementType | None = None,
) -> tuple[str, Version]:
"""Return (URL, version) for the best matching wheel version.
"""Return (URL, version) for the best matching pre-built wheel.

Tries wheel servers in order and returns result from the first that succeeds.
Returns the highest matching version.
Tries wheel servers in order and returns the highest matching version
from the first server that succeeds.
"""
results = resolve_all_prebuilt_wheels(
ctx=ctx, req=req, wheel_server_urls=wheel_server_urls, req_type=req_type
Expand Down
Loading
Loading