reactor-effect-native 0.2.0 → 0.3.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +4 -2
- package/README.md +90 -15
- package/dist/_internal/bridge.d.ts +70 -14
- package/dist/_internal/bridge.d.ts.map +1 -1
- package/dist/_internal/bridge.js +290 -145
- package/dist/_internal/bridge.js.map +1 -1
- package/dist/_internal/isolated/child.d.ts +2 -0
- package/dist/_internal/isolated/child.d.ts.map +1 -0
- package/dist/_internal/isolated/child.js +176 -0
- package/dist/_internal/isolated/child.js.map +1 -0
- package/dist/_internal/isolated/host.d.ts +147 -0
- package/dist/_internal/isolated/host.d.ts.map +1 -0
- package/dist/_internal/isolated/host.js +645 -0
- package/dist/_internal/isolated/host.js.map +1 -0
- package/dist/_internal/isolated/protocol.d.ts +399 -0
- package/dist/_internal/isolated/protocol.d.ts.map +1 -0
- package/dist/_internal/isolated/protocol.js +250 -0
- package/dist/_internal/isolated/protocol.js.map +1 -0
- package/dist/_internal/peer.d.ts +68 -13
- package/dist/_internal/peer.d.ts.map +1 -1
- package/dist/_internal/peer.js +233 -157
- package/dist/_internal/peer.js.map +1 -1
- package/dist/index.d.ts +19 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -33
- package/dist/index.js.map +1 -1
- package/dist/isolated.d.ts +25 -0
- package/dist/isolated.d.ts.map +1 -0
- package/dist/isolated.js +39 -0
- package/dist/isolated.js.map +1 -0
- package/lib/darwin-arm64/libreactor_effect_native.dylib +0 -0
- package/lib/darwin-arm64/native-identity.json +4 -3
- package/lib/linux-x64/libreactor_effect_native.so +0 -0
- package/lib/linux-x64/native-identity.json +4 -3
- package/package.json +7 -3
- package/rust/Cargo.toml +108 -2
- package/rust/build.rs +253 -114
- package/rust/clippy.toml +7 -0
- package/rust/include/reactor_effect_native.h +101 -42
- package/rust/src/abi.rs +258 -0
- package/rust/src/error.rs +149 -0
- package/rust/src/ffi/memory.rs +256 -0
- package/rust/src/ffi/tests.rs +719 -0
- package/rust/src/ffi.rs +474 -0
- package/rust/src/lib.rs +36 -2341
- package/rust/src/peer/callbacks.rs +145 -0
- package/rust/src/peer/media.rs +221 -0
- package/rust/src/peer/owner/tests.rs +188 -0
- package/rust/src/peer/owner.rs +353 -0
- package/rust/src/peer/shared.rs +323 -0
- package/rust/src/peer/tests.rs +513 -0
- package/rust/src/peer.rs +189 -0
- package/rust/src/protocol/event.rs +238 -0
- package/rust/src/protocol/request.rs +347 -0
- package/rust/src/protocol/stats.rs +356 -0
- package/rust/src/protocol.rs +71 -0
- package/rust/src/sync/gate.rs +159 -0
- package/rust/src/sync/notifier.rs +136 -0
- package/rust/src/sync/queue.rs +384 -0
- package/rust/src/sync.rs +20 -0
- package/rust/src/test_support.rs +99 -0
- package/rust-toolchain.toml +7 -0
- package/scripts/stage.mjs +41 -1
package/rust/build.rs
CHANGED
|
@@ -1,85 +1,207 @@
|
|
|
1
|
+
//! Embeds the library's source and build identity and, on macOS, links the
|
|
2
|
+
//! compiler-rt archive that the pinned libwebrtc needs.
|
|
3
|
+
//!
|
|
4
|
+
//! The identity is a JSON object that `scripts/stage.mjs` finds in the built
|
|
5
|
+
//! library and checks against the checked-out sources before staging it. Its
|
|
6
|
+
//! `sourceSha256` covers the same files, hashed the same way, as
|
|
7
|
+
//! `stage.mjs --source-hash` and the pack check in `scripts/pack.ts`. Its
|
|
8
|
+
//! `webrtcPrebuilt` names the Reactor libwebrtc prebuilt that
|
|
9
|
+
//! `reactor-webrtc-sys` links, which staging checks against the shipped SBOM.
|
|
10
|
+
|
|
1
11
|
use std::env;
|
|
12
|
+
use std::error::Error;
|
|
13
|
+
use std::fmt::{self, Write as _};
|
|
2
14
|
use std::fs;
|
|
3
|
-
use std::io::Write;
|
|
15
|
+
use std::io::{self, Write as _};
|
|
4
16
|
use std::path::{Path, PathBuf};
|
|
5
17
|
use std::process::{Command, Stdio};
|
|
6
18
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
/// A failed build step, which Cargo reports with the script's output.
|
|
20
|
+
type BuildResult<T> = Result<T, Box<dyn Error>>;
|
|
21
|
+
|
|
22
|
+
/// The C ABI version. A library test checks it against `abi::ABI_VERSION`.
|
|
23
|
+
const ABI_VERSION: u32 = 4;
|
|
24
|
+
|
|
25
|
+
/// The source identity's inputs outside `src/`, which it covers entirely.
|
|
26
|
+
const SOURCE_FILES: [&str; 5] = [
|
|
27
|
+
"Cargo.toml",
|
|
28
|
+
"Cargo.lock",
|
|
29
|
+
"build.rs",
|
|
30
|
+
".cargo/config.toml",
|
|
31
|
+
"include/reactor_effect_native.h",
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
/// Overrides that make `reactor-webrtc-sys` link something other than its
|
|
35
|
+
/// tagged prebuilt; with either set, the identity names no prebuilt.
|
|
36
|
+
const WEBRTC_OVERRIDES: [&str; 2] = ["REACTOR_WEBRTC_LIB_DIR", "REACTOR_WEBRTC_PREBUILT_URL"];
|
|
37
|
+
|
|
38
|
+
/// Environment that changes the compiled library, recorded when set.
|
|
39
|
+
const BUILD_ENVIRONMENT: [&str; 6] = [
|
|
40
|
+
"CC",
|
|
41
|
+
"CXX",
|
|
42
|
+
"CARGO_ENCODED_RUSTFLAGS",
|
|
43
|
+
"CFLAGS",
|
|
44
|
+
"CXXFLAGS",
|
|
45
|
+
"MACOSX_DEPLOYMENT_TARGET",
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
fn main() -> BuildResult<()> {
|
|
49
|
+
let root = env::var_os("CARGO_MANIFEST_DIR")
|
|
50
|
+
.map(PathBuf::from)
|
|
51
|
+
.ok_or("Cargo sets CARGO_MANIFEST_DIR")?;
|
|
52
|
+
let identity = build_identity(&root)?;
|
|
53
|
+
println!("cargo::rustc-env=REACTOR_EFFECT_BUILD_IDENTITY={identity}");
|
|
54
|
+
if env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("macos") {
|
|
55
|
+
link_compiler_rt()?;
|
|
21
56
|
}
|
|
57
|
+
Ok(())
|
|
22
58
|
}
|
|
23
59
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
60
|
+
/// The identity JSON: the source hash, target and profile, the toolchain's
|
|
61
|
+
/// versions and the build environment.
|
|
62
|
+
fn build_identity(root: &Path) -> BuildResult<String> {
|
|
63
|
+
let cc = env::var("CC").unwrap_or_else(|_| "clang".to_owned());
|
|
64
|
+
let cxx = env::var("CXX").unwrap_or_else(|_| "clang++".to_owned());
|
|
65
|
+
let rustc = cargo_env("RUSTC")?;
|
|
66
|
+
// Each value is already JSON.
|
|
67
|
+
let mut fields = vec![
|
|
68
|
+
("schemaVersion", "1".to_owned()),
|
|
69
|
+
("abiVersion", ABI_VERSION.to_string()),
|
|
70
|
+
("sourceSha256", json_string(&source_sha256(root)?)),
|
|
71
|
+
("target", json_string(&cargo_env("TARGET")?)),
|
|
72
|
+
("profile", json_string(&cargo_env("PROFILE")?)),
|
|
73
|
+
("rustc", json_string(&tool_version(&rustc)?)),
|
|
74
|
+
("cc", json_string(&tool_version(&cc)?)),
|
|
75
|
+
("cxx", json_string(&tool_version(&cxx)?)),
|
|
76
|
+
(
|
|
77
|
+
"webrtcPrebuilt",
|
|
78
|
+
webrtc_prebuilt(root)?.map_or_else(|| "null".to_owned(), |tag| json_string(&tag)),
|
|
79
|
+
),
|
|
80
|
+
];
|
|
81
|
+
for key in BUILD_ENVIRONMENT {
|
|
82
|
+
println!("cargo::rerun-if-env-changed={key}");
|
|
83
|
+
if let Ok(value) = env::var(key) {
|
|
84
|
+
fields.push((key, json_string(&value)));
|
|
35
85
|
}
|
|
36
86
|
}
|
|
37
|
-
|
|
38
|
-
|
|
87
|
+
let fields: Vec<String> = fields
|
|
88
|
+
.iter()
|
|
89
|
+
.map(|(key, value)| format!("{}:{value}", json_string(key)))
|
|
90
|
+
.collect();
|
|
91
|
+
Ok(format!("{{{}}}", fields.join(",")))
|
|
39
92
|
}
|
|
40
93
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
94
|
+
/// The prebuilt tag `reactor-webrtc-sys` downloads, derived as it derives it:
|
|
95
|
+
/// from `WEBRTC_VERSION` at the root of the pinned `reactor-webrtc` checkout.
|
|
96
|
+
/// `None` when an override links a local or custom archive instead.
|
|
97
|
+
fn webrtc_prebuilt(root: &Path) -> BuildResult<Option<String>> {
|
|
98
|
+
for key in WEBRTC_OVERRIDES {
|
|
99
|
+
println!("cargo::rerun-if-env-changed={key}");
|
|
100
|
+
if env::var_os(key).is_some() {
|
|
101
|
+
return Ok(None);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
let output = Command::new(cargo_env("CARGO")?)
|
|
105
|
+
.args(["metadata", "--format-version", "1", "--offline", "--locked"])
|
|
106
|
+
.arg("--manifest-path")
|
|
107
|
+
.arg(root.join("Cargo.toml"))
|
|
44
108
|
.output()
|
|
45
|
-
.
|
|
46
|
-
|
|
47
|
-
output.status.
|
|
48
|
-
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
.
|
|
52
|
-
.
|
|
53
|
-
.next()
|
|
54
|
-
.
|
|
55
|
-
.
|
|
109
|
+
.map_err(|error| format!("cargo metadata could not run: {error}"))?;
|
|
110
|
+
if !output.status.success() {
|
|
111
|
+
return Err(format!("cargo metadata failed: {}", output.status).into());
|
|
112
|
+
}
|
|
113
|
+
let metadata = String::from_utf8(output.stdout)?;
|
|
114
|
+
let manifest = metadata
|
|
115
|
+
.split("\"manifest_path\":\"")
|
|
116
|
+
.skip(1)
|
|
117
|
+
.filter_map(|rest| rest.split('"').next())
|
|
118
|
+
.find(|path| path.ends_with("/reactor-webrtc-sys/Cargo.toml"))
|
|
119
|
+
.ok_or("cargo metadata names no reactor-webrtc-sys manifest")?;
|
|
120
|
+
// `<checkout>/crates/reactor-webrtc-sys/Cargo.toml`, as its build script reads it.
|
|
121
|
+
let version = Path::new(manifest)
|
|
122
|
+
.ancestors()
|
|
123
|
+
.nth(3)
|
|
124
|
+
.ok_or("reactor-webrtc-sys is not inside a reactor-webrtc checkout")?
|
|
125
|
+
.join("WEBRTC_VERSION");
|
|
126
|
+
println!("cargo::rerun-if-changed={}", version.display());
|
|
127
|
+
let source = fs::read_to_string(&version)
|
|
128
|
+
.map_err(|error| format!("read {}: {error}", version.display()))?;
|
|
129
|
+
prebuilt_tag(&source).map(Some)
|
|
56
130
|
}
|
|
57
131
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
let mut
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
132
|
+
/// `webrtc-<milestone>-<commit8>-p<patch>` from `WEBRTC_VERSION`'s variables.
|
|
133
|
+
fn prebuilt_tag(source: &str) -> BuildResult<String> {
|
|
134
|
+
let (mut branch, mut commit, mut patch) = (None, None, "0");
|
|
135
|
+
for line in source.lines().map(str::trim) {
|
|
136
|
+
if line.starts_with('#') {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if let Some(value) = line.strip_prefix("WEBRTC_BRANCH=") {
|
|
140
|
+
branch = Some(value);
|
|
141
|
+
} else if let Some(value) = line.strip_prefix("WEBRTC_COMMIT=") {
|
|
142
|
+
commit = Some(value);
|
|
143
|
+
} else if let Some(value) = line.strip_prefix("REACTOR_PATCH_LEVEL=") {
|
|
144
|
+
patch = value;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
let branch = branch.ok_or("WEBRTC_VERSION names no WEBRTC_BRANCH")?;
|
|
148
|
+
let commit = commit
|
|
149
|
+
.and_then(|commit| commit.get(..8))
|
|
150
|
+
.ok_or("WEBRTC_VERSION pins no WEBRTC_COMMIT")?;
|
|
151
|
+
let milestone = branch.strip_prefix("branch-heads/").unwrap_or(branch);
|
|
152
|
+
Ok(format!("webrtc-{milestone}-{commit}-p{patch}"))
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/// A variable Cargo sets for every build script.
|
|
156
|
+
fn cargo_env(key: &str) -> BuildResult<String> {
|
|
157
|
+
env::var(key).map_err(|error| format!("Cargo sets {key}: {error}").into())
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/// SHA-256 over each source input, as `path NUL contents NUL` in path order.
|
|
161
|
+
fn source_sha256(root: &Path) -> BuildResult<String> {
|
|
162
|
+
let mut files = SOURCE_FILES.map(str::to_owned).to_vec();
|
|
163
|
+
files.extend(files_under(root, &root.join("src"))?);
|
|
70
164
|
files.sort();
|
|
165
|
+
// The directory as well, so a new source file reruns this script.
|
|
166
|
+
println!("cargo::rerun-if-changed=src");
|
|
71
167
|
let mut input = Vec::new();
|
|
72
168
|
for file in files {
|
|
73
|
-
println!("cargo
|
|
169
|
+
println!("cargo::rerun-if-changed={file}");
|
|
170
|
+
let contents = fs::read(root.join(&file))
|
|
171
|
+
.map_err(|error| format!("read native build input {file}: {error}"))?;
|
|
74
172
|
input.extend_from_slice(file.as_bytes());
|
|
75
173
|
input.push(0);
|
|
76
|
-
input.extend_from_slice(&
|
|
174
|
+
input.extend_from_slice(&contents);
|
|
77
175
|
input.push(0);
|
|
78
176
|
}
|
|
177
|
+
sha256_hex(&input)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/// Every file under `directory`, as a `/`-separated path relative to `root`.
|
|
181
|
+
fn files_under(root: &Path, directory: &Path) -> BuildResult<Vec<String>> {
|
|
182
|
+
let unreadable = |error: io::Error| format!("read {}: {error}", directory.display());
|
|
183
|
+
let mut files = Vec::new();
|
|
184
|
+
for entry in fs::read_dir(directory).map_err(unreadable)? {
|
|
185
|
+
let path = entry.map_err(unreadable)?.path();
|
|
186
|
+
if path.is_dir() {
|
|
187
|
+
files.extend(files_under(root, &path)?);
|
|
188
|
+
} else if path.is_file() {
|
|
189
|
+
let relative = path
|
|
190
|
+
.strip_prefix(root)?
|
|
191
|
+
.to_str()
|
|
192
|
+
.ok_or_else(|| format!("source path is not UTF-8: {}", path.display()))?;
|
|
193
|
+
files.push(relative.replace('\\', "/"));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
Ok(files)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/// SHA-256 as hex, from the platform's tool rather than a build dependency.
|
|
200
|
+
fn sha256_hex(input: &[u8]) -> BuildResult<String> {
|
|
79
201
|
let mut command = if cfg!(target_os = "macos") {
|
|
80
|
-
let mut
|
|
81
|
-
|
|
82
|
-
|
|
202
|
+
let mut shasum = Command::new("shasum");
|
|
203
|
+
shasum.args(["-a", "256"]);
|
|
204
|
+
shasum
|
|
83
205
|
} else {
|
|
84
206
|
Command::new("sha256sum")
|
|
85
207
|
};
|
|
@@ -87,73 +209,90 @@ fn build_identity() {
|
|
|
87
209
|
.stdin(Stdio::piped())
|
|
88
210
|
.stdout(Stdio::piped())
|
|
89
211
|
.spawn()
|
|
90
|
-
.
|
|
212
|
+
.map_err(|error| {
|
|
213
|
+
format!("a SHA-256 tool is required to identify native build inputs: {error}")
|
|
214
|
+
})?;
|
|
215
|
+
// The piped stdin closes at the end of this statement, ending the input.
|
|
91
216
|
child
|
|
92
217
|
.stdin
|
|
93
218
|
.take()
|
|
94
|
-
.
|
|
95
|
-
.write_all(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
let
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
let cxx = env::var("CXX").unwrap_or_else(|_| "clang++".into());
|
|
105
|
-
let mut fields = vec![
|
|
106
|
-
"\"schemaVersion\":1".to_owned(),
|
|
107
|
-
"\"abiVersion\":2".to_owned(),
|
|
108
|
-
format!("\"sourceSha256\":{}", quote(digest)),
|
|
109
|
-
format!("\"target\":{}", quote(&env::var("TARGET").unwrap())),
|
|
110
|
-
format!("\"profile\":{}", quote(&env::var("PROFILE").unwrap())),
|
|
111
|
-
format!("\"rustc\":{}", quote(&version(&env::var("RUSTC").unwrap()))),
|
|
112
|
-
format!("\"cc\":{}", quote(&version(&cc))),
|
|
113
|
-
format!("\"cxx\":{}", quote(&version(&cxx))),
|
|
114
|
-
];
|
|
115
|
-
for key in [
|
|
116
|
-
"CC",
|
|
117
|
-
"CXX",
|
|
118
|
-
"CARGO_ENCODED_RUSTFLAGS",
|
|
119
|
-
"CFLAGS",
|
|
120
|
-
"CXXFLAGS",
|
|
121
|
-
"MACOSX_DEPLOYMENT_TARGET",
|
|
122
|
-
] {
|
|
123
|
-
println!("cargo:rerun-if-env-changed={key}");
|
|
124
|
-
if let Ok(value) = env::var(key) {
|
|
125
|
-
fields.push(format!("{}:{}", quote(key), quote(&value)));
|
|
126
|
-
}
|
|
219
|
+
.ok_or("the hash tool has no stdin")?
|
|
220
|
+
.write_all(input)?;
|
|
221
|
+
let output = child.wait_with_output()?;
|
|
222
|
+
if !output.status.success() {
|
|
223
|
+
return Err(format!("native source hash failed: {}", output.status).into());
|
|
224
|
+
}
|
|
225
|
+
let text = String::from_utf8(output.stdout)?;
|
|
226
|
+
let digest = text.split_whitespace().next().unwrap_or_default();
|
|
227
|
+
if digest.len() != 64 || !digest.bytes().all(|byte| byte.is_ascii_hexdigit()) {
|
|
228
|
+
return Err(format!("unexpected SHA-256 digest {digest:?}").into());
|
|
127
229
|
}
|
|
128
|
-
|
|
129
|
-
"cargo:rustc-env=REACTOR_EFFECT_BUILD_IDENTITY={{{}}}",
|
|
130
|
-
fields.join(",")
|
|
131
|
-
);
|
|
230
|
+
Ok(digest.to_owned())
|
|
132
231
|
}
|
|
133
232
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
233
|
+
/// The first line of `program --version`.
|
|
234
|
+
fn tool_version(program: &str) -> BuildResult<String> {
|
|
235
|
+
let output = Command::new(program)
|
|
236
|
+
.arg("--version")
|
|
237
|
+
.output()
|
|
238
|
+
.map_err(|error| format!("could not inspect native build tool {program}: {error}"))?;
|
|
239
|
+
if !output.status.success() {
|
|
240
|
+
return Err(format!("native build tool {program} failed: {}", output.status).into());
|
|
241
|
+
}
|
|
242
|
+
let text = String::from_utf8(output.stdout)?;
|
|
243
|
+
Ok(text.lines().next().unwrap_or_default().to_owned())
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/// `value` as a JSON string literal.
|
|
247
|
+
fn json_string(value: &str) -> String {
|
|
248
|
+
JsonString(value).to_string()
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/// Formats a string as a JSON string literal.
|
|
252
|
+
struct JsonString<'a>(&'a str);
|
|
253
|
+
|
|
254
|
+
impl fmt::Display for JsonString<'_> {
|
|
255
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
256
|
+
f.write_char('"')?;
|
|
257
|
+
for ch in self.0.chars() {
|
|
258
|
+
match ch {
|
|
259
|
+
'"' => f.write_str("\\\"")?,
|
|
260
|
+
'\\' => f.write_str("\\\\")?,
|
|
261
|
+
'\n' => f.write_str("\\n")?,
|
|
262
|
+
'\r' => f.write_str("\\r")?,
|
|
263
|
+
'\t' => f.write_str("\\t")?,
|
|
264
|
+
ch if ch.is_control() => write!(f, "\\u{:04x}", u32::from(ch))?,
|
|
265
|
+
ch => f.write_char(ch)?,
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
f.write_char('"')
|
|
138
269
|
}
|
|
270
|
+
}
|
|
139
271
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
272
|
+
/// The pinned Reactor libwebrtc archive contains `ScreenCaptureKit` objects
|
|
273
|
+
/// produced by Clang. They reference compiler-rt's deployment-version helper,
|
|
274
|
+
/// which rustc's macOS link line does not add on its own.
|
|
275
|
+
fn link_compiler_rt() -> BuildResult<()> {
|
|
143
276
|
let cc = env::var("CC").unwrap_or_else(|_| "clang".to_owned());
|
|
144
277
|
let output = Command::new(cc)
|
|
145
278
|
.arg("-print-resource-dir")
|
|
146
279
|
.output()
|
|
147
|
-
.
|
|
148
|
-
|
|
149
|
-
|
|
280
|
+
.map_err(|error| {
|
|
281
|
+
format!("clang is required to link the Reactor libwebrtc archive on macOS: {error}")
|
|
282
|
+
})?;
|
|
283
|
+
if !output.status.success() {
|
|
284
|
+
return Err(format!("clang -print-resource-dir failed: {}", output.status).into());
|
|
285
|
+
}
|
|
286
|
+
let resource = String::from_utf8(output.stdout)?;
|
|
150
287
|
let directory = PathBuf::from(resource.trim()).join("lib/darwin");
|
|
151
288
|
let runtime = directory.join("libclang_rt.osx.a");
|
|
152
|
-
|
|
153
|
-
runtime.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
|
|
289
|
+
if !runtime.is_file() {
|
|
290
|
+
return Err(format!("missing macOS compiler-rt archive at {}", runtime.display()).into());
|
|
291
|
+
}
|
|
292
|
+
println!("cargo::rustc-link-search=native={}", directory.display());
|
|
293
|
+
println!("cargo::rustc-link-lib=static=clang_rt.osx");
|
|
294
|
+
// Cargo passes rustc-link-lib only to the library target, and the far-peer
|
|
295
|
+
// example cannot link this cdylib, so it names the archive itself.
|
|
296
|
+
println!("cargo::rustc-link-arg-examples={}", runtime.display());
|
|
297
|
+
Ok(())
|
|
159
298
|
}
|
package/rust/clippy.toml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Lint levels live in Cargo.toml's [lints] table; this file only configures them.
|
|
2
|
+
|
|
3
|
+
# In a test, a failed unwrap, expect, index or panic is the assertion.
|
|
4
|
+
allow-unwrap-in-tests = true
|
|
5
|
+
allow-expect-in-tests = true
|
|
6
|
+
allow-indexing-slicing-in-tests = true
|
|
7
|
+
allow-panic-in-tests = true
|
|
@@ -8,16 +8,30 @@
|
|
|
8
8
|
extern "C" {
|
|
9
9
|
#endif
|
|
10
10
|
|
|
11
|
+
/* ABI 4. Every supported host is little-endian; headers are native-endian. */
|
|
12
|
+
|
|
11
13
|
typedef struct ReactorEffectPeer ReactorEffectPeer;
|
|
12
14
|
|
|
15
|
+
/*
|
|
16
|
+
* Non-negative statuses are outcomes. Negative statuses are failure classes;
|
|
17
|
+
* the set is closed for this ABI, and a host maps each class to its own error
|
|
18
|
+
* type. Diagnostic text travels beside a class in ReactorEffectFailure.
|
|
19
|
+
*
|
|
20
|
+
* A misaligned pointer argument fails with INVALID_INPUT without being used.
|
|
21
|
+
* A request over 1 MiB or a message over 256 KiB fails with OVERFLOW without
|
|
22
|
+
* being read.
|
|
23
|
+
*/
|
|
13
24
|
enum ReactorEffectStatus {
|
|
14
25
|
REACTOR_EFFECT_OK = 0,
|
|
15
|
-
REACTOR_EFFECT_AGAIN = 1,
|
|
16
|
-
REACTOR_EFFECT_BUFFER_TOO_SMALL = 2,
|
|
17
|
-
REACTOR_EFFECT_CLOSED = 3,
|
|
18
|
-
|
|
19
|
-
REACTOR_EFFECT_NATIVE = -2,
|
|
20
|
-
REACTOR_EFFECT_OVERFLOW = -3
|
|
26
|
+
REACTOR_EFFECT_AGAIN = 1, /* take: the queue is empty */
|
|
27
|
+
REACTOR_EFFECT_BUFFER_TOO_SMALL = 2, /* sizes written; the item stays queued */
|
|
28
|
+
REACTOR_EFFECT_CLOSED = 3, /* the peer is fenced or shut down */
|
|
29
|
+
REACTOR_EFFECT_INVALID_INPUT = -1, /* an argument or request was rejected */
|
|
30
|
+
REACTOR_EFFECT_NATIVE = -2, /* an unclassified libwebrtc or bridge failure */
|
|
31
|
+
REACTOR_EFFECT_OVERFLOW = -3, /* a queue, buffer or message bound was exceeded */
|
|
32
|
+
REACTOR_EFFECT_PROTOCOL = -4, /* the remote peer broke the negotiated contract */
|
|
33
|
+
REACTOR_EFFECT_SDP_REJECTED = -5, /* libwebrtc refused to create or apply an SDP */
|
|
34
|
+
REACTOR_EFFECT_CHANNEL_CLOSED = -6 /* the data channel is not open */
|
|
21
35
|
};
|
|
22
36
|
|
|
23
37
|
enum ReactorEffectCall {
|
|
@@ -34,15 +48,63 @@ enum ReactorEffectChannel {
|
|
|
34
48
|
REACTOR_EFFECT_DATA = 1
|
|
35
49
|
};
|
|
36
50
|
|
|
51
|
+
/* Readiness bits passed to ReactorEffectNotify. */
|
|
52
|
+
enum ReactorEffectReady {
|
|
53
|
+
REACTOR_EFFECT_READY_EVENTS = 1,
|
|
54
|
+
REACTOR_EFFECT_READY_VIDEO = 2,
|
|
55
|
+
REACTOR_EFFECT_READY_AUDIO = 4
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/* Diagnostic text for a failure status. Never match on it: the status is the class. */
|
|
59
|
+
typedef struct ReactorEffectFailure {
|
|
60
|
+
uint32_t message_len;
|
|
61
|
+
uint8_t message[1020]; /* UTF-8, truncated on a character boundary */
|
|
62
|
+
} ReactorEffectFailure;
|
|
63
|
+
|
|
64
|
+
typedef struct ReactorEffectVideoHeader {
|
|
65
|
+
uint32_t width;
|
|
66
|
+
uint32_t height;
|
|
67
|
+
uint32_t data_len; /* BGRA bytes: width * height * 4 */
|
|
68
|
+
uint32_t metadata_len; /* frame-metadata user data bytes */
|
|
69
|
+
uint64_t frame_id; /* 0 when the sender supplied none */
|
|
70
|
+
uint64_t timestamp_us; /* sender capture time; 0 when absent */
|
|
71
|
+
uint32_t track; /* index into the prepare request's tracks */
|
|
72
|
+
uint32_t reserved;
|
|
73
|
+
uint64_t sequence; /* per-track admission sequence from 0, stamped before any queue drop */
|
|
74
|
+
} ReactorEffectVideoHeader; /* 48 bytes */
|
|
75
|
+
|
|
76
|
+
typedef struct ReactorEffectAudioHeader {
|
|
77
|
+
uint32_t sample_rate;
|
|
78
|
+
uint32_t channels;
|
|
79
|
+
uint32_t samples; /* interleaved int16_t samples: frames * channels */
|
|
80
|
+
uint32_t track; /* index into the prepare request's tracks */
|
|
81
|
+
uint64_t sequence; /* per-track admission sequence from 0, stamped before any queue drop */
|
|
82
|
+
} ReactorEffectAudioHeader; /* 24 bytes */
|
|
83
|
+
|
|
84
|
+
/*
|
|
85
|
+
* Runs on the peer's notifier thread with the readiness bits of every queue
|
|
86
|
+
* that received an item since the previous call; a host must drain each named
|
|
87
|
+
* queue until AGAIN. The callback may call the take functions. libwebrtc
|
|
88
|
+
* threads never run it and never wait for it: while it has not returned, they
|
|
89
|
+
* keep queueing and the bounded queues count what they evict.
|
|
90
|
+
*/
|
|
91
|
+
typedef void (*ReactorEffectNotify)(uint32_t ready);
|
|
92
|
+
|
|
37
93
|
uint32_t reactor_effect_abi_version(void);
|
|
38
94
|
/* Static source/build identity of this loaded image; never free the pointer. */
|
|
39
95
|
const char *reactor_effect_build_identity(void);
|
|
40
96
|
|
|
41
|
-
|
|
97
|
+
/*
|
|
98
|
+
* notify may be NULL: no notifier thread runs and the host polls the take
|
|
99
|
+
* functions itself. Every peer shares one process-wide libwebrtc factory,
|
|
100
|
+
* created on the first prepare and never destroyed.
|
|
101
|
+
*/
|
|
102
|
+
ReactorEffectPeer *reactor_effect_peer_create(ReactorEffectNotify notify);
|
|
42
103
|
|
|
43
104
|
/*
|
|
44
|
-
* request is UTF-8.
|
|
45
|
-
*
|
|
105
|
+
* request is UTF-8. On OK the response is UTF-8 JSON and response_cap must be
|
|
106
|
+
* at least 4 MiB (BUFFER_TOO_SMALL reports that size). Calls are serialized by
|
|
107
|
+
* the native peer owner and may block on libwebrtc. failure may be NULL.
|
|
46
108
|
*/
|
|
47
109
|
int reactor_effect_peer_call(
|
|
48
110
|
ReactorEffectPeer *peer,
|
|
@@ -51,63 +113,60 @@ int reactor_effect_peer_call(
|
|
|
51
113
|
size_t request_len,
|
|
52
114
|
uint8_t *response,
|
|
53
115
|
size_t response_cap,
|
|
54
|
-
size_t *response_len
|
|
116
|
+
size_t *response_len,
|
|
117
|
+
ReactorEffectFailure *failure);
|
|
55
118
|
|
|
56
119
|
int reactor_effect_peer_send(
|
|
57
120
|
ReactorEffectPeer *peer,
|
|
58
121
|
uint32_t channel,
|
|
59
122
|
const uint8_t *data,
|
|
60
123
|
size_t data_len,
|
|
61
|
-
|
|
62
|
-
size_t error_cap,
|
|
63
|
-
size_t *error_len);
|
|
124
|
+
ReactorEffectFailure *failure);
|
|
64
125
|
|
|
65
126
|
/*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* permits one reader across probe/copy; the host must serialize that pair.
|
|
71
|
-
* timeout_ms == 0 is a nonblocking poll.
|
|
127
|
+
* Nonblocking takes. Each copies its queue's oldest item into caller memory
|
|
128
|
+
* once and removes it. BUFFER_TOO_SMALL writes the required size (out_len or
|
|
129
|
+
* the header) and keeps the item at the front; AGAIN means the queue is empty.
|
|
130
|
+
* Events are [u32 little-endian header length][UTF-8 JSON header][payload].
|
|
72
131
|
*/
|
|
73
|
-
int
|
|
132
|
+
int reactor_effect_peer_take_event(
|
|
74
133
|
ReactorEffectPeer *peer,
|
|
75
|
-
uint32_t timeout_ms,
|
|
76
134
|
uint8_t *out,
|
|
77
135
|
size_t out_cap,
|
|
78
136
|
size_t *out_len);
|
|
79
137
|
|
|
80
|
-
int
|
|
138
|
+
int reactor_effect_peer_take_video(
|
|
81
139
|
ReactorEffectPeer *peer,
|
|
82
|
-
|
|
83
|
-
uint8_t *
|
|
84
|
-
size_t
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
140
|
+
ReactorEffectVideoHeader *header,
|
|
141
|
+
uint8_t *bgra,
|
|
142
|
+
size_t bgra_cap,
|
|
143
|
+
uint8_t *metadata,
|
|
144
|
+
size_t metadata_cap);
|
|
145
|
+
|
|
146
|
+
/* pcm_cap counts int16_t samples, not bytes. */
|
|
147
|
+
int reactor_effect_peer_take_audio(
|
|
88
148
|
ReactorEffectPeer *peer,
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
size_t
|
|
92
|
-
size_t *out_len);
|
|
149
|
+
ReactorEffectAudioHeader *header,
|
|
150
|
+
int16_t *pcm,
|
|
151
|
+
size_t pcm_cap);
|
|
93
152
|
|
|
94
153
|
/* Immediate admission fence. No callback/event can be admitted after return. */
|
|
95
154
|
void reactor_effect_peer_close(ReactorEffectPeer *peer);
|
|
96
155
|
|
|
97
156
|
/*
|
|
98
|
-
* Drops channels/tracks/PeerConnection on the owner thread, waits for all
|
|
99
|
-
* callback guards to leave, then joins that owner
|
|
157
|
+
* Drops channels/tracks/PeerConnection on the owner thread, waits for all
|
|
158
|
+
* native callback guards to leave, then joins that owner and the notifier
|
|
159
|
+
* thread. The notifier may be waiting for the host to run ReactorEffectNotify,
|
|
160
|
+
* so never call this from the thread that runs that callback. Safe to call
|
|
161
|
+
* more than once. failure may be NULL.
|
|
100
162
|
*/
|
|
101
|
-
int reactor_effect_peer_shutdown(
|
|
102
|
-
ReactorEffectPeer *peer,
|
|
103
|
-
uint8_t *error,
|
|
104
|
-
size_t error_cap,
|
|
105
|
-
size_t *error_len);
|
|
163
|
+
int reactor_effect_peer_shutdown(ReactorEffectPeer *peer, ReactorEffectFailure *failure);
|
|
106
164
|
|
|
107
165
|
/*
|
|
108
|
-
* Performs shutdown if needed
|
|
109
|
-
* first join EVERY foreign call using this
|
|
110
|
-
* in its FFI executor. Native owner
|
|
166
|
+
* Performs shutdown if needed (so the same thread rule applies), then frees
|
|
167
|
+
* the opaque handle. The host must first join EVERY foreign call using this
|
|
168
|
+
* handle, including work still queued in its FFI executor. Native owner
|
|
169
|
+
* shutdown alone does not establish that.
|
|
111
170
|
*/
|
|
112
171
|
void reactor_effect_peer_destroy(ReactorEffectPeer *peer);
|
|
113
172
|
|