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

feat: solvability implementation for rattler-build #58

Merged
merged 6 commits into from
Sep 23, 2024
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
29 changes: 26 additions & 3 deletions conda_forge_feedstock_check_solvable/check_solvable.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import conda_forge_feedstock_check_solvable.utils
from conda_forge_feedstock_check_solvable.mamba_solver import mamba_solver_factory
from conda_forge_feedstock_check_solvable.rattler_build import invoke_rattler_build
from conda_forge_feedstock_check_solvable.rattler_solver import rattler_solver_factory
from conda_forge_feedstock_check_solvable.utils import (
MAX_GLIBC_MINOR,
Expand Down Expand Up @@ -132,7 +133,12 @@ def _is_recipe_solvable(
print_warning(errors[-1])
return False, errors, {}

if not os.path.exists(os.path.join(feedstock_dir, "recipe", "meta.yaml")):
meta_exists = os.path.exists(os.path.join(feedstock_dir, "recipe", "meta.yaml"))
recipe_exists = os.path.exists(
os.path.join(feedstock_dir, "recipe", "recipe.yaml")
)

if not (meta_exists or recipe_exists):
errors.append(
"No `recipe/meta.yaml` file found! This issue is quite weird and "
"someone should investigate!",
Expand Down Expand Up @@ -187,8 +193,8 @@ def _is_recipe_solvable(


def _is_recipe_solvable_on_platform(
recipe_dir,
cbc_path,
recipe_dir: str,
cbc_path: str,
platform,
arch,
build_platform_arch=None,
Expand Down Expand Up @@ -258,6 +264,23 @@ def _is_recipe_solvable_on_platform(

timeout_timer.raise_for_timeout()

if os.path.exists(os.path.join(recipe_dir, "recipe.yaml")):
# this is a rattler-build recipe so we can invoke rattler-build with
# the new `cbc`.
solvable, errors = invoke_rattler_build(
recipe_dir,
channels=channel_sources,
build_platform=f"{platform}-{arch}",
host_platform=f"{platform}-{arch}",
variants=cbc,
)

if errors:
print_warning("Rattler build errors: %s", errors)
errors = [f"Rattler build errors: {errors}"]

return solvable, errors

# now we render the meta.yaml into an actual recipe
metas = conda_build_api_render(
recipe_dir,
Expand Down
63 changes: 63 additions & 0 deletions conda_forge_feedstock_check_solvable/rattler_build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import subprocess
import tempfile

import yaml

from conda_forge_feedstock_check_solvable.utils import print_debug
from conda_forge_feedstock_check_solvable.virtual_packages import (
virtual_package_repodata,
)


def run_rattler_build(command):
try:
# Run the command and capture output
print_debug("Running: ", " ".join(command))
result = subprocess.run(
" ".join(command), shell=True, check=False, capture_output=True, text=True
)

# Get the status code
status_code = result.returncode

# Get stdout and stderr
stdout = result.stdout.strip()
stderr = result.stderr.strip()

return status_code, stdout, stderr
except Exception as e:
return -1, "", str(e)


def invoke_rattler_build(
recipe_dir: str, channels, build_platform, host_platform, variants
) -> (bool, str):
# this is OK since there is an lru cache
virtual_package_repo_url = virtual_package_repodata()
# create a temporary file and dump the variants as YAML

with tempfile.NamedTemporaryFile(mode="w", delete=False) as variants_file:
yaml.dump(variants, variants_file)
variants_file.flush()

channels_args = []
for c in channels:
channels_args.extend(["-c", c])

channels_args.extend(["-c", virtual_package_repo_url])

args = (
["rattler-build", "build", "--recipe", recipe_dir]
+ channels_args
+ ["--target-platform", host_platform]
+ ["--build-platform", build_platform]
+ ["-m", variants_file.name]
+ ["--render-only", "--with-solve"]
)

status, out, err = run_rattler_build(args)

if status == 0:
return True, ""
else:
return False, out + err
33 changes: 33 additions & 0 deletions tests/test_check_solvable.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,3 +572,36 @@ def test_pillow_solvable(tmp_path, solver):
pprint.pprint(solvable_by_variant)
assert solvable, pprint.pformat(errors)
assert any("python3.10" in k for k in solvable_by_variant)


def test_jolt_physics_rattler(tmp_path):
"""test the new recipe format"""
feedstock_dir = clone_and_checkout_repo(
tmp_path,
"https://github.com/conda-forge/jolt-physics-feedstock",
ref="main",
)
solvable, errors, solvable_by_variant = is_recipe_solvable(
feedstock_dir,
solver="rattler",
verbosity=VERB,
timeout=None,
fail_fast=True,
)
pprint.pprint(solvable_by_variant)
assert solvable, pprint.pformat(errors)


def test_v1_unsolvable(tmp_path):
"""test an unsolvable recipe in the new format"""
feedstock_dir = os.path.join(os.path.dirname(__file__), "v1-unsolvable-feedstock")
solvable, errors, _ = is_recipe_solvable(
feedstock_dir,
solver="rattler",
verbosity=VERB,
timeout=None,
fail_fast=True,
)
assert solvable is False

print(errors[0])
4 changes: 4 additions & 0 deletions tests/v1-unsolvable-feedstock/.ci_support/osx_arm64_.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
python:
- 100
channel_sources:
- conda-forge
7 changes: 7 additions & 0 deletions tests/v1-unsolvable-feedstock/recipe/recipe.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package:
name: v1-unsolvable
version: "0.1"

requirements:
build:
- python