1111from buildcheck .tests .helpers import REPO , upload , workflow_text
1212
1313CSV = """# a comment
14- classifier,directory,library,platform,kind
15- cpu-linux-x86-64,Linux/x86_64/cpu,libjllama.so,yes,library
16- cuda13-linux-x86-64,Linux/x86_64/cuda13,libggml-cuda.so,no,module
17- cpu-linux-s390x,Linux/s390x/cpu,libjllama.so,yes,library
18- cpu-windows-aarch64,Windows/aarch64/cpu,jllama.dll,yes,library
19- vulkan-windows-aarch64,Windows/aarch64/vulkan,ggml-vulkan.dll,no,module
20- cpu-android-aarch64,Linux-Android/aarch64/cpu,libjllama.so,no,library
21- opencl-android-aarch64,Linux-Android/aarch64/opencl,libggml-opencl.so,no,module
22- metal-macos-aarch64,Mac/aarch64/metal,libjllama.dylib,yes,library
14+ classifier,directory,library,platform,kind,optin
15+ cpu-linux-x86-64,Linux/x86_64/cpu,libjllama.so,yes,library,no
16+ cuda13-linux-x86-64,Linux/x86_64/cuda13,libggml-cuda.so,no,module,no
17+ cpu-linux-s390x,Linux/s390x/cpu,libjllama.so,yes,library,no
18+ cpu-windows-aarch64,Windows/aarch64/cpu,jllama.dll,yes,library,no
19+ vulkan-windows-aarch64,Windows/aarch64/vulkan,ggml-vulkan.dll,no,module,no
20+ cpu-android-aarch64,Linux-Android/aarch64/cpu,libjllama.so,no,library,no
21+ opencl-android-aarch64,Linux-Android/aarch64/opencl,libggml-opencl.so,no,module,yes
22+ metal-macos-aarch64,Mac/aarch64/metal,libjllama.dylib,yes,library,no
2323"""
2424
2525ROWS = natives .rows (CSV )
@@ -67,7 +67,8 @@ class ListTest(unittest.TestCase):
6767
6868 def test_rows_skip_comments (self ):
6969 self .assertEqual (ROWS [0 ], {"classifier" : "cpu-linux-x86-64" , "directory" : "Linux/x86_64/cpu" ,
70- "library" : "libjllama.so" , "platform" : "yes" , "kind" : "library" })
70+ "library" : "libjllama.so" , "platform" : "yes" , "kind" : "library" ,
71+ "optin" : "no" })
7172 self .assertEqual (len (ROWS ), 8 )
7273 self .assertEqual ([r ["classifier" ] for r in natives .modules (ROWS )],
7374 ["cuda13-linux-x86-64" , "vulkan-windows-aarch64" , "opencl-android-aarch64" ])
@@ -86,10 +87,10 @@ def test_smoke_targets_are_the_desktop_library_platforms(self):
8687
8788 def test_check_rows (self ):
8889 self .assertEqual (natives .check_rows (ROWS ), [])
89- bad = natives .rows ("classifier,directory,library,platform,kind\n "
90- "cuda-linux-x86-64,Linux/x86_64/cuda13,x,no,module\n "
91- "cpu-a,A/b/cpu,libjllama.so,maybe,library\n "
92- "cpu-a,A/b/cpu,libjllama.so,no,thing\n " )
90+ bad = natives .rows ("classifier,directory,library,platform,kind,optin \n "
91+ "cuda-linux-x86-64,Linux/x86_64/cuda13,x,no,module,no \n "
92+ "cpu-a,A/b/cpu,libjllama.so,maybe,library,no \n "
93+ "cpu-a,A/b/cpu,libjllama.so,no,thing,no \n " )
9394 failures = natives .check_rows (bad )
9495 self .assertEqual (len (failures ), 7 , failures )
9596 self .assertIn ("twice" , failures [0 ])
@@ -102,20 +103,20 @@ def test_check_rows(self):
102103 self .assertTrue (any (expected in f for f in failures ), expected )
103104
104105 def test_check_rows_refuses_a_module_without_a_library_and_colliding_file_names (self ):
105- orphan = natives .rows ("classifier,directory,library,platform,kind\n "
106- "vulkan-linux-aarch64,Linux/aarch64/vulkan,libggml-vulkan.so,no,module\n " )
106+ orphan = natives .rows ("classifier,directory,library,platform,kind,optin \n "
107+ "vulkan-linux-aarch64,Linux/aarch64/vulkan,libggml-vulkan.so,no,module,no \n " )
107108 self .assertEqual (natives .check_rows (orphan ),
108109 ["natives.csv: Linux/aarch64 has module jars but no library jar -- nothing would load them" ])
109- twice = natives .rows ("classifier,directory,library,platform,kind\n "
110- "cpu-linux-x86-64,Linux/x86_64/cpu,libjllama.so,yes,library\n "
111- "sycl-linux-x86-64,Linux/x86_64/sycl,libggml-sycl.so,no,module\n "
112- "sycl-fp32-linux-x86-64,Linux/x86_64/sycl-fp32,libggml-sycl.so,no,module\n " )
110+ twice = natives .rows ("classifier,directory,library,platform,kind,optin \n "
111+ "cpu-linux-x86-64,Linux/x86_64/cpu,libjllama.so,yes,library,no \n "
112+ "sycl-linux-x86-64,Linux/x86_64/sycl,libggml-sycl.so,no,module,no \n "
113+ "sycl-fp32-linux-x86-64,Linux/x86_64/sycl-fp32,libggml-sycl.so,no,module,no \n " )
113114 failures = natives .check_rows (twice )
114115 self .assertEqual (len (failures ), 1 , failures )
115116 self .assertIn ("same name" , failures [0 ])
116- platform_module = natives .rows ("classifier,directory,library,platform,kind\n "
117- "cpu-linux-x86-64,Linux/x86_64/cpu,libjllama.so,yes,library\n "
118- "cuda13-linux-x86-64,Linux/x86_64/cuda13,libggml-cuda.so,yes,module\n " )
117+ platform_module = natives .rows ("classifier,directory,library,platform,kind,optin \n "
118+ "cpu-linux-x86-64,Linux/x86_64/cpu,libjllama.so,yes,library,no \n "
119+ "cuda13-linux-x86-64,Linux/x86_64/cuda13,libggml-cuda.so,yes,module,no \n " )
119120 self .assertIn ("cannot be a platform jar" , natives .check_rows (platform_module )[0 ])
120121
121122
@@ -170,22 +171,54 @@ def test_a_matrix_launches_its_rows(self):
170171 ["publish.yml smoke runs (a job downloading llama-smoke-<target>): missing windows-aarch64" ])
171172
172173 def test_a_module_jar_without_a_smoke_target_is_refused (self ):
173- rows = natives .rows (CSV + "vulkan-linux-s390x,Linux/s390x/vulkan,libggml-vulkan.so,no,module\n " )
174+ rows = natives .rows (CSV + "vulkan-linux-s390x,Linux/s390x/vulkan,libggml-vulkan.so,no,module,no \n " )
174175 failures = natives .check_workflow (rows , jobs (rows = rows ))
175176 self .assertEqual (failures , ["natives.csv: the module jar vulkan-linux-s390x has no smoke target -- it would "
176177 "ship unlaunched" ])
177178
178179 def test_loader (self ):
180+ # The fixture declares opencl opt-in (see CSV), so the loader text has to name it.
179181 text = ('List<String> LIBRARY_BACKENDS = Arrays.asList("metal", "cpu");\n '
180- 'List<String> MODULE_BACKENDS = Arrays.asList(\n "cuda13", "vulkan",\n "opencl");' )
182+ 'List<String> MODULE_BACKENDS = Arrays.asList(\n "cuda13", "vulkan",\n "opencl");\n '
183+ 'Set<String> OPT_IN_BACKENDS = Collections.singleton("opencl");' )
181184 self .assertEqual (natives .check_loader (ROWS , text ), [])
182185 self .assertEqual (natives .check_loader (ROWS , text .replace ('"cuda13", ' , "" )),
183186 ["LlamaLoader.MODULE_BACKENDS does not know 'cuda13' -- its jars would never load" ])
184- self .assertEqual (natives .check_loader (ROWS , text .replace ('"opencl"' , '"opencl", "msvc"' )),
185- ["LlamaLoader.MODULE_BACKENDS names 'msvc', which no module jar of natives.csv ships" ])
187+ self .assertEqual (
188+ natives .check_loader (ROWS , text .replace ('"opencl");' + chr (10 ), '"opencl", "msvc");' + chr (10 ))),
189+ ["LlamaLoader.MODULE_BACKENDS names 'msvc', which no module jar of natives.csv ships" ])
186190 self .assertEqual (natives .check_loader (ROWS , text .replace ('"metal", ' , "" )),
187191 ["LlamaLoader.LIBRARY_BACKENDS does not know 'metal' -- its jars would never load" ])
188192
193+ # The opt-in column and the loader constant have to agree in both directions. A backend the
194+ # list calls opt-in while the loader does not would ship a jar that takes over silently --
195+ # the measured OpenVINO regression the column exists for -- and the other way round a jar
196+ # would be inert with no row saying why.
197+ self .assertEqual (natives .check_loader (ROWS , text .replace ('Collections.singleton("opencl")' , "emptySet()" )),
198+ ["LlamaLoader.OPT_IN_BACKENDS does not know 'opencl', which natives.csv declares opt-in" ])
199+ self .assertEqual (
200+ natives .check_loader (ROWS , text .replace ('singleton("opencl")' , 'asList("opencl", "vulkan")' )),
201+ ["LlamaLoader.OPT_IN_BACKENDS names 'vulkan', which no row of natives.csv declares opt-in" ])
202+
203+ def test_check_rows_holds_the_optin_column (self ):
204+ missing = natives .rows ("classifier,directory,library,platform,kind,optin\n "
205+ "cpu-linux-x86-64,Linux/x86_64/cpu,libjllama.so,yes,library\n " )
206+ self .assertTrue (any ("optin must be yes or no" in f for f in natives .check_rows (missing )))
207+
208+ # Only a module can be opt-in: a library jar is the one jar that has to load.
209+ library = natives .rows ("classifier,directory,library,platform,kind,optin\n "
210+ "cpu-linux-x86-64,Linux/x86_64/cpu,libjllama.so,yes,library,yes\n " )
211+ self .assertTrue (any ("only a module can be opt-in" in f for f in natives .check_rows (library )))
212+
213+ # Opt-in is decided per backend, so two jars of one backend cannot disagree -- one of them
214+ # would be inert and the other not, which no reader would expect.
215+ split = natives .rows ("classifier,directory,library,platform,kind,optin\n "
216+ "cpu-linux-x86-64,Linux/x86_64/cpu,libjllama.so,yes,library,no\n "
217+ "cpu-windows-x86-64,Windows/x86_64/cpu,jllama.dll,yes,library,no\n "
218+ "openvino-linux-x86-64,Linux/x86_64/openvino,libggml-openvino.so,no,module,yes\n "
219+ "openvino-windows-x86-64,Windows/x86_64/openvino,ggml-openvino.dll,no,module,no\n " )
220+ self .assertTrue (any ("is opt-in in one row and not in another" in f for f in natives .check_rows (split )))
221+
189222 def test_cmake_both_directions (self ):
190223 text = "\n " .join (f" set(JLLAMA_BACKEND { b } )" for b in ("cuda13" , "cpu" , "vulkan" , "opencl" , "metal" ))
191224 self .assertEqual (natives .check_cmake (ROWS , text ), [])
0 commit comments