f31522130f
Build system (5 gaps hardened): - COOKBOOK_OFFLINE defaults to true (fork-mode) - normalize_patch handles diff -ruN format - New 'repo validate-patches' command (25/25 relibc patches) - 14 patched Qt/Wayland/display recipes added to protected list - relibc archive regenerated with current patch chain Boot fixes (fixable): - Full ISO EFI partition: 16 MiB → 1 MiB (matches mini, BIOS hardcoded 2 MiB offset) - D-Bus system bus: absolute /usr/bin/dbus-daemon path (was skipped) - redbear-sessiond: absolute /usr/bin/redbear-sessiond path (was skipped) - daemon framework: silenced spurious INIT_NOTIFY warnings for oneshot_async services (P0-daemon-silence-init-notify.patch) - udev-shim: demoted INIT_NOTIFY warning to INFO (expected for oneshot_async) - relibc: comprehensive named semaphores (sem_open/close/unlink) replacing upstream todo!() stubs - greeterd: Wayland socket timeout 15s → 30s (compositor DRM wait) - greeter-ui: built and linked (header guard unification, sem_compat stubs removed) - mc: un-ignored in both configs, fixed glib/libiconv/pcre2 transitive deps - greeter config: removed stale keymapd dependency from display/greeter services - prefix toolchain: relibc headers synced, _RELIBC_STDLIB_H guard unified Unfixable (diagnosed, upstream): - i2c-hidd: abort on no-I2C-hardware (QEMU) — process::exit → relibc abort - kded6/greeter-ui: page fault 0x8 — Qt library null deref - Thread panics fd != -1 — Rust std library on Redox - DHCP timeout / eth0 MAC — QEMU user-mode networking - hwrngd/thermald — no hardware RNG/thermal in VM - live preload allocation — BIOS memory fragmentation, continues on demand
240 lines
7.5 KiB
JavaScript
240 lines
7.5 KiB
JavaScript
// Copyright (C) 2022 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
// Exposes platform capabilities as static properties
|
|
|
|
export class AbortedError extends Error {
|
|
constructor(stdout) {
|
|
super(`The program has been aborted`)
|
|
|
|
this.stdout = stdout;
|
|
}
|
|
}
|
|
export class Platform {
|
|
static #webAssemblySupported = typeof WebAssembly !== 'undefined';
|
|
|
|
static #canCompileStreaming = WebAssembly.compileStreaming !== 'undefined';
|
|
|
|
static #webGLSupported = (() => {
|
|
// We expect that WebGL is supported if WebAssembly is; however
|
|
// the GPU may be blacklisted.
|
|
try {
|
|
const canvas = document.createElement('canvas');
|
|
return !!(
|
|
window.WebGLRenderingContext &&
|
|
(canvas.getContext('webgl') || canvas.getContext('experimental-webgl'))
|
|
);
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
})();
|
|
|
|
static #canLoadQt = Platform.#webAssemblySupported && Platform.#webGLSupported;
|
|
|
|
static get webAssemblySupported() {
|
|
return this.#webAssemblySupported;
|
|
}
|
|
static get canCompileStreaming() {
|
|
return this.#canCompileStreaming;
|
|
}
|
|
static get webGLSupported() {
|
|
return this.#webGLSupported;
|
|
}
|
|
static get canLoadQt() {
|
|
return this.#canLoadQt;
|
|
}
|
|
}
|
|
|
|
// Locates a resource, based on its relative path
|
|
export class ResourceLocator {
|
|
#rootPath;
|
|
|
|
constructor(rootPath) {
|
|
this.#rootPath = rootPath;
|
|
if (rootPath.length > 0 && !rootPath.endsWith('/')) rootPath += '/';
|
|
}
|
|
|
|
locate(relativePath) {
|
|
return this.#rootPath + relativePath;
|
|
}
|
|
}
|
|
|
|
// Allows fetching of resources, such as text resources or wasm modules.
|
|
export class ResourceFetcher {
|
|
#locator;
|
|
|
|
constructor(locator) {
|
|
this.#locator = locator;
|
|
}
|
|
|
|
async fetchText(filePath) {
|
|
return (await this.#fetchRawResource(filePath)).text();
|
|
}
|
|
|
|
async fetchCompileWasm(filePath, onFetched) {
|
|
const fetchResponse = await this.#fetchRawResource(filePath);
|
|
onFetched?.();
|
|
|
|
if (Platform.canCompileStreaming) {
|
|
try {
|
|
return await WebAssembly.compileStreaming(fetchResponse);
|
|
} catch {
|
|
// NOOP - fallback to sequential fetching below
|
|
}
|
|
}
|
|
return WebAssembly.compile(await fetchResponse.arrayBuffer());
|
|
}
|
|
|
|
async #fetchRawResource(filePath) {
|
|
const response = await fetch(this.#locator.locate(filePath));
|
|
if (!response.ok)
|
|
throw new Error(
|
|
`${response.status} ${response.statusText} ${response.url}`
|
|
);
|
|
return response;
|
|
}
|
|
}
|
|
|
|
// Represents a WASM module, wrapping the instantiation and execution thereof.
|
|
export class CompiledModule {
|
|
#createQtAppInstanceFn;
|
|
#js;
|
|
#wasm;
|
|
#resourceLocator;
|
|
#qtContainerElements;
|
|
|
|
constructor(createQtAppInstanceFn, js, wasm, resourceLocator, qtContainerElements) {
|
|
this.#createQtAppInstanceFn = createQtAppInstanceFn;
|
|
this.#js = js;
|
|
this.#wasm = wasm;
|
|
this.#resourceLocator = resourceLocator;
|
|
this.#qtContainerElements = qtContainerElements;
|
|
}
|
|
|
|
static make(js, wasm, entryFunctionName, resourceLocator, qtContainerElements)
|
|
{
|
|
const exports = {};
|
|
const module = {};
|
|
eval(js);
|
|
if (!module.exports) {
|
|
throw new Error(
|
|
'${entryFunctionName} has not been exported by the main script'
|
|
);
|
|
}
|
|
|
|
return new CompiledModule(
|
|
module.exports, js, wasm, resourceLocator, qtContainerElements
|
|
);
|
|
}
|
|
|
|
async exec(parameters) {
|
|
return await new Promise(async (resolve, reject) => {
|
|
let instance = undefined;
|
|
let result = undefined;
|
|
|
|
let testFinished = false;
|
|
const testFinishedEvent = new CustomEvent('testFinished');
|
|
instance = await this.#createQtAppInstanceFn((() => {
|
|
const params = this.#makeDefaultExecParams({
|
|
onInstantiationError: (error) => { reject(error); },
|
|
});
|
|
params.arguments = parameters?.args;
|
|
let data = '';
|
|
params.print = (out) => {
|
|
parameters?.onStdout?.(out);
|
|
data += `${out}\n`;
|
|
};
|
|
params.printErr = () => { };
|
|
params.onAbort = () => reject(new AbortedError(data));
|
|
params.quit = (code, exception) => {
|
|
if (exception && exception.name !== 'ExitStatus')
|
|
reject(exception);
|
|
};
|
|
params.notifyTestFinished = (code) => {
|
|
result = { stdout: data, exitCode: code };
|
|
testFinished = true;
|
|
window.dispatchEvent(testFinishedEvent);
|
|
};
|
|
return params;
|
|
})());
|
|
if (!testFinished) {
|
|
await new Promise((resolve) => {
|
|
window.addEventListener('testFinished', () => {
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
resolve({
|
|
stdout: result.stdout,
|
|
exitCode: result.exitCode,
|
|
instance,
|
|
});
|
|
});
|
|
}
|
|
|
|
#makeDefaultExecParams(params) {
|
|
const instanceParams = {};
|
|
instanceParams.instantiateWasm = async (imports, onDone) => {
|
|
try {
|
|
onDone(await WebAssembly.instantiate(this.#wasm, imports), this.#wasm);
|
|
} catch (e) {
|
|
params?.onInstantiationError?.(e);
|
|
}
|
|
};
|
|
instanceParams.locateFile = (filename) =>
|
|
this.#resourceLocator.locate(filename);
|
|
instanceParams.monitorRunDependencies = (name) => { };
|
|
instanceParams.print = (text) => true && console.log(text);
|
|
instanceParams.printErr = (text) => true && console.warn(text);
|
|
|
|
instanceParams.mainScriptUrlOrBlob = new Blob([this.#js], {
|
|
type: 'text/javascript',
|
|
});
|
|
|
|
// Add Qt container elements if provided
|
|
if (this.#qtContainerElements) {
|
|
instanceParams.qtContainerElements = this.#qtContainerElements;
|
|
}
|
|
|
|
return instanceParams;
|
|
}
|
|
}
|
|
|
|
// Streamlines loading of WASM modules.
|
|
export class ModuleLoader {
|
|
#fetcher;
|
|
#resourceLocator;
|
|
|
|
constructor(
|
|
fetcher,
|
|
resourceLocator
|
|
) {
|
|
this.#fetcher = fetcher;
|
|
this.#resourceLocator = resourceLocator;
|
|
}
|
|
|
|
// Loads an emscripten module named |moduleName| from the main resource path. Provides
|
|
// progress of 'downloading' and 'compiling' to the caller using the |onProgress| callback.
|
|
async loadEmscriptenModule(
|
|
moduleName, onProgress, qtContainerElements
|
|
) {
|
|
if (!Platform.webAssemblySupported)
|
|
throw new Error('Web assembly not supported');
|
|
if (!Platform.webGLSupported)
|
|
throw new Error('WebGL is not supported');
|
|
|
|
onProgress('downloading');
|
|
|
|
const jsLoadPromise = this.#fetcher.fetchText(`${moduleName}.js`);
|
|
const wasmLoadPromise = this.#fetcher.fetchCompileWasm(
|
|
`${moduleName}.wasm`,
|
|
() => {
|
|
onProgress('compiling');
|
|
}
|
|
);
|
|
|
|
const [js, wasm] = await Promise.all([jsLoadPromise, wasmLoadPromise]);
|
|
return CompiledModule.make(js, wasm, `${moduleName}_entry`, this.#resourceLocator, qtContainerElements);
|
|
}
|
|
}
|