99
1010import argparse
1111from dataclasses import dataclass
12- import fcntl
1312import glob
1413import hashlib
15- import io
1614import json
1715import os
1816import platform
@@ -143,6 +141,14 @@ def subprocess_output(self):
143141 return sys .stdout
144142 return subprocess .DEVNULL
145143
144+ def check_call (self , args , ** kwargs ):
145+ self .log_info (f"Running: { ' ' .join (args )} " )
146+ return subprocess .check_call (args , ** kwargs )
147+
148+ def check_output (self , args , ** kwargs ):
149+ self .log_info (f"Running: { ' ' .join (args )} " )
150+ return subprocess .check_output (args , ** kwargs )
151+
146152 def ffx_daemon_log_path (self ):
147153 return os .path .join (self .tmp_dir (), "ffx_daemon_log" )
148154
@@ -178,7 +184,7 @@ def start_ffx_isolation(self):
178184 )
179185
180186 # Disable analytics
181- subprocess .check_call (
187+ self .check_call (
182188 [
183189 ffx_path ,
184190 "config" ,
@@ -197,7 +203,7 @@ def start_ffx_isolation(self):
197203 "test.experimental_structured_output" : "true" ,
198204 }
199205 for key , value in configs .items ():
200- subprocess .check_call (
206+ self .check_call (
201207 [
202208 ffx_path ,
203209 "config" ,
@@ -222,7 +228,7 @@ def ffx_cmd_env(self):
222228 }
223229
224230 def stop_ffx_isolation (self ):
225- subprocess .check_call (
231+ self .check_call (
226232 [
227233 self .tool_path ("ffx" ),
228234 "daemon" ,
@@ -265,7 +271,7 @@ def start(self):
265271 self .start_ffx_isolation ()
266272
267273 # Stop any running emulators (there shouldn't be any)
268- subprocess .check_call (
274+ self .check_call (
269275 [
270276 ffx_path ,
271277 "emu" ,
@@ -282,11 +288,11 @@ def start(self):
282288 product_name = "minimal." + self .triple_to_arch (self .target )
283289 fuchsia_version = "20.20240412.3.1"
284290
285- # FIXME: We should be able to replace this with the machine parsable
286- # `ffx --machine json product lookup ...` once F15 is released.
287- out = subprocess .check_output (
291+ out = self .check_output (
288292 [
289293 ffx_path ,
294+ "--machine" ,
295+ "json" ,
290296 "product" ,
291297 "lookup" ,
292298 product_name ,
@@ -300,16 +306,15 @@ def start(self):
300306
301307 self .log_debug (out )
302308
303- for line in io .BytesIO (out ):
304- if line .startswith (b"gs://" ):
305- transfer_manifest_url = line .rstrip ()
306- break
307- else :
308- raise Exception ("Unable to parse transfer manifest" )
309+ try :
310+ transfer_manifest_url = json .loads (out )["transfer_manifest_url" ]
311+ except Exception as e :
312+ print (e )
313+ raise Exception ("Unable to parse transfer manifest" ) from e
309314
310315 # Download the product bundle.
311316 product_bundle_dir = os .path .join (self .tmp_dir (), 'product-bundle' )
312- subprocess .check_call (
317+ self .check_call (
313318 [
314319 ffx_path ,
315320 "product" ,
@@ -325,7 +330,7 @@ def start(self):
325330
326331 # Start emulator
327332 # FIXME: condition --accel hyper on target arch matching host arch
328- subprocess .check_call (
333+ self .check_call (
329334 [
330335 ffx_path ,
331336 "emu" ,
@@ -346,42 +351,52 @@ def start(self):
346351
347352 # Create new package repo
348353 self .log_info ("Creating package repo..." )
349- subprocess .check_call (
354+ self .check_call (
350355 [
351- self . tool_path ( "pm" ) ,
352- "newrepo " ,
353- "-repo " ,
356+ ffx_path ,
357+ "repository " ,
358+ "create " ,
354359 self .repo_dir (),
355360 ],
361+ env = ffx_env ,
356362 stdout = self .subprocess_output (),
357363 stderr = self .subprocess_output (),
358364 )
359365
360- # Add repo
361- subprocess .check_call (
366+ self .check_call (
362367 [
363368 ffx_path ,
364369 "repository" ,
365370 "add-from-pm" ,
366- self .repo_dir (),
367371 "--repository" ,
368372 self .TEST_REPO_NAME ,
373+ self .repo_dir (),
369374 ],
370375 env = ffx_env ,
371376 stdout = self .subprocess_output (),
372377 stderr = self .subprocess_output (),
373378 )
374379
380+ # Write to file
381+ self .write_to_file ()
382+
375383 # Start repository server
376- subprocess .check_call (
377- [ffx_path , "repository" , "server" , "start" , "--address" , "[::]:0" ],
384+ self .check_call (
385+ [
386+ ffx_path ,
387+ "repository" ,
388+ "server" ,
389+ "start" ,
390+ "--address" ,
391+ "[::]:0" ,
392+ ],
378393 env = ffx_env ,
379394 stdout = self .subprocess_output (),
380395 stderr = self .subprocess_output (),
381396 )
382397
383398 # Register with newly-started emulator
384- subprocess .check_call (
399+ self .check_call (
385400 [
386401 ffx_path ,
387402 "target" ,
@@ -395,12 +410,6 @@ def start(self):
395410 stderr = self .subprocess_output (),
396411 )
397412
398- # Create lockfiles
399- open (self .pm_lockfile_path (), "a" ).close ()
400-
401- # Write to file
402- self .write_to_file ()
403-
404413 self .log_info ("Success! Your environment is ready to run tests." )
405414
406415 # FIXME: shardify this
@@ -445,7 +454,6 @@ def start(self):
445454 meta/{package_name}.cm={package_dir}/meta/{package_name}.cm
446455 bin/{exe_name}={bin_path}
447456 lib/{libstd_name}={libstd_path}
448- lib/{libtest_name}={libtest_path}
449457 lib/ld.so.1={sdk_dir}/arch/{target_arch}/sysroot/dist/lib/ld.so.1
450458 lib/libfdio.so={sdk_dir}/arch/{target_arch}/dist/libfdio.so
451459 """
@@ -482,9 +490,6 @@ def run(self, args):
482490 if not libstd_paths :
483491 raise Exception (f"Failed to locate libstd (in { self .rustlibs_dir ()} )" )
484492
485- if not libtest_paths :
486- raise Exception (f"Failed to locate libtest (in { self .rustlibs_dir ()} )" )
487-
488493 # Build a unique, deterministic name for the test using the name of the
489494 # binary and the last 6 hex digits of the hash of the full path
490495 def path_checksum (path ):
@@ -500,6 +505,7 @@ def path_checksum(path):
500505 cml_path = os .path .join (package_dir , "meta" , f"{ package_name } .cml" )
501506 cm_path = os .path .join (package_dir , "meta" , f"{ package_name } .cm" )
502507 manifest_path = os .path .join (package_dir , f"{ package_name } .manifest" )
508+ manifest_json_path = os .path .join (package_dir , "package_manifest.json" )
503509 far_path = os .path .join (package_dir , f"{ package_name } -0.far" )
504510
505511 shared_libs = args .shared_libs [: args .n ]
@@ -523,22 +529,6 @@ def log(msg):
523529
524530 log (f"Bin path: { bin_path } " )
525531
526- log ("Setting up package..." )
527-
528- # Set up package
529- subprocess .check_call (
530- [
531- self .tool_path ("pm" ),
532- "-o" ,
533- package_dir ,
534- "-n" ,
535- package_name ,
536- "init" ,
537- ],
538- stdout = log_file ,
539- stderr = log_file ,
540- )
541-
542532 log ("Writing CML..." )
543533
544534 # Write and compile CML
@@ -563,7 +553,7 @@ def log(msg):
563553
564554 log ("Compiling CML..." )
565555
566- subprocess .check_call (
556+ self .check_call (
567557 [
568558 self .tool_path ("cmc" ),
569559 "compile" ,
@@ -590,64 +580,80 @@ def log(msg):
590580 target = self .target ,
591581 sdk_dir = self .sdk_dir ,
592582 libstd_name = os .path .basename (libstd_paths [0 ]),
593- libtest_name = os .path .basename (libtest_paths [0 ]),
594583 libstd_path = libstd_paths [0 ],
595- libtest_path = libtest_paths [0 ],
596584 target_arch = self .triple_to_arch (self .target ),
597585 )
598586 )
587+ # `libtest`` was historically a shared library, but now seems to be (sometimes?)
588+ # statically linked. If we find it as a shared library, include it in the manifest.
589+ if libtest_paths :
590+ manifest .write (
591+ f"lib/{ os .path .basename (libtest_paths [0 ])} ={ libtest_paths [0 ]} \n "
592+ )
599593 for shared_lib in shared_libs :
600594 manifest .write (f"lib/{ os .path .basename (shared_lib )} ={ shared_lib } \n " )
601595
596+ log ("Determining API level..." )
597+ out = self .check_output (
598+ [
599+ self .tool_path ("ffx" ),
600+ "--machine" ,
601+ "json" ,
602+ "version" ,
603+ ],
604+ env = self .ffx_cmd_env (),
605+ stderr = log_file ,
606+ )
607+ api_level = json .loads (out )["tool_version" ]["api_level" ]
608+
602609 log ("Compiling and archiving manifest..." )
603610
604- subprocess .check_call (
611+ self .check_call (
605612 [
606- self .tool_path ("pm" ),
613+ self .tool_path ("ffx" ),
614+ "package" ,
615+ "build" ,
616+ manifest_path ,
607617 "-o" ,
608618 package_dir ,
609- "-m" ,
610- manifest_path ,
611- "build" ,
619+ "--api-level" ,
620+ str (api_level ),
612621 ],
622+ env = self .ffx_cmd_env (),
613623 stdout = log_file ,
614624 stderr = log_file ,
615625 )
616- subprocess .check_call (
626+
627+ self .check_call (
617628 [
618- self .tool_path ("pm" ),
619- "-o" ,
620- package_dir ,
621- "-m" ,
622- manifest_path ,
629+ self .tool_path ("ffx" ),
630+ "package" ,
623631 "archive" ,
632+ "create" ,
633+ "-o" ,
634+ far_path ,
635+ manifest_json_path ,
624636 ],
637+ env = self .ffx_cmd_env (),
625638 stdout = log_file ,
626639 stderr = log_file ,
627640 )
628641
629642 log ("Publishing package to repo..." )
630643
631644 # Publish package to repo
632- with open (self .pm_lockfile_path (), "w" ) as pm_lockfile :
633- fcntl .lockf (pm_lockfile .fileno (), fcntl .LOCK_EX )
634- subprocess .check_call (
635- [
636- self .tool_path ("pm" ),
637- "publish" ,
638- "-a" ,
639- "-repo" ,
640- self .repo_dir (),
641- "-f" ,
642- far_path ,
643- ],
644- stdout = log_file ,
645- stderr = log_file ,
646- )
647- # This lock should be released automatically when the pm
648- # lockfile is closed, but we'll be polite and unlock it now
649- # since the spec leaves some wiggle room.
650- fcntl .lockf (pm_lockfile .fileno (), fcntl .LOCK_UN )
645+ self .check_call (
646+ [
647+ self .tool_path ("ffx" ),
648+ "repository" ,
649+ "publish" ,
650+ "--package" ,
651+ os .path .join (package_dir , "package_manifest.json" ),
652+ self .repo_dir (),
653+ ],
654+ stdout = log_file ,
655+ stderr = log_file ,
656+ )
651657
652658 log ("Running ffx test..." )
653659
@@ -765,7 +771,7 @@ def stop(self):
765771
766772 # Shut down the emulator
767773 self .log_info ("Stopping emulator..." )
768- subprocess .check_call (
774+ self .check_call (
769775 [
770776 self .tool_path ("ffx" ),
771777 "emu" ,
0 commit comments