Skip to content

Commit 2b1eac5

Browse files
authored
Merge pull request #413 from hyperware-ai/develop
develop
2 parents dcc2b42 + 3a99796 commit 2b1eac5

5 files changed

Lines changed: 112 additions & 34 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "kit"
33
authors = ["Sybil Technologies AG"]
4-
version = "3.2.0"
4+
version = "3.2.1"
55
edition = "2021"
66
description = "Development toolkit for Hyperware"
77
homepage = "https://hyperware.ai"

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,29 @@ NodeJS (v18 or higher) and NPM are required to build and develop the UI.
9696
The UI is written in React with Vite as the bundler + reloader.
9797

9898
To use `npm start` instead of `npm run dev`, use `kit dev-ui --release`.
99+
100+
## Appendix: Deps for Debian/Ubuntu
101+
102+
```bash
103+
apt update
104+
DEBIAN_FRONTEND=noninteractive apt install -y curl git build-essential pkg-config libssl-dev libclang-dev python3 python3-venv
105+
106+
curl https://sh.rustup.rs -sSf | sh -s -- -y
107+
. ~/.bashrc
108+
cargo install --git https://github.com/hyperware-ai/kit --locked
109+
kit setup -d --non-interactive
110+
. ~/.bashrc
111+
```
112+
113+
## Appendix: Deps for macOS
114+
115+
```bash
116+
xcode-select --install
117+
brew install pkg-config llvm openssl@3 python@3.12 libusb
118+
119+
curl https://sh.rustup.rs -sSf | sh -s -- -y
120+
. ~/.bashrc
121+
cargo install --git https://github.com/hyperware-ai/kit --locked
122+
kit setup -d --non-interactive
123+
. ~/.bashrc
124+
```

src/build/caller_utils_generator.rs

Lines changed: 78 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -254,42 +254,80 @@ struct SignatureStruct {
254254
args_comment: Option<String>, // Parsed from // args: (name: type, ...) comment
255255
}
256256

257-
// Find all interface imports in the world WIT file
257+
// Find all interface imports in the selected world WIT file(s)
258258
#[instrument(level = "trace", skip_all)]
259-
fn find_interfaces_in_world(api_dir: &Path) -> Result<Vec<String>> {
260-
debug!(dir = ?api_dir, "Finding interface imports in world definitions");
261-
let mut interfaces = Vec::new();
259+
fn find_interfaces_in_world(api_dir: &Path, world_name: &str) -> Result<Vec<String>> {
260+
debug!(dir = ?api_dir, world = %world_name, "Finding interface imports in world definitions");
261+
let mut world_defs: HashMap<String, String> = HashMap::new();
262262

263-
// Find world definition files
263+
// Index world definition files by world name
264264
for entry in WalkDir::new(api_dir)
265265
.max_depth(1)
266266
.into_iter()
267267
.filter_map(Result::ok)
268268
{
269269
let path = entry.path();
270+
if !(path.is_file() && path.extension().map_or(false, |ext| ext == "wit")) {
271+
continue;
272+
}
273+
let Ok(content) = fs::read_to_string(path) else {
274+
continue;
275+
};
276+
if !content.contains("world ") {
277+
continue;
278+
}
279+
let world_name = content
280+
.lines()
281+
.find(|line| line.trim().starts_with("world "))
282+
.and_then(|world_line| world_line.trim().split_whitespace().nth(1))
283+
.map(|name| {
284+
name.trim_end_matches(" {")
285+
.trim_start_matches('%')
286+
.to_string()
287+
});
288+
if let Some(clean_name) = world_name {
289+
world_defs.insert(clean_name.clone(), content);
290+
debug!(file = %path.display(), world = %clean_name, "Indexed world definition");
291+
}
292+
}
270293

271-
if path.is_file() && path.extension().map_or(false, |ext| ext == "wit") {
272-
if let Ok(content) = fs::read_to_string(path) {
273-
if content.contains("world ") {
274-
debug!(file = %path.display(), "Analyzing world definition file for imports");
275-
276-
// Extract import statements
277-
for line in content.lines() {
278-
let line = line.trim();
279-
if line.starts_with("import ") && line.ends_with(";") {
280-
let interface = line
281-
.trim_start_matches("import ")
282-
.trim_end_matches(";")
283-
.trim();
284-
285-
interfaces.push(interface.to_string());
286-
debug!(interface = %interface, "Found interface import");
287-
}
288-
}
289-
}
294+
let mut interfaces = Vec::new();
295+
let mut visited = std::collections::HashSet::new();
296+
let mut stack = vec![world_name.to_string()];
297+
298+
while let Some(current_world) = stack.pop() {
299+
let clean_world = current_world.trim_start_matches('%').to_string();
300+
if !visited.insert(clean_world.clone()) {
301+
continue;
302+
}
303+
let Some(content) = world_defs.get(&clean_world) else {
304+
debug!(world = %clean_world, "World definition not found for imports");
305+
continue;
306+
};
307+
308+
debug!(world = %clean_world, "Analyzing world definition file for imports");
309+
for line in content.lines() {
310+
let line = line.trim();
311+
if line.starts_with("import ") && line.ends_with(';') {
312+
let interface = line
313+
.trim_start_matches("import ")
314+
.trim_end_matches(';')
315+
.trim()
316+
.trim_start_matches('%');
317+
interfaces.push(interface.to_string());
318+
debug!(interface = %interface, "Found interface import");
319+
} else if line.starts_with("include ") && line.ends_with(';') {
320+
let include_world = line
321+
.trim_start_matches("include ")
322+
.trim_end_matches(';')
323+
.trim()
324+
.trim_start_matches('%')
325+
.to_string();
326+
stack.push(include_world);
290327
}
291328
}
292329
}
330+
293331
debug!(count = interfaces.len(), interfaces = ?interfaces, "Found interface imports");
294332
Ok(interfaces)
295333
}
@@ -450,8 +488,8 @@ fn generate_async_function(signature: &SignatureStruct) -> Option<String> {
450488
if field.wit_type == "string" {
451489
target_param = "&str";
452490
} else {
453-
// Use hyperware_process_lib::Address instead of WitAddress
454-
target_param = "&Address";
491+
// Use a distinct alias for hyperware_process_lib::Address to avoid WIT name clashes
492+
target_param = "&ProcessAddress";
455493
}
456494
} else if field.name == "returning" {
457495
return_type = rust_type;
@@ -620,8 +658,8 @@ crate-type = ["cdylib", "lib"]
620658
"types"
621659
};
622660

623-
// Get all interfaces from the world file
624-
let interface_imports = find_interfaces_in_world(api_dir)?;
661+
// Get all interfaces from the selected world
662+
let interface_imports = find_interfaces_in_world(api_dir, world_name)?;
625663

626664
// Store all types from each interface
627665
let mut interface_types: HashMap<String, Vec<String>> = HashMap::new();
@@ -638,8 +676,17 @@ crate-type = ["cdylib", "lib"]
638676
// Exclude world definition files
639677
if let Ok(content) = fs::read_to_string(path) {
640678
if !content.contains("world ") {
641-
debug!(file = %path.display(), "Adding WIT file for parsing");
642-
wit_files.push(path.to_path_buf());
679+
let interface_name = path.file_stem().unwrap().to_string_lossy();
680+
let interface_name = interface_name.trim_start_matches('%');
681+
if interface_imports
682+
.iter()
683+
.any(|i| i.trim_start_matches('%') == interface_name)
684+
{
685+
debug!(file = %path.display(), "Adding WIT file for parsing");
686+
wit_files.push(path.to_path_buf());
687+
} else {
688+
debug!(file = %path.display(), "Skipping WIT file not in selected world");
689+
}
643690
} else {
644691
debug!(file = %path.display(), "Skipping world definition WIT file");
645692
}
@@ -735,7 +782,7 @@ crate-type = ["cdylib", "lib"]
735782
// Add global imports
736783
lib_rs.push_str("pub use hyperware_process_lib::hyperapp::AppSendError;\n");
737784
lib_rs.push_str("pub use hyperware_process_lib::hyperapp::send;\n");
738-
lib_rs.push_str("pub use hyperware_process_lib::{Address, Request};\n");
785+
lib_rs.push_str("pub use hyperware_process_lib::{Address as ProcessAddress, Request};\n");
739786
lib_rs.push_str("use serde_json::json;\n\n");
740787

741788
// Add interface use statements

src/setup/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const MINIMUM_NPM_MINOR: u32 = 0;
2020
pub const REQUIRED_PY_MAJOR: u32 = 3;
2121
pub const MINIMUM_PY_MINOR: u32 = 10;
2222
pub const REQUIRED_PY_PACKAGE: &str = "componentize-py==0.11.0";
23+
const WASM_TOOLS_VERSION: &str = "1.225.0";
2324

2425
#[derive(Clone)]
2526
pub enum Dependency {
@@ -476,7 +477,11 @@ fn install_deps(deps: Vec<Dependency>, verbose: bool, toolchain: &str) -> Result
476477
Dependency::RustWasm32Wasi => {
477478
call_rustup("target add wasm32-wasip1", verbose, toolchain)?
478479
}
479-
Dependency::WasmTools => call_cargo("install wasm-tools", verbose, toolchain)?,
480+
Dependency::WasmTools => call_cargo(
481+
&format!("install wasm-tools --locked --version {WASM_TOOLS_VERSION}"),
482+
verbose,
483+
toolchain,
484+
)?,
480485
Dependency::Foundry => install_foundry(verbose)?,
481486
Dependency::Docker => {}
482487
}

0 commit comments

Comments
 (0)