@@ -16,69 +16,46 @@ package python
1616
1717import (
1818 "fmt"
19- "slices"
2019 "strings"
2120
2221 "chainguard.dev/apko/pkg/build/types"
2322)
2423
25- // platformTags returns the list of compatible wheel platform tags for the
26- // given architecture, ordered from most specific to least specific.
27- func platformTags (arch types.Architecture ) []string {
28- switch arch {
29- case types .ParseArchitecture ("amd64" ):
30- return []string {
31- "manylinux_2_17_x86_64" ,
32- "manylinux2014_x86_64" ,
33- "manylinux_2_5_x86_64" ,
34- "manylinux1_x86_64" ,
35- "linux_x86_64" ,
36- }
37- case types .ParseArchitecture ("arm64" ):
38- return []string {
39- "manylinux_2_17_aarch64" ,
40- "manylinux2014_aarch64" ,
41- "linux_aarch64" ,
42- }
43- case types .ParseArchitecture ("arm/v7" ):
44- return []string {
45- "manylinux_2_17_armv7l" ,
46- "manylinux2014_armv7l" ,
47- "linux_armv7l" ,
48- }
49- case types .ParseArchitecture ("arm/v6" ):
50- return []string {
51- "manylinux_2_17_armv6l" ,
52- "linux_armv6l" ,
53- }
54- case types .ParseArchitecture ("386" ):
55- return []string {
56- "manylinux_2_17_i686" ,
57- "manylinux2014_i686" ,
58- "manylinux_2_5_i686" ,
59- "manylinux1_i686" ,
60- "linux_i686" ,
61- }
62- case types .ParseArchitecture ("ppc64le" ):
63- return []string {
64- "manylinux_2_17_ppc64le" ,
65- "manylinux2014_ppc64le" ,
66- "linux_ppc64le" ,
67- }
68- case types .ParseArchitecture ("s390x" ):
69- return []string {
70- "manylinux_2_17_s390x" ,
71- "manylinux2014_s390x" ,
72- "linux_s390x" ,
73- }
74- case types .ParseArchitecture ("riscv64" ):
75- return []string {
76- "manylinux_2_17_riscv64" ,
77- "linux_riscv64" ,
78- }
79- default :
80- return []string {"any" }
24+ // archToMachine maps OCI architecture strings to the Python/Linux machine
25+ // string used in wheel platform tags.
26+ var archToMachine = map [types.Architecture ]string {
27+ types .ParseArchitecture ("amd64" ): "x86_64" ,
28+ types .ParseArchitecture ("arm64" ): "aarch64" ,
29+ types .ParseArchitecture ("arm/v7" ): "armv7l" ,
30+ types .ParseArchitecture ("arm/v6" ): "armv6l" ,
31+ types .ParseArchitecture ("386" ): "i686" ,
32+ types .ParseArchitecture ("ppc64le" ): "ppc64le" ,
33+ types .ParseArchitecture ("s390x" ): "s390x" ,
34+ types .ParseArchitecture ("riscv64" ): "riscv64" ,
35+ types .ParseArchitecture ("loong64" ): "loongarch64" ,
36+ }
37+
38+ // isLinuxPlatformTag checks whether a single platform tag (e.g.
39+ // "musllinux_1_2_x86_64") targets the given machine architecture and
40+ // is compatible with the image's libc. musl images only accept musllinux
41+ // wheels; glibc images only accept manylinux wheels.
42+ func isLinuxPlatformTag (tag , machine string , libc string ) bool {
43+ if ! strings .HasSuffix (tag , "_" + machine ) {
44+ return false
8145 }
46+ if tag == "linux_" + machine {
47+ return true
48+ }
49+ if libc == "musl" {
50+ return strings .HasPrefix (tag , "musllinux_" )
51+ }
52+ return strings .HasPrefix (tag , "manylinux" )
53+ }
54+
55+ // isBinaryWheel returns true if the wheel targets a specific platform
56+ // (not pure-python "any").
57+ func isBinaryWheel (w wheelFileParts ) bool {
58+ return w .PlatformTag != "any"
8259}
8360
8461// wheelFileParts holds the parsed components of a wheel filename per PEP 427.
@@ -124,8 +101,8 @@ func parseWheelFilename(filename string) (wheelFileParts, error) {
124101}
125102
126103// isCompatibleWheel checks whether a wheel file is compatible with the given
127- // Python version and architecture .
128- func isCompatibleWheel (w wheelFileParts , pythonVersion string , arch types.Architecture ) bool {
104+ // Python version, architecture, and libc .
105+ func isCompatibleWheel (w wheelFileParts , pythonVersion string , arch types.Architecture , libc string ) bool {
129106 // Check python tag compatibility
130107 if ! isCompatiblePythonTag (w .PythonTag , pythonVersion ) {
131108 return false
@@ -137,7 +114,7 @@ func isCompatibleWheel(w wheelFileParts, pythonVersion string, arch types.Archit
137114 }
138115
139116 // Check platform compatibility
140- return isCompatiblePlatform (w .PlatformTag , arch )
117+ return isCompatiblePlatform (w .PlatformTag , arch , libc )
141118}
142119
143120// isCompatiblePythonTag checks if the wheel's python tag is compatible.
@@ -166,56 +143,29 @@ func isCompatibleABI(tag, pythonVersion string) bool {
166143 return false
167144}
168145
169- // isCompatiblePlatform checks if the wheel's platform tag is compatible.
170- func isCompatiblePlatform (tag string , arch types.Architecture ) bool {
146+ // isCompatiblePlatform checks if the wheel's platform tag is compatible
147+ // with the given architecture and libc, without version limits.
148+ func isCompatiblePlatform (tag string , arch types.Architecture , libc string ) bool {
171149 if tag == "any" {
172150 return true
173151 }
174- compatible := platformTags (arch )
152+ machine , ok := archToMachine [arch ]
153+ if ! ok {
154+ return false
155+ }
175156 for t := range strings .SplitSeq (tag , "." ) {
176- if slices . Contains ( compatible , t ) {
157+ if isLinuxPlatformTag ( t , machine , libc ) {
177158 return true
178159 }
179160 }
180161 return false
181162}
182163
183- // wheelScore returns a priority score for the wheel. Higher is better.
184- // Binary wheels for the exact platform are preferred over pure-Python wheels.
185- func wheelScore (w wheelFileParts , pythonVersion string , arch types.Architecture ) int {
186- score := 0
187-
188- // Prefer exact CPython tag over generic py3
189- cpTag := "cp" + strings .ReplaceAll (pythonVersion , "." , "" )
190- for t := range strings .SplitSeq (w .PythonTag , "." ) {
191- if t == cpTag {
192- score += 100
193- break
194- }
195- }
196-
197- // Prefer specific ABI over none/abi3
198- for t := range strings .SplitSeq (w .ABITag , "." ) {
199- switch t {
200- case cpTag :
201- score += 50
202- case "abi3" :
203- score += 25
204- }
205- }
206-
207- // Prefer specific platform over any
208- if w .PlatformTag != "any" {
209- platTags := platformTags (arch )
210- for i , pt := range platTags {
211- for pp := range strings .SplitSeq (w .PlatformTag , "." ) {
212- if pp == pt {
213- // More specific platforms (earlier in list) get higher scores
214- score += 10 * (len (platTags ) - i )
215- }
216- }
217- }
164+ // isBetterWheel returns true if candidate is a better choice than current.
165+ // Prefers binary wheels over pure-python.
166+ func isBetterWheel (current , candidate wheelFileParts ) bool {
167+ if ! isBinaryWheel (current ) && isBinaryWheel (candidate ) {
168+ return true
218169 }
219-
220- return score
170+ return false
221171}
0 commit comments