@swifttui/build 0.0.14 → 0.0.16
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/README.md +23 -4
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +77 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/src/build/buildAppWasm.d.ts +21 -0
- package/dist/src/build/buildAppWasm.js +61 -0
- package/dist/src/build/buildAppWasm.js.map +1 -0
- package/dist/src/build/buildSwiftTUIWebApp.d.ts +10 -0
- package/dist/src/build/buildSwiftTUIWebApp.js +23 -0
- package/dist/src/build/buildSwiftTUIWebApp.js.map +1 -0
- package/dist/src/build/generateSceneManifest.d.ts +13 -0
- package/dist/src/build/generateSceneManifest.js +28 -0
- package/dist/src/build/generateSceneManifest.js.map +1 -0
- package/dist/src/build/optimizePackagedWasm.js +17 -0
- package/dist/src/build/optimizePackagedWasm.js.map +1 -0
- package/dist/src/build/resolveSwiftArtifacts.d.ts +39 -0
- package/dist/src/build/resolveSwiftArtifacts.js +161 -0
- package/dist/src/build/resolveSwiftArtifacts.js.map +1 -0
- package/dist/src/build/runCommand.js +55 -0
- package/dist/src/build/runCommand.js.map +1 -0
- package/dist/src/build/stripPackagedWasm.js +16 -0
- package/dist/src/build/stripPackagedWasm.js.map +1 -0
- package/dist/src/build/swiftCommandPrefix.js +14 -0
- package/dist/src/build/swiftCommandPrefix.js.map +1 -0
- package/dist/src/build/wasmTypeDiagnostics.js +204 -0
- package/dist/src/build/wasmTypeDiagnostics.js.map +1 -0
- package/package.json +25 -11
- package/AGENTS.md +0 -44
- package/cli.ts +0 -98
- package/index.ts +0 -4
- package/src/build/buildAppWasm.test.ts +0 -178
- package/src/build/buildAppWasm.ts +0 -107
- package/src/build/buildSwiftTUIWebApp.ts +0 -22
- package/src/build/generateSceneManifest.ts +0 -46
- package/src/build/optimizePackagedWasm.ts +0 -20
- package/src/build/resolveSwiftArtifacts.test.ts +0 -90
- package/src/build/resolveSwiftArtifacts.ts +0 -253
- package/src/build/runCommand.ts +0 -81
- package/src/build/stripPackagedWasm.ts +0 -19
- package/src/build/swiftCommandPrefix.ts +0 -9
- package/src/build/wasmTypeDiagnostics.ts +0 -313
- package/tsconfig.json +0 -21
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { optimizePackagedWasm } from "./optimizePackagedWasm.ts";
|
|
4
|
-
import {
|
|
5
|
-
resolveSwiftArtifacts,
|
|
6
|
-
type ResolveSwiftArtifactsOptions,
|
|
7
|
-
type SwiftArtifactPaths,
|
|
8
|
-
type WasmBuildConfiguration,
|
|
9
|
-
} from "./resolveSwiftArtifacts.ts";
|
|
10
|
-
import { stripPackagedWasm } from "./stripPackagedWasm.ts";
|
|
11
|
-
import { formatWasmTypeDiagnostics } from "./wasmTypeDiagnostics.ts";
|
|
12
|
-
|
|
13
|
-
export interface BuildAppWasmOptions extends ResolveSwiftArtifactsOptions {
|
|
14
|
-
configuration?: WasmBuildConfiguration;
|
|
15
|
-
packagePath: string;
|
|
16
|
-
outputDirectory: string;
|
|
17
|
-
product: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function buildAppWasm(
|
|
21
|
-
options: BuildAppWasmOptions
|
|
22
|
-
): Promise<SwiftArtifactPaths> {
|
|
23
|
-
const artifacts = await resolveSwiftArtifacts(options);
|
|
24
|
-
|
|
25
|
-
const packagedWasmPath = join(options.outputDirectory, "assets", "app.wasm");
|
|
26
|
-
await mkdir(join(options.outputDirectory, "assets"), { recursive: true });
|
|
27
|
-
await rm(packagedWasmPath, { force: true });
|
|
28
|
-
await packageBrowserValidatedWasm({
|
|
29
|
-
sourceWasmPath: artifacts.wasmPath,
|
|
30
|
-
outputWasmPath: packagedWasmPath,
|
|
31
|
-
});
|
|
32
|
-
return artifacts;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
interface PackageBrowserValidatedWasmOptions {
|
|
36
|
-
optimize?: (wasmPath: string) => Promise<void>;
|
|
37
|
-
sourceWasmPath: string;
|
|
38
|
-
outputWasmPath: string;
|
|
39
|
-
strip?: (wasmPath: string) => Promise<void>;
|
|
40
|
-
onWarning?: (message: string) => void;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export async function packageBrowserValidatedWasm(
|
|
44
|
-
options: PackageBrowserValidatedWasmOptions
|
|
45
|
-
): Promise<void> {
|
|
46
|
-
const sourceBytes = await readFile(options.sourceWasmPath);
|
|
47
|
-
await writeFile(options.outputWasmPath, sourceBytes);
|
|
48
|
-
|
|
49
|
-
const optimize = options.optimize ?? optimizePackagedWasm;
|
|
50
|
-
try {
|
|
51
|
-
await optimize(options.outputWasmPath);
|
|
52
|
-
await validateBrowserWasm(options.outputWasmPath, "optimized wasm");
|
|
53
|
-
} catch (error) {
|
|
54
|
-
await writeFile(options.outputWasmPath, sourceBytes);
|
|
55
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
await validateBrowserWasm(options.outputWasmPath, "generated wasm");
|
|
59
|
-
} catch (rawError) {
|
|
60
|
-
const rawMessage = rawError instanceof Error ? rawError.message : String(rawError);
|
|
61
|
-
throw new Error([
|
|
62
|
-
rawMessage,
|
|
63
|
-
`wasm optimization step failed: ${message}`,
|
|
64
|
-
].join("\n"));
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const warning = [
|
|
68
|
-
`warning: keeping unoptimized wasm at ${options.outputWasmPath}`,
|
|
69
|
-
`wasm optimization step failed or did not produce browser-parseable output: ${message}`,
|
|
70
|
-
].join("\n");
|
|
71
|
-
(options.onWarning ?? console.warn)(warning);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const strip = options.strip ?? stripPackagedWasm;
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
await strip(options.outputWasmPath);
|
|
78
|
-
await validateBrowserWasm(options.outputWasmPath, "stripped wasm");
|
|
79
|
-
} catch (error) {
|
|
80
|
-
// Stripping is a size optimization only. Keep the known-good raw wasm
|
|
81
|
-
// whenever toolchain-specific objcopy output fails browser validation.
|
|
82
|
-
await writeFile(options.outputWasmPath, sourceBytes);
|
|
83
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
84
|
-
const warning = [
|
|
85
|
-
`warning: keeping unstripped wasm at ${options.outputWasmPath}`,
|
|
86
|
-
`strip step failed browser validation or tooling requirements: ${message}`,
|
|
87
|
-
].join("\n");
|
|
88
|
-
(options.onWarning ?? console.warn)(warning);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async function validateBrowserWasm(
|
|
93
|
-
wasmPath: string,
|
|
94
|
-
description: string
|
|
95
|
-
): Promise<void> {
|
|
96
|
-
const bytes = await readFile(wasmPath);
|
|
97
|
-
try {
|
|
98
|
-
// Validate against the same JS API the browser uses before we publish it.
|
|
99
|
-
await WebAssembly.compile(bytes);
|
|
100
|
-
} catch (error) {
|
|
101
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
102
|
-
throw new Error([
|
|
103
|
-
`${description} does not parse in browser WebAssembly (${wasmPath}): ${message}`,
|
|
104
|
-
formatWasmTypeDiagnostics(bytes),
|
|
105
|
-
].join("\n"));
|
|
106
|
-
}
|
|
107
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { mkdir, rm } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { buildAppWasm, type BuildAppWasmOptions } from "./buildAppWasm.ts";
|
|
4
|
-
import { generateSceneManifest } from "./generateSceneManifest.ts";
|
|
5
|
-
|
|
6
|
-
export interface BuildSwiftTUIWebAppOptions extends BuildAppWasmOptions {
|
|
7
|
-
appExecutable?: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export async function buildSwiftTUIWebApp(
|
|
11
|
-
options: BuildSwiftTUIWebAppOptions
|
|
12
|
-
): Promise<void> {
|
|
13
|
-
await rm(options.outputDirectory, { recursive: true, force: true });
|
|
14
|
-
await mkdir(options.outputDirectory, { recursive: true });
|
|
15
|
-
await generateSceneManifest({
|
|
16
|
-
packagePath: options.packagePath,
|
|
17
|
-
outputPath: join(options.outputDirectory, "scene-manifest.json"),
|
|
18
|
-
appExecutable: options.appExecutable ?? options.product,
|
|
19
|
-
swiftCommand: options.swiftCommand,
|
|
20
|
-
});
|
|
21
|
-
await buildAppWasm(options);
|
|
22
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
loadWebHostSceneManifest,
|
|
5
|
-
webTUISceneManifestToJSON,
|
|
6
|
-
type WebHostSceneManifest,
|
|
7
|
-
} from "@swifttui/web/manifest";
|
|
8
|
-
import { runCommand } from "./runCommand.ts";
|
|
9
|
-
import { swiftCommandPrefix } from "./swiftCommandPrefix.ts";
|
|
10
|
-
|
|
11
|
-
export interface GenerateSceneManifestOptions {
|
|
12
|
-
packagePath: string;
|
|
13
|
-
outputPath: string;
|
|
14
|
-
appExecutable: string;
|
|
15
|
-
swiftCommand?: readonly string[];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export async function generateSceneManifest(
|
|
19
|
-
options: GenerateSceneManifestOptions
|
|
20
|
-
): Promise<WebHostSceneManifest> {
|
|
21
|
-
const output = await runManifestCommand(options);
|
|
22
|
-
const manifest = await loadWebHostSceneManifest(output.trim());
|
|
23
|
-
await mkdir(dirname(options.outputPath), { recursive: true });
|
|
24
|
-
await writeFile(options.outputPath, webTUISceneManifestToJSON(manifest));
|
|
25
|
-
return manifest;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async function runManifestCommand(
|
|
29
|
-
options: GenerateSceneManifestOptions
|
|
30
|
-
): Promise<string> {
|
|
31
|
-
return await runCommand(
|
|
32
|
-
[
|
|
33
|
-
...(options.swiftCommand ?? swiftCommandPrefix()),
|
|
34
|
-
"run",
|
|
35
|
-
"--package-path",
|
|
36
|
-
options.packagePath,
|
|
37
|
-
options.appExecutable,
|
|
38
|
-
],
|
|
39
|
-
{
|
|
40
|
-
env: {
|
|
41
|
-
...process.env,
|
|
42
|
-
TUIGUI_MODE: "manifest",
|
|
43
|
-
},
|
|
44
|
-
}
|
|
45
|
-
);
|
|
46
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { findExecutable, runCommand } from "./runCommand.ts";
|
|
2
|
-
|
|
3
|
-
export async function optimizePackagedWasm(
|
|
4
|
-
wasmPath: string
|
|
5
|
-
): Promise<void> {
|
|
6
|
-
const wasmOptPath = findExecutable("wasm-opt");
|
|
7
|
-
if (!wasmOptPath) {
|
|
8
|
-
throw new Error(
|
|
9
|
-
"missing wasm-opt in PATH; install Binaryen so wasm packaging is deterministic across environments"
|
|
10
|
-
);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
await runCommand([
|
|
14
|
-
wasmOptPath,
|
|
15
|
-
"-Os",
|
|
16
|
-
wasmPath,
|
|
17
|
-
"-o",
|
|
18
|
-
wasmPath,
|
|
19
|
-
]);
|
|
20
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "bun:test";
|
|
2
|
-
import {
|
|
3
|
-
formatCommandForLogs,
|
|
4
|
-
hasRequiredWasmFlags,
|
|
5
|
-
requiredWasmSwiftFlags,
|
|
6
|
-
wasmBuildConfigurationLogLines,
|
|
7
|
-
} from "./resolveSwiftArtifacts.ts";
|
|
8
|
-
|
|
9
|
-
test("detects the required wasm Swift flag sequence", () => {
|
|
10
|
-
expect(
|
|
11
|
-
hasRequiredWasmFlags([
|
|
12
|
-
"build",
|
|
13
|
-
"--swift-sdk",
|
|
14
|
-
"swift-6.3.1-RELEASE_wasm",
|
|
15
|
-
"-c",
|
|
16
|
-
"release",
|
|
17
|
-
...requiredWasmSwiftFlags,
|
|
18
|
-
"-Xlinker",
|
|
19
|
-
"--initial-memory=1",
|
|
20
|
-
])
|
|
21
|
-
).toBe(true);
|
|
22
|
-
|
|
23
|
-
expect(
|
|
24
|
-
hasRequiredWasmFlags([
|
|
25
|
-
"build",
|
|
26
|
-
"--swift-sdk",
|
|
27
|
-
"swift-6.3.1-RELEASE_wasm",
|
|
28
|
-
"-c",
|
|
29
|
-
"release",
|
|
30
|
-
"-Xswiftc",
|
|
31
|
-
"-Osize",
|
|
32
|
-
"-Xswiftc",
|
|
33
|
-
"-disable-llvm-merge-functions-pass",
|
|
34
|
-
])
|
|
35
|
-
).toBe(false);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test("formats commands for readable CI logs", () => {
|
|
39
|
-
expect(
|
|
40
|
-
formatCommandForLogs([
|
|
41
|
-
"swiftly",
|
|
42
|
-
"run",
|
|
43
|
-
"swift",
|
|
44
|
-
"build",
|
|
45
|
-
"--package-path",
|
|
46
|
-
"/tmp/My Project",
|
|
47
|
-
"-Xlinker",
|
|
48
|
-
"stack-size=1048576",
|
|
49
|
-
])
|
|
50
|
-
).toBe(
|
|
51
|
-
"swiftly run swift build --package-path '/tmp/My Project' -Xlinker stack-size=1048576"
|
|
52
|
-
);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test("emits explicit CI log lines for flag confirmation and commands", () => {
|
|
56
|
-
const lines = wasmBuildConfigurationLogLines({
|
|
57
|
-
configuration: "release",
|
|
58
|
-
packagePath: "/tmp/pkg",
|
|
59
|
-
product: "WebExampleApp",
|
|
60
|
-
swiftlyWorkingDirectory: "/tmp",
|
|
61
|
-
buildCommand: ["swiftly", "run", "swift", "build", "--product", "WebExampleApp"],
|
|
62
|
-
showBinPathCommand: ["swiftly", "run", "swift", "build", "--show-bin-path"],
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
expect(lines).toContain("WASM_REQUIRED_FLAGS_CONFIRMED=true");
|
|
66
|
-
expect(lines).toContain("WASM_BUILD_CONFIGURATION_NAME=release");
|
|
67
|
-
expect(lines).toContain(
|
|
68
|
-
"WASM_REQUIRED_FLAGS=-Xswiftc -Osize -Xswiftc -Xfrontend -Xswiftc -disable-llvm-merge-functions-pass"
|
|
69
|
-
);
|
|
70
|
-
expect(lines).toContain(
|
|
71
|
-
'WASM_BUILD_COMMAND_ARGS_JSON=["swiftly","run","swift","build","--product","WebExampleApp"]'
|
|
72
|
-
);
|
|
73
|
-
expect(lines).toContain(
|
|
74
|
-
'WASM_SHOW_BIN_PATH_COMMAND_ARGS_JSON=["swiftly","run","swift","build","--show-bin-path"]'
|
|
75
|
-
);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test("marks required release flags as skipped for debug wasm builds", () => {
|
|
79
|
-
const lines = wasmBuildConfigurationLogLines({
|
|
80
|
-
configuration: "debug",
|
|
81
|
-
packagePath: "/tmp/pkg",
|
|
82
|
-
product: "WebExampleApp",
|
|
83
|
-
swiftlyWorkingDirectory: "/tmp",
|
|
84
|
-
buildCommand: ["swiftly", "run", "swift", "build", "-c", "debug"],
|
|
85
|
-
showBinPathCommand: ["swiftly", "run", "swift", "build", "-c", "debug", "--show-bin-path"],
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
expect(lines).toContain("WASM_BUILD_CONFIGURATION_NAME=debug");
|
|
89
|
-
expect(lines).toContain("WASM_REQUIRED_FLAGS_CONFIRMED=skipped");
|
|
90
|
-
});
|
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
import { access } from "node:fs/promises";
|
|
2
|
-
import { dirname, join, resolve } from "node:path";
|
|
3
|
-
import { runCommand } from "./runCommand.ts";
|
|
4
|
-
import { swiftCommandPrefix } from "./swiftCommandPrefix.ts";
|
|
5
|
-
|
|
6
|
-
export interface ResolveSwiftArtifactsOptions {
|
|
7
|
-
configuration?: WasmBuildConfiguration;
|
|
8
|
-
extraLinkerFlags?: readonly string[];
|
|
9
|
-
extraSwiftBuildArgs?: readonly string[];
|
|
10
|
-
extraSwiftcFlags?: readonly string[];
|
|
11
|
-
initialMemory?: number | string;
|
|
12
|
-
maxMemory?: number | string;
|
|
13
|
-
packagePath: string;
|
|
14
|
-
product: string;
|
|
15
|
-
stackSize?: number | string;
|
|
16
|
-
swiftCommand?: readonly string[];
|
|
17
|
-
swiftSDK?: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export type WasmBuildConfiguration = "debug" | "release";
|
|
21
|
-
|
|
22
|
-
export interface SwiftArtifactPaths {
|
|
23
|
-
binPath: string;
|
|
24
|
-
wasmPath: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export const requiredWasmSwiftFlags = [
|
|
28
|
-
"-Xswiftc",
|
|
29
|
-
"-Osize",
|
|
30
|
-
"-Xswiftc",
|
|
31
|
-
"-Xfrontend",
|
|
32
|
-
"-Xswiftc",
|
|
33
|
-
"-disable-llvm-merge-functions-pass",
|
|
34
|
-
] as const;
|
|
35
|
-
|
|
36
|
-
export const defaultWasmSwiftSDK = "swift-6.3.1-RELEASE_wasm";
|
|
37
|
-
export const defaultInitialMemory = "536870912";
|
|
38
|
-
export const defaultMaxMemory = "4294967296";
|
|
39
|
-
export const defaultStackSize = "1048576";
|
|
40
|
-
|
|
41
|
-
export async function resolveSwiftArtifacts(
|
|
42
|
-
options: ResolveSwiftArtifactsOptions
|
|
43
|
-
): Promise<SwiftArtifactPaths> {
|
|
44
|
-
const configuration = options.configuration ?? "release";
|
|
45
|
-
const swiftlyWorkingDirectory = await resolveSwiftlyWorkingDirectory(options.packagePath);
|
|
46
|
-
const swiftCommand = [...(options.swiftCommand ?? swiftCommandPrefix())];
|
|
47
|
-
const environment = {
|
|
48
|
-
...process.env
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
// The browser WebAssembly API rejects function types with more than 1000
|
|
52
|
-
// parameters. Swift's wasm release builds can trip that limit when LLVM's
|
|
53
|
-
// merge-functions pass combines large outlined-copy helpers. `-Osize` helps,
|
|
54
|
-
// but some Darwin CI runners still reproduce the failure unless we also
|
|
55
|
-
// disable that merge pass explicitly.
|
|
56
|
-
const swiftBuildArgs = [
|
|
57
|
-
"build",
|
|
58
|
-
"--package-path",
|
|
59
|
-
options.packagePath,
|
|
60
|
-
"--swift-sdk",
|
|
61
|
-
options.swiftSDK ?? defaultWasmSwiftSDK,
|
|
62
|
-
"-c",
|
|
63
|
-
configuration,
|
|
64
|
-
...requiredSwiftFlags(configuration),
|
|
65
|
-
...swiftcFlags(options.extraSwiftcFlags),
|
|
66
|
-
"-Xlinker",
|
|
67
|
-
`--initial-memory=${options.initialMemory ?? defaultInitialMemory}`,
|
|
68
|
-
"-Xlinker",
|
|
69
|
-
`--max-memory=${options.maxMemory ?? defaultMaxMemory}`,
|
|
70
|
-
"-Xlinker",
|
|
71
|
-
"-z",
|
|
72
|
-
"-Xlinker",
|
|
73
|
-
`stack-size=${options.stackSize ?? defaultStackSize}`,
|
|
74
|
-
...linkerFlags(options.extraLinkerFlags),
|
|
75
|
-
...(options.extraSwiftBuildArgs ?? []),
|
|
76
|
-
];
|
|
77
|
-
|
|
78
|
-
if (configuration === "release") {
|
|
79
|
-
confirmRequiredWasmFlags(swiftBuildArgs);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const buildCommand = [
|
|
83
|
-
...swiftCommand,
|
|
84
|
-
...swiftBuildArgs,
|
|
85
|
-
"--product",
|
|
86
|
-
options.product,
|
|
87
|
-
];
|
|
88
|
-
const showBinPathCommand = [
|
|
89
|
-
...swiftCommand,
|
|
90
|
-
...swiftBuildArgs,
|
|
91
|
-
"--show-bin-path",
|
|
92
|
-
];
|
|
93
|
-
|
|
94
|
-
logWasmBuildConfiguration({
|
|
95
|
-
configuration,
|
|
96
|
-
packagePath: options.packagePath,
|
|
97
|
-
product: options.product,
|
|
98
|
-
swiftlyWorkingDirectory,
|
|
99
|
-
buildCommand,
|
|
100
|
-
showBinPathCommand,
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
await runCommand(buildCommand, {
|
|
104
|
-
cwd: swiftlyWorkingDirectory,
|
|
105
|
-
env: environment,
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
const binPath = await runCommand(showBinPathCommand, {
|
|
109
|
-
cwd: swiftlyWorkingDirectory,
|
|
110
|
-
env: environment,
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
const wasmPath = join(binPath.trim(), `${options.product}.wasm`);
|
|
114
|
-
return {
|
|
115
|
-
binPath: binPath.trim(),
|
|
116
|
-
wasmPath,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
interface WasmBuildConfigurationLog {
|
|
121
|
-
configuration?: WasmBuildConfiguration;
|
|
122
|
-
packagePath: string;
|
|
123
|
-
product: string;
|
|
124
|
-
swiftlyWorkingDirectory: string;
|
|
125
|
-
buildCommand: string[];
|
|
126
|
-
showBinPathCommand: string[];
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export function hasRequiredWasmFlags(args: readonly string[]): boolean {
|
|
130
|
-
return containsSubsequence(args, requiredWasmSwiftFlags);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function confirmRequiredWasmFlags(args: readonly string[]): void {
|
|
134
|
-
if (hasRequiredWasmFlags(args)) {
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
throw new Error(
|
|
139
|
-
`missing required wasm Swift flags: ${requiredWasmSwiftFlags.join(" ")}`
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function requiredSwiftFlags(configuration: WasmBuildConfiguration): readonly string[] {
|
|
144
|
-
switch (configuration) {
|
|
145
|
-
case "debug":
|
|
146
|
-
return [];
|
|
147
|
-
case "release":
|
|
148
|
-
return requiredWasmSwiftFlags;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function swiftcFlags(flags: readonly string[] | undefined): string[] {
|
|
153
|
-
return (flags ?? []).flatMap((flag) => ["-Xswiftc", flag]);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function linkerFlags(flags: readonly string[] | undefined): string[] {
|
|
157
|
-
return (flags ?? []).flatMap((flag) => ["-Xlinker", flag]);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function logWasmBuildConfiguration(config: WasmBuildConfigurationLog): void {
|
|
161
|
-
for (const line of wasmBuildConfigurationLogLines(config)) {
|
|
162
|
-
console.error(line);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export function wasmBuildConfigurationLogLines(
|
|
167
|
-
config: WasmBuildConfigurationLog
|
|
168
|
-
): string[] {
|
|
169
|
-
const configuration = config.configuration ?? "release";
|
|
170
|
-
return [
|
|
171
|
-
`WASM_BUILD_CONFIGURATION_NAME=${configuration}`,
|
|
172
|
-
`WASM_REQUIRED_FLAGS_CONFIRMED=${configuration === "release" ? "true" : "skipped"}`,
|
|
173
|
-
`WASM_REQUIRED_FLAGS=${requiredWasmSwiftFlags.join(" ")}`,
|
|
174
|
-
`WASM_REQUIRED_FLAGS_JSON=${JSON.stringify([...requiredWasmSwiftFlags])}`,
|
|
175
|
-
`WASM_BUILD_COMMAND=${formatCommandForLogs(config.buildCommand)}`,
|
|
176
|
-
`WASM_BUILD_COMMAND_ARGS_JSON=${JSON.stringify(config.buildCommand)}`,
|
|
177
|
-
`WASM_SHOW_BIN_PATH_COMMAND=${formatCommandForLogs(config.showBinPathCommand)}`,
|
|
178
|
-
`WASM_SHOW_BIN_PATH_COMMAND_ARGS_JSON=${JSON.stringify(config.showBinPathCommand)}`,
|
|
179
|
-
`WASM_BUILD_CONFIGURATION ${JSON.stringify({
|
|
180
|
-
packagePath: config.packagePath,
|
|
181
|
-
product: config.product,
|
|
182
|
-
configuration,
|
|
183
|
-
swiftlyWorkingDirectory: config.swiftlyWorkingDirectory,
|
|
184
|
-
requiredFlags: [...requiredWasmSwiftFlags],
|
|
185
|
-
buildCommand: formatCommandForLogs(config.buildCommand),
|
|
186
|
-
showBinPathCommand: formatCommandForLogs(config.showBinPathCommand),
|
|
187
|
-
})}`,
|
|
188
|
-
];
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export function formatCommandForLogs(args: readonly string[]): string {
|
|
192
|
-
return args.map(shellQuote).join(" ");
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function containsSubsequence(
|
|
196
|
-
args: readonly string[],
|
|
197
|
-
expected: readonly string[]
|
|
198
|
-
): boolean {
|
|
199
|
-
if (expected.length == 0) {
|
|
200
|
-
return true;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
for (let index = 0; index <= args.length - expected.length; index += 1) {
|
|
204
|
-
let matches = true;
|
|
205
|
-
for (let expectedIndex = 0; expectedIndex < expected.length; expectedIndex += 1) {
|
|
206
|
-
if (args[index + expectedIndex] !== expected[expectedIndex]) {
|
|
207
|
-
matches = false;
|
|
208
|
-
break;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
if (matches) {
|
|
212
|
-
return true;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return false;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function shellQuote(arg: string): string {
|
|
220
|
-
if (/^[A-Za-z0-9_./:=+-]+$/.test(arg)) {
|
|
221
|
-
return arg;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return `'${arg.replaceAll("'", `'\\''`)}'`;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
async function resolveSwiftlyWorkingDirectory(
|
|
228
|
-
startPath: string
|
|
229
|
-
): Promise<string> {
|
|
230
|
-
let currentPath = resolve(startPath);
|
|
231
|
-
|
|
232
|
-
while (true) {
|
|
233
|
-
if (await fileExists(join(currentPath, ".swift-version"))) {
|
|
234
|
-
return currentPath;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const parentPath = dirname(currentPath);
|
|
238
|
-
if (parentPath === currentPath) {
|
|
239
|
-
return resolve(startPath);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
currentPath = parentPath;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
async function fileExists(path: string): Promise<boolean> {
|
|
247
|
-
try {
|
|
248
|
-
await access(path);
|
|
249
|
-
return true;
|
|
250
|
-
} catch {
|
|
251
|
-
return false;
|
|
252
|
-
}
|
|
253
|
-
}
|
package/src/build/runCommand.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { accessSync } from "node:fs";
|
|
2
|
-
import { delimiter, join } from "node:path";
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
4
|
-
|
|
5
|
-
export interface RunCommandOptions {
|
|
6
|
-
cwd?: string;
|
|
7
|
-
env?: Record<string, string | undefined>;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export async function runCommand(
|
|
11
|
-
cmd: string[],
|
|
12
|
-
options: RunCommandOptions = {}
|
|
13
|
-
): Promise<string> {
|
|
14
|
-
const executable = cmd[0];
|
|
15
|
-
if (!executable) {
|
|
16
|
-
throw new Error("cannot run an empty command");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const proc = spawn(executable, cmd.slice(1), {
|
|
20
|
-
cwd: options.cwd,
|
|
21
|
-
env: normalizeEnvironment(options.env),
|
|
22
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
const stdoutChunks: Buffer[] = [];
|
|
26
|
-
const stderrChunks: Buffer[] = [];
|
|
27
|
-
proc.stdout.on("data", (chunk: Buffer) => {
|
|
28
|
-
stdoutChunks.push(chunk);
|
|
29
|
-
});
|
|
30
|
-
proc.stderr.on("data", (chunk: Buffer) => {
|
|
31
|
-
stderrChunks.push(chunk);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const exitCode = await new Promise<number | null>((resolve, reject) => {
|
|
35
|
-
proc.on("error", reject);
|
|
36
|
-
proc.on("close", resolve);
|
|
37
|
-
});
|
|
38
|
-
const stdout = Buffer.concat(stdoutChunks).toString();
|
|
39
|
-
const stderr = Buffer.concat(stderrChunks).toString();
|
|
40
|
-
|
|
41
|
-
if (exitCode !== 0) {
|
|
42
|
-
throw new Error([stdout, stderr].filter(Boolean).join("\n").trim() || `command failed: ${cmd.join(" ")}`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return stdout;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function findExecutable(
|
|
49
|
-
name: string,
|
|
50
|
-
pathValue: string | undefined = process.env.PATH
|
|
51
|
-
): string | undefined {
|
|
52
|
-
for (const directory of pathValue?.split(delimiter) ?? []) {
|
|
53
|
-
if (!directory) {
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
const candidate = join(directory, name);
|
|
57
|
-
try {
|
|
58
|
-
accessSync(candidate);
|
|
59
|
-
return candidate;
|
|
60
|
-
} catch {
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return undefined;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function normalizeEnvironment(
|
|
68
|
-
env: Record<string, string | undefined> | undefined
|
|
69
|
-
): NodeJS.ProcessEnv | undefined {
|
|
70
|
-
if (!env) {
|
|
71
|
-
return undefined;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const normalized: NodeJS.ProcessEnv = {};
|
|
75
|
-
for (const [key, value] of Object.entries(env)) {
|
|
76
|
-
if (value !== undefined) {
|
|
77
|
-
normalized[key] = value;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return normalized;
|
|
81
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { findExecutable, runCommand } from "./runCommand.ts";
|
|
2
|
-
|
|
3
|
-
export async function stripPackagedWasm(
|
|
4
|
-
wasmPath: string
|
|
5
|
-
): Promise<void> {
|
|
6
|
-
const objcopyPath = findExecutable("llvm-objcopy");
|
|
7
|
-
if (!objcopyPath) {
|
|
8
|
-
throw new Error(
|
|
9
|
-
"missing llvm-objcopy in PATH; install the swiftly-managed toolchain before packaging wasm"
|
|
10
|
-
);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
await runCommand([
|
|
14
|
-
objcopyPath,
|
|
15
|
-
"--strip-debug",
|
|
16
|
-
"--remove-section=name",
|
|
17
|
-
wasmPath,
|
|
18
|
-
]);
|
|
19
|
-
}
|