Skip to content
Open
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
24 changes: 24 additions & 0 deletions conda_forge_tick/make_migrators.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
CrossPythonMigrator,
CrossRBaseMigrator,
CrossRBaseWinMigrator,
CrossToNativeMigrator,
DependencyUpdateMigrator,
DuplicateLinesCleanup,
ExtraJinja2KeysCleanup,
Expand Down Expand Up @@ -968,6 +969,27 @@ def add_cdt_migrator(
migrators[-1].pr_limit = pr_limit


def add_cross_to_native_migrator(
migrators: MutableSequence[Migrator],
gx: nx.DiGraph,
):
with fold_log_lines("making cross-to-native migrator"):
migrators.append(
CrossToNativeMigrator(
total_graph=gx,
pr_limit=PR_LIMIT,
piggy_back_migrations=_make_mini_migrators_with_defaults(
extra_mini_migrators=[YAMLRoundTrip()],
),
)
)
pr_limit, _, _ = _compute_migrator_pr_limit(
migrators[-1],
PR_LIMIT,
)
migrators[-1].pr_limit = pr_limit


def _make_version_migrator(
gx: nx.DiGraph,
dry_run: bool = False,
Expand Down Expand Up @@ -1036,6 +1058,8 @@ def initialize_migrators(

add_cdt_migrator(migrators, gx)

add_cross_to_native_migrator(migrators, gx)

pinning_migrators: list[Migrator] = []
migration_factory(pinning_migrators, gx, _testing_frac=_testing_frac)
create_migration_yaml_creator(
Expand Down
3 changes: 3 additions & 0 deletions conda_forge_tick/migrators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
UpdateCMakeArgsWinMigrator,
UpdateConfigSubGuessMigrator,
)
from .cross_to_native import (
CrossToNativeMigrator,
)
from .cstdlib import StdlibMigrator
from .dep_updates import DependencyUpdateMigrator
from .duplicate_lines import DuplicateLinesCleanup
Expand Down
103 changes: 103 additions & 0 deletions conda_forge_tick/migrators/cross_to_native.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from pathlib import Path
from typing import Any

from conda_forge_tick.contexts import ClonedFeedstockContext, FeedstockContext
from conda_forge_tick.migrators.core import Migrator
from conda_forge_tick.migrators_types import (
AttrsTypedDict,
CondaForgeYamlContents,
MigrationUidTypedDict,
)
from conda_forge_tick.utils import (
yaml_safe_dump,
yaml_safe_load,
)


class CrossToNativeMigrator(Migrator):
name = "Cross-to-native Migrator"
rerender = True
migrator_version = 1

# platforms to migrate
_platforms = {"linux_aarch64"}
# build platforms where default = github_actions
_gha_platforms = {"linux_64"}

def _migrate_platform(self, platform: str, cfyaml: CondaForgeYamlContents) -> bool:
build_platform = cfyaml.get("build_platform", {}).get(platform)
if build_platform is None:
return False
provider = cfyaml.get("provider", {}).get(build_platform, "default")
return provider == "github_actions" or (
provider == "default" and build_platform in self._gha_platforms
)

def filter_not_in_migration(
self, attrs: AttrsTypedDict, not_bad_str_start: str = ""
) -> bool:
if super().filter_not_in_migration(attrs, not_bad_str_start):
return True

# TODO: check if there are any relevant cross-targets
cfyaml = attrs.get("conda-forge.yml", {})
for platform in self._platforms:
if self._migrate_platform(platform, cfyaml):
return False

return True

def migrate(
self, recipe_dir: str, attrs: AttrsTypedDict, **kwargs: Any
) -> MigrationUidTypedDict:
# Only v0 recipes are supported, the handful of v1 recipes is not worth the complexity.
recipe_file = next(
filter(
lambda x: x.exists(),
(Path(recipe_dir) / "recipe.yaml", Path(recipe_dir) / "meta.yaml"),
)
)
Comment thread
mgorny marked this conversation as resolved.
self.set_build_number(recipe_file)

cfyaml_path = Path(recipe_dir) / "../conda-forge.yml"
with open(cfyaml_path) as fp:
cfyaml = yaml_safe_load(fp)

for platform in self._platforms:
if not self._migrate_platform(platform, cfyaml):
continue

del cfyaml["build_platform"][platform]
cfyaml.setdefault("provider", {})[platform] = "default"

with open(cfyaml_path, "w") as fp:
yaml_safe_dump(cfyaml, fp)
return self.migrator_uid(attrs)

def pr_body(
self, feedstock_ctx: ClonedFeedstockContext, add_label_text: bool = True
) -> str:
body = super().pr_body(feedstock_ctx)
body = body.format(
f"""\
GitHub Actions provide native runners for {", ".join(self._platforms)} builds.
This migrator will attempt to switch from cross-compilation to native build.
"""
)
return body

def commit_message(self, feedstock_ctx: FeedstockContext) -> str:
return "Migrate cross-compilation to native build"

def pr_title(self, feedstock_ctx: FeedstockContext) -> str:
return "Migrate cross-compilation to native build"

def remote_branch(self, feedstock_ctx: FeedstockContext) -> str:
return f"cross-to-native-{self.migrator_version}"

def migrator_uid(self, attrs: AttrsTypedDict) -> MigrationUidTypedDict:
if self.name is None:
raise ValueError("name is None")
n = super().migrator_uid(attrs)
n["name"] = self.name
return n
123 changes: 123 additions & 0 deletions tests/test_cross_to_native.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import networkx as nx
import pytest
from test_migrators import run_test_migration

from conda_forge_tick.migrators import CrossToNativeMigrator

TOTAL_GRAPH = nx.DiGraph()
TOTAL_GRAPH.graph["outputs_lut"] = {}
cross_to_native_migrator = CrossToNativeMigrator(total_graph=TOTAL_GRAPH)


TEST_YAML = """\
{% set version = "1.2.3" %}
{% set build = @BUILD@ %}

package:
name: test
version: {{ version }}

build:
number: {{ build }}
"""


@pytest.mark.parametrize("provider", [None, "default", "github_actions"])
def test_cross_to_native(tmp_path, provider: str | None):
"""Test successfully migrating cross to native."""
input_yaml = """\
build_platform:
linux_aarch64: linux_64
linux_ppc64le: linux_64
osx_arm64: osx_64
win_arm64: win_64
"""
if provider is not None:
input_yaml += f"""\
provider:
linux_64: {provider}
"""

cfyaml = tmp_path / "conda-forge.yml"
cfyaml.write_text(input_yaml)

run_test_migration(
m=cross_to_native_migrator,
inp=TEST_YAML.replace("@BUILD@", "1"),
output=TEST_YAML.replace("@BUILD@", "2"),
prb="GitHub Actions provide native runners for linux_aarch64 builds",
kwargs={},
mr_out={
"migrator_name": "CrossToNativeMigrator",
"migrator_version": 1,
"name": "Cross-to-native Migrator",
},
tmp_path=tmp_path,
)

expected_providers = ""
if provider is not None:
expected_providers += f" linux_64: {provider}\n"

assert (
cfyaml.read_text()
== f"""\
build_platform:
linux_ppc64le: linux_64
osx_arm64: osx_64
win_arm64: win_64
provider:
{expected_providers}\
linux_aarch64: default
"""
)


@pytest.mark.parametrize("provider_platform", ["linux_64", "linux_aarch64"])
def test_no_cross(tmp_path, provider_platform: str):
"""Test package with no aarch64 build and with native aarch64 build."""
input_yaml = f"""\
provider:
{provider_platform}: default
"""

cfyaml = tmp_path / "conda-forge.yml"
cfyaml.write_text(input_yaml)

run_test_migration(
m=cross_to_native_migrator,
inp=TEST_YAML.replace("@BUILD@", "1"),
output="",
prb=None,
kwargs={},
mr_out=None,
tmp_path=tmp_path,
should_filter=True,
)


def test_azure(tmp_path):
"""Test package using non-GHA runner."""
input_yaml = """\
build_platform:
linux_aarch64: linux_64
linux_ppc64le: linux_64
osx_arm64: osx_64
win_arm64: win_64
provider:
linux_64: azure
"""

cfyaml = tmp_path / "conda-forge.yml"
cfyaml.write_text(input_yaml)

run_test_migration(
m=cross_to_native_migrator,
inp=TEST_YAML.replace("@BUILD@", "1"),
output="",
prb=None,
kwargs={},
mr_out=None,
tmp_path=tmp_path,
should_filter=True,
)
Loading