11#![ deny( warnings) ]
22
33use {
4- anyhow:: { anyhow, bail, ensure, Context , Error , Result } ,
5- async_trait:: async_trait,
6- bytes:: Bytes ,
7- component_init:: Invoker ,
8- futures:: future:: FutureExt ,
9- heck:: ToSnakeCase ,
10- indexmap:: { IndexMap , IndexSet } ,
11- serde:: Deserialize ,
12- std:: {
4+ anyhow:: { anyhow, bail, ensure, Context , Error , Result } , async_trait:: async_trait, bytes:: Bytes , component_init:: Invoker , futures:: future:: FutureExt , heck:: ToSnakeCase , indexmap:: { IndexMap , IndexSet } , prelink:: { embedded_helper_utils, embedded_python_standard_library} , serde:: Deserialize , std:: {
135 collections:: { HashMap , HashSet } ,
146 env, fs,
157 io:: Cursor ,
168 iter,
179 ops:: Deref ,
1810 path:: { Path , PathBuf } ,
1911 str,
20- } ,
21- summary:: { Escape , Locations , Summary } ,
22- tar:: Archive ,
23- wasm_convert:: IntoValType ,
24- wasm_encoder:: {
12+ } , summary:: { Escape , Locations , Summary } , wasm_convert:: IntoValType , wasm_encoder:: {
2513 CodeSection , ExportKind , ExportSection , Function , FunctionSection , Instruction , Module ,
2614 TypeSection ,
27- } ,
28- wasmparser:: { FuncType , Parser , Payload , TypeRef } ,
29- wasmtime:: {
15+ } , wasmparser:: { FuncType , Parser , Payload , TypeRef } , wasmtime:: {
3016 component:: { Component , Instance , Linker , ResourceTable , ResourceType } ,
3117 Config , Engine , Store ,
32- } ,
33- wasmtime_wasi:: {
18+ } , wasmtime_wasi:: {
3419 pipe:: { MemoryInputPipe , MemoryOutputPipe } ,
3520 DirPerms , FilePerms , WasiCtx , WasiCtxBuilder , WasiView ,
36- } ,
37- wit_parser:: { Resolve , TypeDefKind , UnresolvedPackageGroup , WorldId , WorldItem , WorldKey } ,
38- zstd:: Decoder ,
21+ } , wit_parser:: { Resolve , TypeDefKind , UnresolvedPackageGroup , WorldId , WorldItem , WorldKey }
3922} ;
4023
4124mod abi;
@@ -48,6 +31,7 @@ mod summary;
4831#[ cfg( test) ]
4932mod test;
5033mod util;
34+ mod prelink;
5135
5236static NATIVE_EXTENSION_SUFFIX : & str = ".cpython-312-wasm32-wasi.so" ;
5337
@@ -62,6 +46,12 @@ pub struct Ctx {
6246 table : ResourceTable ,
6347}
6448
49+ pub struct Library {
50+ name : String ,
51+ module : Vec < u8 > ,
52+ dl_openable : bool ,
53+ }
54+
6555impl WasiView for Ctx {
6656 fn ctx ( & mut self ) -> & mut WasiCtx {
6757 & mut self . wasi
@@ -104,7 +94,7 @@ impl TryFrom<(&Path, RawComponentizePyConfig)> for ComponentizePyConfig {
10494}
10595
10696#[ derive( Debug ) ]
107- struct ConfigContext < T > {
97+ pub struct ConfigContext < T > {
10898 module : String ,
10999 root : PathBuf ,
110100 path : PathBuf ,
@@ -207,29 +197,20 @@ pub async fn componentize(
207197 . filter_map ( |& s| Path :: new ( s) . exists ( ) . then_some ( s) )
208198 . collect :: < Vec < _ > > ( ) ;
209199
210- // Untar the embedded copy of the Python standard library into a temporary directory
211- let stdlib = tempfile:: tempdir ( ) ?;
212-
213- Archive :: new ( Decoder :: new ( Cursor :: new ( include_bytes ! ( concat!(
214- env!( "OUT_DIR" ) ,
215- "/python-lib.tar.zst"
216- ) ) ) ) ?)
217- . unpack ( stdlib. path ( ) ) ?;
200+ let embedded_python_standard_lib = embedded_python_standard_library ( ) ;
201+ let embedded_helper_utils = embedded_helper_utils ( ) ;
218202
219- // Untar the embedded copy of helper utilities into a temporary directory
220- let bundled = tempfile:: tempdir ( ) ?;
221-
222- Archive :: new ( Decoder :: new ( Cursor :: new ( include_bytes ! ( concat!(
223- env!( "OUT_DIR" ) ,
224- "/bundled.tar.zst"
225- ) ) ) ) ?)
226- . unpack ( bundled. path ( ) ) ?;
203+ // Remove non-existent elements from `python_path` so we don't choke on them later
204+ let python_path = & python_path
205+ . iter ( )
206+ . filter_map ( |& s| Path :: new ( s) . exists ( ) . then_some ( s) )
207+ . collect :: < Vec < _ > > ( ) ;
227208
228209 // Search `python_path` for native extension libraries and/or componentize-py.toml files. Packages containing
229210 // the latter may contain their own WIT files defining their own worlds (in addition to what the caller
230211 // specified as paramters), which we'll try to match up with `module_worlds` in the next step.
231- let mut raw_configs = Vec :: new ( ) ;
232- let mut library_path = Vec :: with_capacity ( python_path. len ( ) ) ;
212+ let mut raw_configs: Vec < crate :: ConfigContext < crate :: RawComponentizePyConfig > > = Vec :: new ( ) ;
213+ let mut library_path: Vec < ( & str , Vec < std :: path :: PathBuf > ) > = Vec :: with_capacity ( python_path. len ( ) ) ;
233214 for path in python_path {
234215 let mut libraries = Vec :: new ( ) ;
235216 search_directory (
@@ -238,10 +219,12 @@ pub async fn componentize(
238219 & mut libraries,
239220 & mut raw_configs,
240221 & mut HashSet :: new ( ) ,
241- ) ? ;
222+ ) . unwrap ( ) ;
242223 library_path. push ( ( * path, libraries) ) ;
243224 }
244225
226+ let mut libraries = prelink:: bundle_libraries ( library_path) ;
227+
245228 // Validate the paths parsed from any componentize-py.toml files discovered above and match them up with
246229 // `module_worlds` entries. Note that we use an `IndexMap` to preserve the order specified in `module_worlds`,
247230 // which is required to be topologically sorted with respect to package dependencies.
@@ -341,108 +324,11 @@ pub async fn componentize(
341324
342325 let summary = Summary :: try_new ( & resolve, & worlds) ?;
343326
344- struct Library {
345- name : String ,
346- module : Vec < u8 > ,
347- dl_openable : bool ,
348- }
349-
350- let mut libraries = vec ! [
351- Library {
352- name: "libcomponentize_py_runtime.so" . into( ) ,
353- module: zstd:: decode_all( Cursor :: new( include_bytes!( concat!(
354- env!( "OUT_DIR" ) ,
355- "/libcomponentize_py_runtime.so.zst"
356- ) ) ) ) ?,
357- dl_openable: false ,
358- } ,
359- Library {
360- name: "libpython3.12.so" . into( ) ,
361- module: zstd:: decode_all( Cursor :: new( include_bytes!( concat!(
362- env!( "OUT_DIR" ) ,
363- "/libpython3.12.so.zst"
364- ) ) ) ) ?,
365- dl_openable: false ,
366- } ,
367- Library {
368- name: "libc.so" . into( ) ,
369- module: zstd:: decode_all( Cursor :: new( include_bytes!( concat!(
370- env!( "OUT_DIR" ) ,
371- "/libc.so.zst"
372- ) ) ) ) ?,
373- dl_openable: false ,
374- } ,
375- Library {
376- name: "libwasi-emulated-mman.so" . into( ) ,
377- module: zstd:: decode_all( Cursor :: new( include_bytes!( concat!(
378- env!( "OUT_DIR" ) ,
379- "/libwasi-emulated-mman.so.zst"
380- ) ) ) ) ?,
381- dl_openable: false ,
382- } ,
383- Library {
384- name: "libwasi-emulated-process-clocks.so" . into( ) ,
385- module: zstd:: decode_all( Cursor :: new( include_bytes!( concat!(
386- env!( "OUT_DIR" ) ,
387- "/libwasi-emulated-process-clocks.so.zst"
388- ) ) ) ) ?,
389- dl_openable: false ,
390- } ,
391- Library {
392- name: "libwasi-emulated-getpid.so" . into( ) ,
393- module: zstd:: decode_all( Cursor :: new( include_bytes!( concat!(
394- env!( "OUT_DIR" ) ,
395- "/libwasi-emulated-getpid.so.zst"
396- ) ) ) ) ?,
397- dl_openable: false ,
398- } ,
399- Library {
400- name: "libwasi-emulated-signal.so" . into( ) ,
401- module: zstd:: decode_all( Cursor :: new( include_bytes!( concat!(
402- env!( "OUT_DIR" ) ,
403- "/libwasi-emulated-signal.so.zst"
404- ) ) ) ) ?,
405- dl_openable: false ,
406- } ,
407- Library {
408- name: "libc++.so" . into( ) ,
409- module: zstd:: decode_all( Cursor :: new( include_bytes!( concat!(
410- env!( "OUT_DIR" ) ,
411- "/libc++.so.zst"
412- ) ) ) ) ?,
413- dl_openable: false ,
414- } ,
415- Library {
416- name: "libc++abi.so" . into( ) ,
417- module: zstd:: decode_all( Cursor :: new( include_bytes!( concat!(
418- env!( "OUT_DIR" ) ,
419- "/libc++abi.so.zst"
420- ) ) ) ) ?,
421- dl_openable: false ,
422- } ,
423- Library {
424- name: "libcomponentize_py_bindings.so" . into( ) ,
425- module: bindings:: make_bindings( & resolve, & worlds, & summary) ?,
426- dl_openable: false ,
427- } ,
428- ] ;
429-
430- for ( index, ( path, libs) ) in library_path. iter ( ) . enumerate ( ) {
431- for library in libs {
432- let path = library
433- . strip_prefix ( path)
434- . unwrap ( )
435- . to_str ( )
436- . context ( "non-UTF-8 path" ) ?
437- . replace ( '\\' , "/" ) ;
438-
439- libraries. push ( Library {
440- name : format ! ( "/{index}/{path}" ) ,
441- module : fs:: read ( library) ?,
442- dl_openable : true ,
443- } ) ;
444- }
445- }
327+ libraries. push ( Library {
328+ name : "libcomponentize_py_bindings.so" . into ( ) ,
329+ module : bindings:: make_bindings ( & resolve, & worlds, & summary) ?,
330+ dl_openable : false ,
331+ } ) ;
446332
447333 // Link all the libraries (including any native extensions) into a single component.
448334 let mut linker = wit_component:: Linker :: default ( ) . validate ( true ) ;
@@ -524,7 +410,7 @@ pub async fn componentize(
524410 // application's first and only chance to load any standard or third-party modules since we do not yet include
525411 // a virtual filesystem in the component to make those modules available at runtime.
526412
527- let stdout = MemoryOutputPipe :: new ( 10000 ) ;
413+ let stdout: MemoryOutputPipe = MemoryOutputPipe :: new ( 10000 ) ;
528414 let stderr = MemoryOutputPipe :: new ( 10000 ) ;
529415
530416 let mut wasi = WasiCtxBuilder :: new ( ) ;
@@ -534,8 +420,8 @@ pub async fn componentize(
534420 . env ( "PYTHONUNBUFFERED" , "1" )
535421 . env ( "COMPONENTIZE_PY_APP_NAME" , app_name)
536422 . env ( "PYTHONHOME" , "/python" )
537- . preopened_dir ( stdlib . path ( ) , "python" , DirPerms :: all ( ) , FilePerms :: all ( ) ) ?
538- . preopened_dir ( bundled . path ( ) , "bundled" , DirPerms :: all ( ) , FilePerms :: all ( ) ) ?;
423+ . preopened_dir ( embedded_python_standard_lib . path ( ) , "python" , DirPerms :: all ( ) , FilePerms :: all ( ) ) ?
424+ . preopened_dir ( embedded_helper_utils . path ( ) , "bundled" , DirPerms :: all ( ) , FilePerms :: all ( ) ) ?;
539425
540426 // Generate guest mounts for each host directory in `python_path`.
541427 for ( index, path) in python_path. iter ( ) . enumerate ( ) {
@@ -628,7 +514,7 @@ pub async fn componentize(
628514
629515 Ok ( ( ) )
630516 }
631- replace ( bundled . path ( ) , "proxy" , & module) ?;
517+ replace ( embedded_helper_utils . path ( ) , "proxy" , & module) ?;
632518 } ;
633519
634520 for ( mounts, world_dir) in world_dir_mounts. iter ( ) {
@@ -663,6 +549,10 @@ pub async fn componentize(
663549 let engine = Engine :: new ( & config) ?;
664550
665551 let mut linker = Linker :: new ( & engine) ;
552+
553+ //whenever the guest adds to fulfil x or y,
554+ //add to linker is giving access to wasi so it can access the host file system
555+
666556 let added_to_linker = if let Some ( add_to_linker) = add_to_linker {
667557 add_to_linker ( & mut linker) ?;
668558 true
@@ -672,6 +562,9 @@ pub async fn componentize(
672562
673563 let mut store = Store :: new ( & engine, Ctx { wasi, table } ) ;
674564
565+ //can stub out wasi, if does try to access file system then trap it
566+ //there are no env variables etc
567+
675568 let app_name = app_name. to_owned ( ) ;
676569 let component = component_init:: initialize_staged (
677570 & component,
0 commit comments