Skip to content

Add win-arm64 migrator #3194

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

Merged
merged 18 commits into from
Apr 21, 2025
Merged
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
23 changes: 22 additions & 1 deletion conda_forge_tick/make_migrators.py
Original file line number Diff line number Diff line change
@@ -45,11 +45,13 @@
CrossCompilationForARMAndPower,
CrossPythonMigrator,
CrossRBaseMigrator,
CrossRBaseWinMigrator,
DependencyUpdateMigrator,
DuplicateLinesCleanup,
ExtraJinja2KeysCleanup,
FlangMigrator,
GuardTestingMigrator,
GuardTestingWinMigrator,
Jinja2VarsCleanup,
LibboostMigrator,
LicenseMigrator,
@@ -69,12 +71,13 @@
StaticLibMigrator,
StdlibMigrator,
UpdateCMakeArgsMigrator,
UpdateCMakeArgsWinMigrator,
UpdateConfigSubGuessMigrator,
Version,
YAMLRoundTrip,
make_from_lazy_json_data,
)
from conda_forge_tick.migrators.arch import OSXArm
from conda_forge_tick.migrators.arch import OSXArm, WinArm64
from conda_forge_tick.migrators.migration_yaml import MigrationYamlCreator
from conda_forge_tick.os_utils import pushd
from conda_forge_tick.utils import (
@@ -270,6 +273,24 @@ def add_arch_migrate(migrators: MutableSequence[Migrator], gx: nx.DiGraph) -> No
),
)

with fold_log_lines("making win-arm64 migrator"):
migrators.append(
WinArm64(
total_graph=gx,
pr_limit=PR_LIMIT,
piggy_back_migrations=[
CondaForgeYAMLCleanup(),
UpdateCMakeArgsWinMigrator(),
GuardTestingWinMigrator(),
CrossRBaseWinMigrator(),
CrossPythonMigrator(),
NoCondaInspectMigrator(),
MPIPinRunAsBuildCleanup(),
CombineV1ConditionsMigrator(),
],
),
)


def add_rebuild_migration_yaml(
migrators: MutableSequence[Migrator],
5 changes: 4 additions & 1 deletion conda_forge_tick/migrators/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# flake8: noqa
from .arch import ArchRebuild, OSXArm
from .arch import ArchRebuild, OSXArm, WinArm64
from .broken_rebuild import RebuildBroken
from .conda_forge_yaml_cleanup import CondaForgeYAMLCleanup
from .core import (
@@ -15,9 +15,12 @@
CrossCompilationForARMAndPower,
CrossPythonMigrator,
CrossRBaseMigrator,
CrossRBaseWinMigrator,
GuardTestingMigrator,
GuardTestingWinMigrator,
NoCondaInspectMigrator,
UpdateCMakeArgsMigrator,
UpdateCMakeArgsWinMigrator,
UpdateConfigSubGuessMigrator,
)
from .cstdlib import StdlibMigrator
82 changes: 65 additions & 17 deletions conda_forge_tick/migrators/arch.py
Original file line number Diff line number Diff line change
@@ -223,31 +223,30 @@
return super().remote_branch(feedstock_ctx) + "_arch"


class OSXArm(GraphMigrator):
class _CrossCompileRebuild(GraphMigrator):
"""
A Migrator that add arm osx builds to feedstocks
A Migrator that adds arch platform builds to feedstocks
"""

migrator_version = 1
rerender = True
# We purposefully don't want to bump build number for this migrator
bump_number = 0

ignored_packages = set()
excluded_dependencies = set()

arches = ["osx_arm64"]

additional_keys = {
"build_platform": {"osx_arm64": "osx_64"},
"test": "native_and_emulated",
}
@property
def additional_keys(self):
return {

Check warning on line 240 in conda_forge_tick/migrators/arch.py

Codecov / codecov/patch

conda_forge_tick/migrators/arch.py#L240

Added line #L240 was not covered by tests
"build_platform": self.build_platform,
"test": "native_and_emulated",
}

def __init__(
self,
graph: nx.DiGraph | None = None,
name: str = "arm osx addition",
pr_limit: int = 0,
name: str = "",
piggy_back_migrations: Optional[Sequence[MiniMigrator]] = None,
target_packages: Optional[Sequence[str]] = None,
effective_graph: nx.DiGraph | None = None,
@@ -262,20 +261,23 @@
"share",
"conda-forge",
"migrations",
"osx_arm64.txt",
self.pkg_list_filename,
)
) as f:
target_packages = set(f.read().split())

outputs_lut = get_outputs_lut(total_graph, graph, effective_graph)

# rebuild the graph to only use edges from the arm osx requirements
# rebuild the graph to only use edges from the arch requirements
graph2 = nx.create_empty_copy(total_graph)
for node, attrs in total_graph.nodes(data="payload"):
for plat_arch in self.arches:
for plat_arch, build_plat_arch in self.build_platform.items():
reqs = attrs.get(
f"{plat_arch}_requirements",
attrs.get("osx_64_requirements", attrs.get("requirements", {})),
attrs.get(
f"{build_plat_arch}_requirements",
attrs.get("requirements", {}),
),
)
host_deps = set(as_iterable(reqs.get("host", set())))
run_deps = set(as_iterable(reqs.get("run", set())))
@@ -336,9 +338,7 @@
total_graph=total_graph,
name=name,
)
assert not self.check_solvable, (
"We don't want to check solvability for arm osx!"
)
assert not self.check_solvable, "We don't want to check solvability!"

def migrate(
self, recipe_dir: str, attrs: "AttrsTypedDict", **kwargs: Any
@@ -371,6 +371,20 @@

return muid


class OSXArm(_CrossCompileRebuild):
"""
A Migrator that add osx-arm64 builds to feedstocks
"""

migrator_version = 1
build_platform = {"osx_arm64": "osx_64"}
pkg_list_filename = "osx_arm64.txt"

def __init__(self, *args, **kwargs):
kwargs.setdefault("name", "arm osx addition")
super().__init__(*args, **kwargs)

def pr_title(self, feedstock_ctx: FeedstockContext) -> str:
return "ARM OSX Migrator"

@@ -390,3 +404,37 @@

def remote_branch(self, feedstock_ctx: FeedstockContext) -> str:
return super().remote_branch(feedstock_ctx) + "_arm_osx"


class WinArm64(_CrossCompileRebuild):
"""
A Migrator that add win-arm64 builds to feedstocks
"""

migrator_version = 1
build_platform = {"win_arm64": "win_64"}
pkg_list_filename = "win_arm64.txt"

def __init__(self, *args, **kwargs):
kwargs.setdefault("name", "support windows arm64 platform")
super().__init__(*args, **kwargs)

def pr_title(self, feedstock_ctx: FeedstockContext) -> str:
return "Support Windows ARM64 platform"

Check warning on line 423 in conda_forge_tick/migrators/arch.py

Codecov / codecov/patch

conda_forge_tick/migrators/arch.py#L423

Added line #L423 was not covered by tests

def pr_body(self, feedstock_ctx: ClonedFeedstockContext) -> str:
body = super().pr_body(feedstock_ctx)
body = body.format(

Check warning on line 427 in conda_forge_tick/migrators/arch.py

Codecov / codecov/patch

conda_forge_tick/migrators/arch.py#L426-L427

Added lines #L426 - L427 were not covered by tests
dedent(
"""\
This feedstock is being rebuilt as part of the windows arm migration.

**Feel free to merge the PR if CI is all green, but please don't close it
without reaching out the the ARM Windows team first at <code>@</code>conda-forge/help-win-arm64.**
""",
),
)
return body

Check warning on line 437 in conda_forge_tick/migrators/arch.py

Codecov / codecov/patch

conda_forge_tick/migrators/arch.py#L437

Added line #L437 was not covered by tests

def remote_branch(self, feedstock_ctx: FeedstockContext) -> str:
return super().remote_branch(feedstock_ctx) + "_arm64_win"

Check warning on line 440 in conda_forge_tick/migrators/arch.py

Codecov / codecov/patch

conda_forge_tick/migrators/arch.py#L440

Added line #L440 was not covered by tests
80 changes: 80 additions & 0 deletions conda_forge_tick/migrators/cross_compile.py
Original file line number Diff line number Diff line change
@@ -148,6 +148,38 @@
f.write("".join(lines))


class GuardTestingWinMigrator(CrossCompilationMigratorBase):
def migrate(self, recipe_dir: str, attrs: "AttrsTypedDict", **kwargs: Any) -> None:
with pushd(recipe_dir):
if not os.path.exists("bld.bat"):
return
with open("bld.bat") as f:
lines = list(f.readlines())

for i, line in enumerate(lines):
if "CONDA_BUILD_CROSS_COMPILATION" in line:
return

Check warning on line 161 in conda_forge_tick/migrators/cross_compile.py

Codecov / codecov/patch

conda_forge_tick/migrators/cross_compile.py#L161

Added line #L161 was not covered by tests
if (
line.strip().startswith("make check")
or line.strip().startswith("ctest")
or line.strip().startswith("make test")
):
lines.insert(i, "if not %CONDA_BUILD_SKIP_TESTS%==1 (\n")
insert_after = i + 1
while len(lines) > insert_after and lines[insert_after].endswith(
"\\\n",
):
insert_after += 1

Check warning on line 172 in conda_forge_tick/migrators/cross_compile.py

Codecov / codecov/patch

conda_forge_tick/migrators/cross_compile.py#L172

Added line #L172 was not covered by tests
if lines[insert_after][-1] != "\n":
lines[insert_after] += "\n"
lines.insert(insert_after + 1, ")\n")
break
else:
return

Check warning on line 178 in conda_forge_tick/migrators/cross_compile.py

Codecov / codecov/patch

conda_forge_tick/migrators/cross_compile.py#L178

Added line #L178 was not covered by tests
with open("bld.bat", "w") as f:
f.write("".join(lines))


class CrossPythonMigrator(MiniMigrator):
allowed_schema_versions = {0, 1}
post_migration = True
@@ -171,6 +203,8 @@
with open(recipe_file) as f:
lines = f.readlines()
in_reqs = False
if any("cross-python" in line for line in lines):
return

Check warning on line 207 in conda_forge_tick/migrators/cross_compile.py

Codecov / codecov/patch

conda_forge_tick/migrators/cross_compile.py#L207

Added line #L207 was not covered by tests
for i, line in enumerate(lines):
if line.strip().startswith("requirements:"):
in_reqs = True
@@ -259,6 +293,35 @@
f.write("".join(lines))


class UpdateCMakeArgsWinMigrator(CrossCompilationMigratorBase):
def filter(self, attrs: "AttrsTypedDict", not_bad_str_start: str = "") -> bool:
build_reqs = attrs.get("requirements", {}).get("build", set())
return "cmake" not in build_reqs or skip_migrator_due_to_schema(
attrs, self.allowed_schema_versions
)

def migrate(self, recipe_dir: str, attrs: "AttrsTypedDict", **kwargs: Any) -> None:
with pushd(recipe_dir):
if not os.path.exists("bld.bat"):
return
with open("bld.bat") as f:
lines = list(f.readlines())

for i, line in enumerate(lines):
if "%CMAKE_ARGS%" in line:
return

Check warning on line 312 in conda_forge_tick/migrators/cross_compile.py

Codecov / codecov/patch

conda_forge_tick/migrators/cross_compile.py#L312

Added line #L312 was not covered by tests

for i, line in enumerate(lines):
if line.startswith("cmake "):
lines[i] = "cmake %CMAKE_ARGS% " + line[len("cmake ") :]
break
else:
return

Check warning on line 319 in conda_forge_tick/migrators/cross_compile.py

Codecov / codecov/patch

conda_forge_tick/migrators/cross_compile.py#L319

Added line #L319 was not covered by tests

with open("bld.bat", "w") as f:
f.write("".join(lines))


class Build2HostMigrator(MiniMigrator):
allowed_schema_versions = {0, 1}
post_migration = False
@@ -345,6 +408,12 @@
"""


CRAN_BLD_BAT = """\
"%R%" CMD INSTALL --build . %R_ARGS%
IF %ERRORLEVEL% NEQ 0 exit 1
"""


class CrossRBaseMigrator(MiniMigrator):
allowed_schema_versions = {0, 1}
post_migration = True
@@ -421,6 +490,17 @@
f.write(CRAN_BUILD_SH)


class CrossRBaseWinMigrator(CrossRBaseMigrator):
allowed_schema_versions = {0, 1}
post_migration = True

def migrate(self, recipe_dir: str, attrs: "AttrsTypedDict", **kwargs: Any) -> None:
with pushd(recipe_dir):
if os.path.exists("bld.bat"):
with open("bld.bat", "w") as f:
f.write(CRAN_BLD_BAT)

Check warning on line 501 in conda_forge_tick/migrators/cross_compile.py

Codecov / codecov/patch

conda_forge_tick/migrators/cross_compile.py#L498-L501

Added lines #L498 - L501 were not covered by tests


class CrossCompilationForARMAndPower(MiniMigrator):
allowed_schema_versions = {0, 1}
post_migration = True
1 change: 1 addition & 0 deletions conda_forge_tick/models/pr_info.py
Original file line number Diff line number Diff line change
@@ -53,6 +53,7 @@ class MigratorName(StrEnum):
VERSION = "Version"
ARCH_REBUILD = "ArchRebuild"
OSX_ARM = "OSXArm"
WIN_ARM64 = "WinArm64"
MIGRATION_YAML = "MigrationYaml"
REBUILD = "Rebuild"
BLAS_REBUILD = "BlasRebuild"
2 changes: 2 additions & 0 deletions conda_forge_tick/status_report.py
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@
OSXArm,
Replacement,
Version,
WinArm64,
)
from conda_forge_tick.os_utils import eval_cmd
from conda_forge_tick.path_lengths import cyclic_topological_sort
@@ -470,6 +471,7 @@ def main() -> None:
mgconf.get("longterm", False)
or isinstance(migrator, ArchRebuild)
or isinstance(migrator, OSXArm)
or isinstance(migrator, WinArm64)
):
longterm_status[migrator_name] = f"{migrator.name} Migration Status"
else:
29 changes: 26 additions & 3 deletions tests/test_cross_compile.py
Original file line number Diff line number Diff line change
@@ -11,8 +11,10 @@
CrossPythonMigrator,
CrossRBaseMigrator,
GuardTestingMigrator,
GuardTestingWinMigrator,
NoCondaInspectMigrator,
UpdateCMakeArgsMigrator,
UpdateCMakeArgsWinMigrator,
UpdateConfigSubGuessMigrator,
Version,
)
@@ -28,7 +30,9 @@

config_migrator = UpdateConfigSubGuessMigrator()
guard_testing_migrator = GuardTestingMigrator()
guard_testing_win_migrator = GuardTestingWinMigrator()
cmake_migrator = UpdateCMakeArgsMigrator()
cmake_win_migrator = UpdateCMakeArgsWinMigrator()
cross_python_migrator = CrossPythonMigrator()
cross_rbase_migrator = CrossRBaseMigrator()
b2h_migrator = Build2HostMigrator()
@@ -37,14 +41,22 @@

version_migrator_autoconf = Version(
set(),
piggy_back_migrations=[config_migrator, cmake_migrator, guard_testing_migrator],
piggy_back_migrations=[
config_migrator,
cmake_migrator,
cmake_win_migrator,
guard_testing_migrator,
guard_testing_win_migrator,
],
total_graph=TOTAL_GRAPH,
)
version_migrator_cmake = Version(
set(),
piggy_back_migrations=[
cmake_migrator,
cmake_win_migrator,
guard_testing_migrator,
guard_testing_win_migrator,
cross_rbase_migrator,
cross_python_migrator,
],
@@ -135,6 +147,8 @@ def test_cmake(tmp_path):
tmp_path.joinpath("recipe").mkdir()
with open(tmp_path / "recipe/build.sh", "w") as f:
f.write("#!/bin/bash\ncmake ..\nctest")
with open(tmp_path / "recipe/bld.bat", "w") as f:
f.write("cmake ..\nctest")
run_test_migration(
m=version_migrator_cmake,
inp=YAML_PATH.joinpath("config_recipe.yaml").read_text(),
@@ -148,16 +162,25 @@ def test_cmake(tmp_path):
},
tmp_path=tmp_path,
)
expected = [
expected_unix = [
"#!/bin/bash\n",
"cmake ${CMAKE_ARGS} ..\n",
'if [[ "${CONDA_BUILD_CROSS_COMPILATION:-}" != "1" || "${CROSSCOMPILING_EMULATOR}" != "" ]]; then\n',
"ctest\n",
"fi\n",
]
expected_win = [
"cmake %CMAKE_ARGS% ..\n",
"if not %CONDA_BUILD_SKIP_TESTS%==1 (\n",
"ctest\n",
")\n",
]
with open(tmp_path / "recipe/build.sh") as f:
lines = f.readlines()
assert lines == expected
assert lines == expected_unix
with open(tmp_path / "recipe/bld.bat") as f:
lines = f.readlines()
assert lines == expected_win


@pytest.mark.parametrize("recipe_version", [0, 1])
27 changes: 27 additions & 0 deletions tests/test_migrator_to_json.py
Original file line number Diff line number Diff line change
@@ -272,6 +272,33 @@ def test_migrator_to_json_osx_arm():
assert dumps(migrator2.to_lazy_json_data()) == lzj_data


def test_migrator_to_json_win_arm64():
gx = nx.DiGraph()
gx.add_node("conda", reqs=["python"], payload={}, blah="foo")
gx.graph["outputs_lut"] = {}

migrator = conda_forge_tick.migrators.WinArm64(
target_packages=["python"],
total_graph=gx,
pr_limit=5,
)

data = migrator.to_lazy_json_data()
pprint.pprint(data)
lzj_data = dumps(data)
print("lazy json data:\n", lzj_data)
assert data["__migrator__"] is True
assert data["class"] == "WinArm64"
assert data["name"] == "support_windows_arm64_platform"

migrator2 = make_from_lazy_json_data(loads(lzj_data))
assert [pgm.__class__.__name__ for pgm in migrator2.piggy_back_migrations] == [
pgm.__class__.__name__ for pgm in migrator.piggy_back_migrations
]
assert isinstance(migrator2, conda_forge_tick.migrators.WinArm64)
assert dumps(migrator2.to_lazy_json_data()) == lzj_data


@pytest.mark.parametrize(
"klass",
[