Skip to content

Commit 9f67122

Browse files
author
Phil Cummins
committed
adds library bundling to prelink
1 parent 82afee4 commit 9f67122

File tree

2 files changed

+175
-148
lines changed

2 files changed

+175
-148
lines changed

src/lib.rs

Lines changed: 41 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,24 @@
11
#![deny(warnings)]
22

33
use {
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

4124
mod abi;
@@ -48,6 +31,7 @@ mod summary;
4831
#[cfg(test)]
4932
mod test;
5033
mod util;
34+
mod prelink;
5135

5236
static 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+
6555
impl 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

Comments
 (0)