1515from tempfile import TemporaryDirectory
1616from typing import Any , Literal
1717
18- from packaging .tags import cpython_tags , mac_platforms
18+ from packaging .tags import cpython_tags , mac_platforms , platform_tags
1919from pydantic import BaseModel , ConfigDict , Field , PrivateAttr , field_validator
2020
2121__all__ = (
@@ -102,13 +102,18 @@ class CopiedArtifact:
102102 "i686" : "i686-unknown-linux-musl" ,
103103 "aarch64" : "aarch64-unknown-linux-musl" ,
104104 "armv7" : "armv7-unknown-linux-musleabihf" ,
105+ "loongarch64" : "loongarch64-unknown-linux-musl" ,
106+ "ppc64le" : "powerpc64le-unknown-linux-musl" ,
107+ "s390x" : "s390x-unknown-linux-musl" ,
108+ "riscv64" : "riscv64gc-unknown-linux-musl" ,
105109}
106110
107111WHEEL_ARCHES = {
108112 "x86_64" : "x86_64" ,
109113 "i686" : "i686" ,
110114 "aarch64" : "aarch64" ,
111115 "armv7" : "armv7l" ,
116+ "loongarch64" : "loongarch64" ,
112117 "ppc64le" : "ppc64le" ,
113118 "s390x" : "s390x" ,
114119 "riscv64" : "riscv64" ,
@@ -122,6 +127,16 @@ def _env_truthy(value: str | None) -> bool:
122127 return value .strip ().lower () not in ("" , "0" , "false" , "no" , "off" )
123128
124129
130+ def _is_musl_linux () -> bool :
131+ """Return True if running in a musl Linux environment or container."""
132+ auditwheel = environ .get ("AUDITWHEEL_PLAT" , "" )
133+ if auditwheel .startswith ("musllinux" ):
134+ return True
135+ if sys_platform == "linux" :
136+ return any (tag .startswith ("musllinux" ) for tag in platform_tags ())
137+ return False
138+
139+
125140def _normalize_machine (machine : str ) -> str :
126141 normalized = machine .lower ().replace ("-" , "_" )
127142 aliases = {
@@ -147,15 +162,15 @@ def _normalize_platform(platform: str) -> str:
147162 return "win32"
148163 if normalized .startswith ("macosx" ) or normalized == "darwin" :
149164 return "darwin"
150- if normalized .startswith (("linux" , "manylinux" , "musllinux " )):
165+ if normalized .startswith (("linux" , "manylinux" , "musl " )):
151166 return "linux"
152167 if normalized .startswith (("emscripten" , "pyemscripten" )):
153168 return "emscripten"
154169 return normalized
155170
156171
157172def _linux_targets_for_platform (platform : str ) -> dict [str , str ]:
158- if platform .lower ().startswith ("musllinux " ):
173+ if platform .lower ().startswith ("musl " ):
159174 return LINUX_MUSL_TARGETS
160175 return LINUX_GNU_TARGETS
161176
@@ -236,7 +251,9 @@ def python_extension_name(
236251
237252
238253def _resolve_target (target : str | None = None , * , platform : str | None = None , machine : str | None = None ) -> ResolvedTarget :
239- raw_platform = platform or environ .get ("HATCH_RUST_PLATFORM" , sys_platform )
254+ raw_platform = platform or environ .get ("HATCH_RUST_PLATFORM" )
255+ if raw_platform is None :
256+ raw_platform = "musllinux" if _is_musl_linux () else sys_platform
240257 platform = _normalize_platform (raw_platform )
241258 machine = _normalize_machine (machine or environ .get ("HATCH_RUST_MACHINE" , platform_machine ()))
242259 target = target or environ .get ("CARGO_BUILD_TARGET" )
@@ -283,6 +300,12 @@ def _linux_wheel_platform(resolved_target: ResolvedTarget, platform_tag: str | N
283300 if arch is None :
284301 raise _unsupported_machine ("Linux wheel" , resolved_target .machine , WHEEL_ARCHES )
285302 if "musl" in resolved_target .triple :
303+ detected_platform = next (
304+ (tag for tag in platform_tags () if tag .startswith ("musllinux_" ) and tag .endswith (f"_{ arch } " )),
305+ None ,
306+ )
307+ if detected_platform is not None :
308+ return detected_platform
286309 return f"musllinux_1_2_{ arch } "
287310 return f"linux_{ arch } "
288311
@@ -857,13 +880,36 @@ def _build_artifact_plan(self, artifact: RustArtifactConfig, *, global_target: s
857880 build_command .append ("--frozen" )
858881 build_command .extend (self ._artifact_cargo_args (artifact ))
859882
883+ user_rustc_args = self ._artifact_rustc_args (artifact )
884+ user_crate_types = []
885+ for index , argument in enumerate (user_rustc_args ):
886+ if argument == "--crate-type" and index + 1 < len (user_rustc_args ):
887+ user_crate_types .extend (user_rustc_args [index + 1 ].split ("," ))
888+ elif argument .startswith ("--crate-type=" ):
889+ user_crate_types .extend (argument .split ("=" , 1 )[1 ].split ("," ))
890+ crate_types = {crate_type .strip () for crate_type in (user_crate_types or artifact .crate_type .split ("," ))}
891+
860892 rustc_args = []
861893 if self ._is_python_extension_artifact (artifact ) and "apple" in resolved_target .triple :
862894 rustc_args .extend (("-C" , "link-arg=-undefined" , "-C" , "link-arg=dynamic_lookup" ))
863- rustc_args .extend (self ._artifact_rustc_args (artifact ))
895+ if (
896+ "musl" in resolved_target .triple
897+ and not self ._is_executable_artifact (artifact )
898+ and (not crate_types .isdisjoint (("cdylib" , "dylib" , "proc-macro" )) or self ._is_python_extension_artifact (artifact ))
899+ ):
900+ target_features = (
901+ feature .strip ().lstrip ("+-" )
902+ for argument in user_rustc_args
903+ if argument .startswith (("target-feature=" , "-Ctarget-feature=" , "--codegen=target-feature=" ))
904+ for feature in argument .split ("target-feature=" , 1 )[1 ].split ("," )
905+ )
906+ if "crt-static" not in target_features :
907+ rustc_args .extend (("-C" , "target-feature=-crt-static" ))
908+ rustc_args .extend (user_rustc_args )
864909 # Executables (bin/example) are not crate-type artifacts; injecting
865910 # --crate-type would build them as a library instead of a binary.
866- if "--crate-type" not in rustc_args and not self ._is_executable_artifact (artifact ):
911+ has_crate_type_arg = any (argument == "--crate-type" or argument .startswith ("--crate-type=" ) for argument in user_rustc_args )
912+ if not has_crate_type_arg and not self ._is_executable_artifact (artifact ):
867913 rustc_args .extend (("--crate-type" , artifact .crate_type ))
868914 if rustc_args :
869915 build_command .append ("--" )
0 commit comments