-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Idea] WasiApp #25
Comments
Personally I think it's best to stick pretty close to the C API without adding too many extra layers of abstraction, but if you'd like I think this would probably make for a good example program? |
Okay, I mean that using C-API it is hard to link big program consistent with some pieces 100 wasm files ... |
@alexcrichton One additional question, could you explain of provide link to documentation for case where wasmtime some_app.wasm # It depends on library.wasm that located in the same directory The question, how to provide to |
Sorry I'm not sure what you mean by "pieces 100 wasm files" or what you mean by the path dependencies that |
@alexcrichton class WasiApp {
public:
explicit WasiApp(std::vector<fs::path> linkdirs,
std::optional<fs::path> workdir = std::nullopt)
: link_dirs_{std::move(linkdirs)}
, workdir_{std::move(workdir)} {
linker_.define_wasi().unwrap();
}
void set_config(ws::WasiConfig&& config) {
store_.context().set_wasi(std::move(config)).unwrap();
}
void load_app(const std::string& app_name) {
reset_app();
auto workdir = workdir_.value_or(fs::current_path());
std::optional<fs::path> filename = find_file(workdir, app_name);
ws::Module module = compile(filename.value());
app_instance_ = link(app_name, module);
}
void run(const std::string& entry_point, const std::vector<ws::Val> ¶ms) {
ws::Func f = std::get<ws::Func>(*app_instance_.value().get(store_, entry_point));
f.call(store_, {}).unwrap();
}
private:
[[nodiscard]]
static std::string read_wat_file(const char* name) {
std::ifstream watFile;
watFile.open(name);
std::stringstream strStream;
strStream << watFile.rdbuf();
return strStream.str();
}
[[nodiscard]]
static std::vector<uint8_t> read_wasm_file(const char* name) {
std::ifstream bin_file(name, std::ios::binary);
return {(std::istreambuf_iterator<char>(bin_file)), (std::istreambuf_iterator<char>())};
}
[[nodiscard]]
static std::optional<fs::path> find_file(const fs::path& search_dir,
const std::string& app_name) {
std::optional<fs::path> filename;
for (auto &p : fs::directory_iterator(search_dir)) {
if (is_regular_file(p.path())) {
if (p.path().stem() == app_name &&
(p.path().extension() == ".wasm" ||
p.path().extension() == ".wat")) {
filename = p.path();
break;
}
}
}
return filename;
}
[[nodiscard]]
ws::Instance link(const std::string& name, const ws::Module& module) {
std::unordered_map<std::string, std::vector<std::string>> imports;
for (auto i : module.imports()) {
imports[std::string(i.module())].push_back(std::string(i.name()));
}
for (auto& [import_file, import_symbols] : imports) {
std::sort(import_symbols.begin(), import_symbols.end());
}
load_imports(imports);
ws::Instance linking_instance = linker_.instantiate(store_, module).unwrap();
linker_.define_instance(store_, name, linking_instance);
imported_modules_.push_back(module);
return linking_instance;
}
[[nodiscard]]
ws::Module compile(const fs::path& path) {
if (path.extension() == ".wat") {
auto linking_wat = read_wat_file(path.string().c_str());
return ws::Module::compile(engine_, linking_wat).unwrap();
} else {
auto linking_wasm = read_wasm_file(path.string().c_str());
return ws::Module::compile(engine_, linking_wasm).unwrap();
}
}
void load_imports(const std::unordered_map<std::string, std::vector<std::string>>& imports) {
for (const auto& [import_file, import_symbols] : imports) {
if (import_file.find("wasi_snapshot") != std::string::npos) {
continue;
}
bool is_found_wasm = false;
for (const auto& link_dirs : link_dirs_) {
std::optional<fs::path> link_filename = find_file(link_dirs, import_file);
if (link_filename) {
ws::Module linking_module = compile(link_filename.value());
std::vector<std::string> exports;
for (const auto& e : linking_module.exports()) {
exports.emplace_back(e.name());
}
std::sort(exports.begin(), exports.end());
if (exports != import_symbols) {
continue;
}
ws::Instance linking_instance = link(import_file, linking_module);
is_found_wasm = true;
break;
}
}
if (!is_found_wasm) {
fprintf(stderr, "error: Cannot link module %s\n", import_file.c_str());
std::abort();
}
}
}
void reset_app() {
app_instance_.reset();
imported_modules_.clear();
}
ws::Engine engine_;
ws::Store store_{engine_};
ws::Linker linker_{engine_};
std::vector<fs::path> link_dirs_;
std::optional<fs::path> workdir_;
std::optional<ws::Instance> app_instance_;
std::vector<ws::Module> imported_modules_;
}; Check how |
Ah so the |
What I did is actually elf loader for wasmtime programs that could find libraries in other predefined places. |
Yeah if you'd like to add this to Wasmtime itself it would be best to discuss over there. I don't think this is really ready at this time though because wasm modules referring to each other by name and doing nontrivial things isn't really supported by toolchains today. That's sort of the "dynamic linking" story for wasm modules which currently is supported by Emscripten with JS glue but I don't believe anyone's worked on getting something working off the web. |
@alexcrichton If I will add this functionality, will you accept PR ? |
Added #61 |
Hi all,
Recently I wrote small wrapper that simplify loading and linking wasi application with all dependencies, see example:
The benefits is that this class encapsulate all complexity for properly loading application.
For example, underhood
wasi_app.load_app(app_name)
loads all dependencies that are describe inimport
sectionsDo you think such functionality would be useful in this repo ? (I could create PR)
Or do you think it should be provided as functionality from wasmtime ?
Or not needed at all ?
Please, share your ideas ...
The text was updated successfully, but these errors were encountered: