@swiftbrowser/cli 0.6.7 → 0.7.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/README.md +73 -5
- package/dist/appSources.d.ts +116 -0
- package/dist/appSources.js +624 -0
- package/dist/appSources.js.map +1 -0
- package/dist/browser.d.ts +4 -0
- package/dist/browser.js +8 -0
- package/dist/browser.js.map +1 -0
- package/dist/build.d.ts +17 -2
- package/dist/build.js +38 -11
- package/dist/build.js.map +1 -1
- package/dist/cli.js +28 -4
- package/dist/cli.js.map +1 -1
- package/dist/commands/build.js +10 -7
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/check.js +38 -8
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/dev.d.ts +8 -4
- package/dist/commands/dev.js +25 -10
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.js +52 -4
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/kit.d.ts +1 -0
- package/dist/commands/kit.js +20 -5
- package/dist/commands/kit.js.map +1 -1
- package/dist/commands/shared.d.ts +24 -6
- package/dist/commands/shared.js +92 -16
- package/dist/commands/shared.js.map +1 -1
- package/dist/commands/test.d.ts +4 -2
- package/dist/commands/test.js +25 -17
- package/dist/commands/test.js.map +1 -1
- package/dist/compat.d.ts +2 -30
- package/dist/compat.js +2 -44
- package/dist/compat.js.map +1 -1
- package/dist/compiler.d.ts +50 -0
- package/dist/compiler.js +94 -0
- package/dist/compiler.js.map +1 -0
- package/dist/config.d.ts +5 -2
- package/dist/config.js +7 -3
- package/dist/config.js.map +1 -1
- package/dist/frameworks.d.ts +30 -0
- package/dist/frameworks.js +48 -0
- package/dist/frameworks.js.map +1 -0
- package/dist/icu.d.ts +2 -4
- package/dist/icu.js +7 -36
- package/dist/icu.js.map +1 -1
- package/dist/kit.d.ts +44 -12
- package/dist/kit.js +149 -27
- package/dist/kit.js.map +1 -1
- package/dist/log.js +2 -1
- package/dist/log.js.map +1 -1
- package/dist/manifest.d.ts +73 -0
- package/dist/manifest.js +324 -0
- package/dist/manifest.js.map +1 -0
- package/dist/moduleCache.d.ts +20 -0
- package/dist/moduleCache.js +98 -0
- package/dist/moduleCache.js.map +1 -0
- package/dist/objc.js +1 -1
- package/dist/objc.js.map +1 -1
- package/dist/packages.d.ts +8 -1
- package/dist/packages.js +10 -2
- package/dist/packages.js.map +1 -1
- package/dist/registry.d.ts +46 -0
- package/dist/registry.js +184 -0
- package/dist/registry.js.map +1 -0
- package/dist/resolve.d.ts +80 -0
- package/dist/resolve.js +396 -0
- package/dist/resolve.js.map +1 -0
- package/dist/stage.d.ts +5 -144
- package/dist/stage.js +19 -516
- package/dist/stage.js.map +1 -1
- package/dist/testDiscovery.d.ts +44 -0
- package/dist/testDiscovery.js +111 -0
- package/dist/testDiscovery.js.map +1 -0
- package/dist/vendor.d.ts +41 -5
- package/dist/vendor.js +71 -31
- package/dist/vendor.js.map +1 -1
- package/dist/wasmCompiler.d.ts +160 -0
- package/dist/wasmCompiler.js +628 -0
- package/dist/wasmCompiler.js.map +1 -0
- package/dist/wasmFlags.d.ts +31 -0
- package/dist/wasmFlags.js +56 -0
- package/dist/wasmFlags.js.map +1 -0
- package/dist/wasmPackages.d.ts +163 -0
- package/dist/wasmPackages.js +466 -0
- package/dist/wasmPackages.js.map +1 -0
- package/dist/wasmSdk.d.ts +27 -0
- package/dist/wasmSdk.js +103 -0
- package/dist/wasmSdk.js.map +1 -0
- package/package.json +6 -5
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
// Building the staged app with the Swift compiler and linker built as
|
|
2
|
+
// WebAssembly (@swiftbrowser/wasm-compiler, docs/in-browser-compiler.md
|
|
3
|
+
// Milestone 2) instead of `swift build`: what the browser playground does,
|
|
4
|
+
// and what needs no Swift install. compiler.ts decides when it runs
|
|
5
|
+
// (SWIFTBROWSER_COMPILER).
|
|
6
|
+
//
|
|
7
|
+
// It reproduces SwiftPM's release build of the staging package (stage.ts):
|
|
8
|
+
// one whole-module frontend job for the app's sources against the kit's
|
|
9
|
+
// modules, the ICU data as an object, and a link with the app's exports and
|
|
10
|
+
// the kit's archives of the products the app imports, linked whole. The
|
|
11
|
+
// Swift SDK's files come from wasmSdk.ts: the native toolchain's install or
|
|
12
|
+
// the CLI's own download.
|
|
13
|
+
//
|
|
14
|
+
// Macros expand through plugins built as WebAssembly, which the host runs for
|
|
15
|
+
// the compiler: the shim's (SwiftDataMacros) from the kit, which builds them
|
|
16
|
+
// from the same sources as its modules (kit.ts), and the toolchain's
|
|
17
|
+
// (ObservationMacros, SwiftMacros) from the tools' `plugins` directory.
|
|
18
|
+
// Package dependencies (the copies vendor.ts makes) are compiled module by
|
|
19
|
+
// module first (wasmPackages.ts).
|
|
20
|
+
import { createHash } from 'node:crypto';
|
|
21
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import { diagnosticsOnly } from './build.js';
|
|
24
|
+
import { KIT_TRIPLE, kitArchive, kitWasmPlugins } from './kit.js';
|
|
25
|
+
import { CliError, dim, info } from './log.js';
|
|
26
|
+
import { planPackages } from './packages.js';
|
|
27
|
+
import { appLinkerFlags, commonDirectory, linkedProducts, remapDiagnostics, testTargetPath } from './stage.js';
|
|
28
|
+
import { testRunnerSource, xctestCases } from './testDiscovery.js';
|
|
29
|
+
import { dumpVendored, generatedModuleMap, packageModules, resourceBundleAccessor } from './wasmPackages.js';
|
|
30
|
+
import { evaluateManifest } from './manifest.js';
|
|
31
|
+
import { VERIFY_NONE, appFrontendFlags, kitTargets } from './wasmFlags.js';
|
|
32
|
+
import { WASI_EMULATION_FLAGS, importsCoreFoundation } from './appSources.js';
|
|
33
|
+
import { hashFiles, hashStats, restoreOutputs, storeOutputs, treeFiles } from './moduleCache.js';
|
|
34
|
+
export { appFrontendFlags, kitTargets } from './wasmFlags.js';
|
|
35
|
+
import { cacheDir } from './toolchain.js';
|
|
36
|
+
export async function load() {
|
|
37
|
+
const specifier = '@swiftbrowser/wasm-compiler';
|
|
38
|
+
try {
|
|
39
|
+
const compiler = (await import(specifier));
|
|
40
|
+
const node = (await import(`${specifier}/node`));
|
|
41
|
+
return { compiler, node };
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
throw new CliError(`the WebAssembly compiler needs @swiftbrowser/wasm-compiler, built (npm run build --workspace packages/wasm-compiler): ${error.message}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** SWIFTBROWSER_WASM_TOOLS: a directory of the tools (a build's toolchain/install-tools.sh output) in place of @swiftbrowser/wasm-tools. */
|
|
48
|
+
export function toolsDir(env = process.env) {
|
|
49
|
+
return env['SWIFTBROWSER_WASM_TOOLS'] || undefined;
|
|
50
|
+
}
|
|
51
|
+
/** What keeps the WebAssembly compiler from running here, or null when it can. */
|
|
52
|
+
export async function wasmToolsProblem(env = process.env) {
|
|
53
|
+
try {
|
|
54
|
+
const { node } = await load();
|
|
55
|
+
const tools = node.findTools(toolsDir(env));
|
|
56
|
+
const missing = Object.values(tools).filter((file) => !existsSync(file));
|
|
57
|
+
return missing.length > 0 ? `the WebAssembly tools are missing (${missing.join(', ')})` : null;
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
return error instanceof Error ? error.message.split('\n')[0] : String(error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** The plugins the compiler loads: the tools' (the toolchain's macros), then the kit's in place of any the tools also have. */
|
|
64
|
+
async function pluginFiles(kit) {
|
|
65
|
+
const { node } = await load();
|
|
66
|
+
return { ...node.findPlugins(toolsDir()), ...kitWasmPlugins(kit) };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The plugins as the compiler sees them: each directory mounted at
|
|
70
|
+
* /plugin-<module> (the tools' and the kit's are in different places; a mount
|
|
71
|
+
* two levels down, /plugins/<module>, is not found, as /plugins itself is not
|
|
72
|
+
* there), the guest path → host file map the host runs them from, and the
|
|
73
|
+
* frontend flags that name each with the module whose macros it has.
|
|
74
|
+
*/
|
|
75
|
+
function pluginSetup(plugins) {
|
|
76
|
+
const mounts = {};
|
|
77
|
+
const files = {};
|
|
78
|
+
const flags = [];
|
|
79
|
+
for (const [name, file] of Object.entries(plugins)) {
|
|
80
|
+
const guest = `/plugin-${name}/${path.basename(file)}`;
|
|
81
|
+
mounts[`/plugin-${name}`] = path.dirname(file);
|
|
82
|
+
files[guest] = file;
|
|
83
|
+
flags.push('-load-plugin-executable', `${guest}#${name}`);
|
|
84
|
+
}
|
|
85
|
+
return { mounts, files, flags };
|
|
86
|
+
}
|
|
87
|
+
/** The SDK's target directory, the root the tools see as /sdk. */
|
|
88
|
+
function sdkMount(sdk) {
|
|
89
|
+
return sdk.root;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Type-checks Swift files against the kit with the WebAssembly frontend: what
|
|
93
|
+
* `check` runs, and how a released kit is checked against the compiler.
|
|
94
|
+
* `files` are host paths under `dir`; `flags` the frontend flags besides the
|
|
95
|
+
* kit's module path. Returns the diagnostics with host paths.
|
|
96
|
+
*/
|
|
97
|
+
export async function wasmTypecheck(options) {
|
|
98
|
+
const { compiler, node } = await load();
|
|
99
|
+
const plugins = pluginSetup(await pluginFiles(options.kit));
|
|
100
|
+
const work = path.join(cacheDir(), 'wasm-compiler', 'typecheck');
|
|
101
|
+
mkdirSync(work, { recursive: true });
|
|
102
|
+
const host = node.wasiHost({
|
|
103
|
+
tools: node.findTools(toolsDir()),
|
|
104
|
+
mounts: { '/sdk': sdkMount(options.sdk), '/kit': options.kit.dir, '/src': options.dir, '/work': work, ...plugins.mounts },
|
|
105
|
+
sdkRoot: '/sdk',
|
|
106
|
+
moduleCacheDir: path.join(cacheDir(), 'wasm-compiler', 'module-cache'),
|
|
107
|
+
plugins: plugins.files,
|
|
108
|
+
});
|
|
109
|
+
const sources = options.files.map((file) => `/src/${path.relative(options.dir, file).split(path.sep).join('/')}`);
|
|
110
|
+
const result = await compiler.compile(host, {
|
|
111
|
+
moduleName: options.moduleName,
|
|
112
|
+
sources,
|
|
113
|
+
objectDir: '/work',
|
|
114
|
+
wholeModule: true,
|
|
115
|
+
frontendFlags: ['-I', `/kit/${KIT_TRIPLE}/Modules`, ...(options.flags ?? []), ...plugins.flags],
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
ok: result.ok,
|
|
119
|
+
output: result.output.split('/src/').join(`${options.dir}${path.sep}`),
|
|
120
|
+
seconds: result.steps.reduce((sum, step) => sum + step.seconds, 0),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function swiftSources(dir, relative = '') {
|
|
124
|
+
return readdirSync(path.join(dir, relative), { withFileTypes: true }).flatMap((entry) => {
|
|
125
|
+
const file = path.join(relative, entry.name);
|
|
126
|
+
if (entry.isDirectory())
|
|
127
|
+
return swiftSources(dir, file);
|
|
128
|
+
return entry.isFile() && entry.name.endsWith('.swift') ? [file] : [];
|
|
129
|
+
}).sort();
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The manifests of the packages the app builds: the copies made for the shim
|
|
133
|
+
* (read from the copy, whose manifest adds the shim's products) and the plain
|
|
134
|
+
* packages (from their checkouts), read with the WebAssembly compiler
|
|
135
|
+
* (manifest.ts). Tools without PackageDescription leave it to SwiftPM, for the
|
|
136
|
+
* copies, as before it.
|
|
137
|
+
*/
|
|
138
|
+
async function packageDumps(sdk, vendored, packages, toolchain) {
|
|
139
|
+
const { node } = await load();
|
|
140
|
+
if (!node.findManifestApi(toolsDir())) {
|
|
141
|
+
if (!toolchain || packages.some((pkg) => !vendored.has(pkg.identity))) {
|
|
142
|
+
throw new CliError('the WebAssembly tools have no PackageDescription to read the app\'s packages with; update @swiftbrowser/wasm-tools');
|
|
143
|
+
}
|
|
144
|
+
return vendored.size > 0 ? dumpVendored(toolchain, vendored) : new Map();
|
|
145
|
+
}
|
|
146
|
+
const dirs = new Map();
|
|
147
|
+
for (const [identity, copy] of vendored)
|
|
148
|
+
if (!copy.standIn)
|
|
149
|
+
dirs.set(identity, copy.dir);
|
|
150
|
+
for (const pkg of packages)
|
|
151
|
+
if (!vendored.has(pkg.identity))
|
|
152
|
+
dirs.set(pkg.identity, pkg.path);
|
|
153
|
+
const dumps = new Map();
|
|
154
|
+
for (const [identity, dir] of dirs) {
|
|
155
|
+
const { dump, dependencies } = await evaluateManifest(dir, sdk);
|
|
156
|
+
const original = vendored.has(identity) ? packages.find((pkg) => pkg.identity === identity)?.path : undefined;
|
|
157
|
+
dumps.set(identity, { dir, dump, dependencies, ...(original ? { originalDir: original } : {}) });
|
|
158
|
+
}
|
|
159
|
+
return dumps;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Sets up the WebAssembly tools for a build of the staged app (the kit, the
|
|
163
|
+
* app and its packages mounted, the macro plugins) and compiles the packages'
|
|
164
|
+
* modules the app uses, each after those it imports.
|
|
165
|
+
*/
|
|
166
|
+
async function compilePackages(options, kit, work) {
|
|
167
|
+
const { staged, app } = options;
|
|
168
|
+
const vendored = options.vendored ?? new Map();
|
|
169
|
+
const { compiler, node } = await load();
|
|
170
|
+
mkdirSync(path.join(work, 'modules'), { recursive: true });
|
|
171
|
+
// Each plugin is named to the compiler by its path, with the module whose
|
|
172
|
+
// macros it has; the host runs it, and the compiler checks the file is there.
|
|
173
|
+
const files = await pluginFiles(kit);
|
|
174
|
+
if (staged.features.swiftData && !files['SwiftDataMacros']) {
|
|
175
|
+
throw new CliError('the WebAssembly compiler needs the SwiftDataMacros plugin built as WebAssembly for SwiftData');
|
|
176
|
+
}
|
|
177
|
+
const plugins = pluginSetup(files);
|
|
178
|
+
const packages = await packageDumps(options.sdk, vendored, options.packages ?? [], options.toolchain);
|
|
179
|
+
// The products the app names; a package's own dependencies follow from its targets.
|
|
180
|
+
const used = new Map();
|
|
181
|
+
for (const plan of planPackages(app.packages))
|
|
182
|
+
if (plan.kind === 'dependency')
|
|
183
|
+
used.set(plan.identity, [...plan.package.products]);
|
|
184
|
+
const modules = packageModules(packages, new Set(Object.keys(kit.manifest.products)), used);
|
|
185
|
+
const mounts = { '/sdk': sdkMount(options.sdk), '/kit': kit.dir, '/app': staged.dir, '/work': work };
|
|
186
|
+
for (const [identity, { dir, originalDir }] of packages) {
|
|
187
|
+
mounts[`/packages/${identity}`] = dir;
|
|
188
|
+
mounts[`/originals/${identity}`] = originalDir ?? dir;
|
|
189
|
+
}
|
|
190
|
+
Object.assign(mounts, plugins.mounts);
|
|
191
|
+
const tools = node.findTools(toolsDir());
|
|
192
|
+
const host = node.wasiHost({
|
|
193
|
+
tools,
|
|
194
|
+
mounts,
|
|
195
|
+
sdkRoot: '/sdk',
|
|
196
|
+
moduleCacheDir: path.join(cacheDir(), 'wasm-compiler', 'module-cache'),
|
|
197
|
+
plugins: plugins.files,
|
|
198
|
+
});
|
|
199
|
+
const pluginFlags = plugins.flags;
|
|
200
|
+
const kitModules = `/kit/${KIT_TRIPLE}/Modules`;
|
|
201
|
+
const hostPaths = (text) => {
|
|
202
|
+
let out = text.split('/app/').join(`${staged.dir}${path.sep}`);
|
|
203
|
+
for (const [identity, { dir }] of packages)
|
|
204
|
+
out = out.split(`/packages/${identity}/`).join(`${dir}${path.sep}`);
|
|
205
|
+
return out;
|
|
206
|
+
};
|
|
207
|
+
const steps = [];
|
|
208
|
+
const packageObjects = [];
|
|
209
|
+
// A module's files as the tools see them: the package as the app builds it
|
|
210
|
+
// (its copy for the shim, if any), or as a plugin does (`/originals`).
|
|
211
|
+
const guestIn = (root) => (module, file) => `/${root}/${module.identity}/${file.split(path.sep).join('/')}`;
|
|
212
|
+
const guest = guestIn('packages');
|
|
213
|
+
const onStep = (step) => options.onOutput?.(`${step.tool} ${step.args.join(' ')}\n`);
|
|
214
|
+
const fail = (output) => {
|
|
215
|
+
throw new CliError(hostPaths(diagnosticsOnly(output) || output));
|
|
216
|
+
};
|
|
217
|
+
// The host file of a path the tools see under /work, /packages or /originals.
|
|
218
|
+
const hostOf = (guest) => {
|
|
219
|
+
const [, root, first, ...rest] = guest.split('/');
|
|
220
|
+
if (root === 'work')
|
|
221
|
+
return path.join(work, first, ...rest);
|
|
222
|
+
const pkg = packages.get(first);
|
|
223
|
+
if (pkg && root === 'packages')
|
|
224
|
+
return path.join(pkg.dir, ...rest);
|
|
225
|
+
if (pkg && root === 'originals')
|
|
226
|
+
return path.join(pkg.originalDir ?? pkg.dir, ...rest);
|
|
227
|
+
throw new Error(`no host file for ${guest}`);
|
|
228
|
+
};
|
|
229
|
+
// The packages' modules the app links are kept between builds
|
|
230
|
+
// (moduleCache.ts), keyed by what goes into each: the tools, the kit, the
|
|
231
|
+
// SDK, its flags and files, and the keys of the modules and plugins it
|
|
232
|
+
// imports. (Plugins have their own cache, below.)
|
|
233
|
+
const cachedModules = path.join(cacheDir(), 'wasm-compiler', 'packages');
|
|
234
|
+
const stamp = createHash('sha256').update(`package modules 1\n${options.sdk.root}\n`);
|
|
235
|
+
hashStats(stamp, [...Object.values(tools).filter((file) => !!file), ...Object.values(files), ...treeFiles(kit.dir)]);
|
|
236
|
+
const buildStamp = stamp.digest('hex');
|
|
237
|
+
const moduleKeys = new Map();
|
|
238
|
+
const moduleKey = (module, flags, inputs) => {
|
|
239
|
+
const hash = createHash('sha256').update(`${buildStamp}\n${module.identity}/${module.name}\n${JSON.stringify(flags)}\n`);
|
|
240
|
+
for (const name of [...module.dependencies, ...module.cModules, ...module.macros])
|
|
241
|
+
hash.update(`${name} ${moduleKeys.get(name) ?? '-'}\n`);
|
|
242
|
+
hashFiles(hash, inputs);
|
|
243
|
+
return hash.digest('hex').slice(0, 24);
|
|
244
|
+
};
|
|
245
|
+
const reused = [];
|
|
246
|
+
// The module's outputs (guest paths) from the cache, else built and stored.
|
|
247
|
+
const cached = async (module, key, build) => {
|
|
248
|
+
moduleKeys.set(module.name, key);
|
|
249
|
+
const entry = path.join(cachedModules, `${module.name}-${key}`);
|
|
250
|
+
const restored = restoreOutputs(entry, hostOf);
|
|
251
|
+
if (restored) {
|
|
252
|
+
reused.push(module.name);
|
|
253
|
+
return restored;
|
|
254
|
+
}
|
|
255
|
+
const outputs = await build();
|
|
256
|
+
storeOutputs(entry, outputs, hostOf);
|
|
257
|
+
return outputs;
|
|
258
|
+
};
|
|
259
|
+
const byName = new Map(modules.map((module) => [module.name, module]));
|
|
260
|
+
const closure = (module) => {
|
|
261
|
+
const found = new Map();
|
|
262
|
+
const walk = (m) => {
|
|
263
|
+
for (const name of m.dependencies) {
|
|
264
|
+
if (found.has(name))
|
|
265
|
+
continue;
|
|
266
|
+
found.set(name, byName.get(name));
|
|
267
|
+
walk(byName.get(name));
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
walk(module);
|
|
271
|
+
return [...found.values()];
|
|
272
|
+
};
|
|
273
|
+
const optimization = options.debug ? ['-Onone'] : ['-O', '-enable-default-cmo'];
|
|
274
|
+
// Plugins compile unoptimized: swift-syntax takes minutes optimized as
|
|
275
|
+
// WebAssembly, and a macro expands quickly enough without.
|
|
276
|
+
const PLUGIN_OPTIMIZATION = ['-Onone'];
|
|
277
|
+
const loadPlugins = (macros) => macros.flatMap((m) => ['-load-plugin-executable', `/work/plugins/${m}.wasm#${m}`]);
|
|
278
|
+
const clangFlags = (c) => [...(c.map ? ['-Xcc', `-fmodule-map-file=${c.map}`] : []), '-Xcc', `-I${c.include}`];
|
|
279
|
+
// A C module compiled as SwiftPM compiles a C target
|
|
280
|
+
// (ClangModuleBuildDescription.basicArguments), with its module map.
|
|
281
|
+
const compileClang = async (module, root, out, reached) => {
|
|
282
|
+
if (!tools.clang)
|
|
283
|
+
throw new CliError(`the WebAssembly tools have no clang to build ${module.name}, a C target of ${module.identity}; update @swiftbrowser/wasm-tools`);
|
|
284
|
+
const at = guestIn(root);
|
|
285
|
+
const clang = module.clang;
|
|
286
|
+
const include = at(module, clang.includeDir);
|
|
287
|
+
const sources = module.sources.map((file) => at(module, file));
|
|
288
|
+
const flags = [
|
|
289
|
+
...(options.debug ? ['-O0', '-DDEBUG=1'] : ['-O2']), '-DSWIFT_PACKAGE=1', '-fblocks',
|
|
290
|
+
'-I', include,
|
|
291
|
+
...clang.headerSearchPaths.flatMap((dir) => ['-I', at(module, dir)]),
|
|
292
|
+
...reached.flatMap((c) => ['-I', c.include]),
|
|
293
|
+
...module.flags,
|
|
294
|
+
];
|
|
295
|
+
const compile = async () => {
|
|
296
|
+
const built = await compiler.compileC(host, { sources, objectDir: `/work/${out}/${module.name}`, root: `/${root}/${module.identity}`, flags, onStep });
|
|
297
|
+
steps.push(...built.steps.map((step) => ({ ...step, tool: `${module.name} (${step.tool})` })));
|
|
298
|
+
if (!built.ok)
|
|
299
|
+
fail(built.output);
|
|
300
|
+
return built.objects;
|
|
301
|
+
};
|
|
302
|
+
// Its headers are inputs too: the target's directory, its public headers and its search paths.
|
|
303
|
+
const sourceDir = commonDirectory(module.sources);
|
|
304
|
+
const headers = [sourceDir, clang.includeDir, ...clang.headerSearchPaths].flatMap((dir) => treeFiles(hostOf(at(module, dir))));
|
|
305
|
+
const objects = root === 'packages'
|
|
306
|
+
? await cached(module, moduleKey(module, flags, [...new Set([...sources.map(hostOf), ...headers])]), compile)
|
|
307
|
+
: await compile();
|
|
308
|
+
let map;
|
|
309
|
+
if (clang.moduleMap && 'custom' in clang.moduleMap)
|
|
310
|
+
map = at(module, clang.moduleMap.custom);
|
|
311
|
+
else if (clang.moduleMap) {
|
|
312
|
+
writeFileSync(path.join(work, out, module.name, 'module.modulemap'), generatedModuleMap(module.name, 'umbrellaHeader' in clang.moduleMap
|
|
313
|
+
? { umbrellaHeader: at(module, clang.moduleMap.umbrellaHeader) }
|
|
314
|
+
: { umbrellaDirectory: at(module, clang.moduleMap.umbrellaDirectory) }));
|
|
315
|
+
map = `/work/${out}/${module.name}/module.modulemap`;
|
|
316
|
+
}
|
|
317
|
+
return { objects, include, ...(map ? { map } : {}) };
|
|
318
|
+
};
|
|
319
|
+
// Macro targets become WebAssembly plugins, built as the compiler runs
|
|
320
|
+
// them: from the packages' own sources (not their copies for the shim),
|
|
321
|
+
// without the kit, into a module directory of their own. They are cached by
|
|
322
|
+
// what goes into them, so that swift-syntax compiles once.
|
|
323
|
+
mkdirSync(path.join(work, 'plugins'), { recursive: true });
|
|
324
|
+
mkdirSync(path.join(work, 'plugin-modules'), { recursive: true });
|
|
325
|
+
const cachedPlugins = path.join(cacheDir(), 'wasm-compiler', 'plugins');
|
|
326
|
+
const pluginKey = (macro) => {
|
|
327
|
+
const hash = createHash('sha256').update(`plugin 2\n${statSync(tools['swift-frontend']).size}\n`);
|
|
328
|
+
for (const m of [macro, ...closure(macro)]) {
|
|
329
|
+
hash.update(`${m.identity}/${m.name} ${m.flags.join(' ')}\n`);
|
|
330
|
+
const { dir, originalDir } = packages.get(m.identity);
|
|
331
|
+
for (const file of m.sources)
|
|
332
|
+
if (existsSync(path.join(originalDir ?? dir, file)))
|
|
333
|
+
hash.update(`${file}\n`).update(readFileSync(path.join(originalDir ?? dir, file)));
|
|
334
|
+
}
|
|
335
|
+
return hash.digest('hex').slice(0, 24);
|
|
336
|
+
};
|
|
337
|
+
// A module's own sources, as its package has them: not the files its copy for the shim adds.
|
|
338
|
+
const originalSources = (m) => {
|
|
339
|
+
const { dir, originalDir } = packages.get(m.identity);
|
|
340
|
+
return m.sources.filter((file) => existsSync(path.join(originalDir ?? dir, file)));
|
|
341
|
+
};
|
|
342
|
+
const forPlugins = new Map();
|
|
343
|
+
const buildForPlugin = async (module) => {
|
|
344
|
+
if (forPlugins.has(module.name))
|
|
345
|
+
return;
|
|
346
|
+
for (const dependency of module.dependencies)
|
|
347
|
+
await buildForPlugin(byName.get(dependency));
|
|
348
|
+
mkdirSync(path.join(work, 'plugin-packages', module.name), { recursive: true });
|
|
349
|
+
const reached = module.cModules.map((name) => forPlugins.get(name));
|
|
350
|
+
if (module.clang) {
|
|
351
|
+
forPlugins.set(module.name, await compileClang(module, 'originals', 'plugin-packages', reached));
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const built = await compiler.compile(host, {
|
|
355
|
+
moduleName: module.name,
|
|
356
|
+
sources: originalSources(module).map((file) => guestIn('originals')(module, file)),
|
|
357
|
+
objectDir: `/work/plugin-packages/${module.name}`,
|
|
358
|
+
emitModule: `/work/plugin-modules/${module.name}.swiftmodule`,
|
|
359
|
+
frontendFlags: [
|
|
360
|
+
'-I', '/work/plugin-modules', ...module.flags, ...reached.flatMap(clangFlags), ...PLUGIN_OPTIMIZATION, ...VERIFY_NONE,
|
|
361
|
+
'-D', 'SWIFT_PACKAGE', '-package-name', module.identity, ...loadPlugins(module.macros),
|
|
362
|
+
],
|
|
363
|
+
onStep,
|
|
364
|
+
});
|
|
365
|
+
steps.push(...built.steps.map((step) => ({ ...step, tool: `${module.name} for plugins (${step.tool})` })));
|
|
366
|
+
if (!built.ok)
|
|
367
|
+
fail(built.output);
|
|
368
|
+
forPlugins.set(module.name, { objects: built.objects });
|
|
369
|
+
};
|
|
370
|
+
const buildPlugin = async (macro) => {
|
|
371
|
+
const plugin = path.join(work, 'plugins', `${macro.name}.wasm`);
|
|
372
|
+
const key = pluginKey(macro);
|
|
373
|
+
moduleKeys.set(macro.name, key);
|
|
374
|
+
const cachedPlugin = path.join(cachedPlugins, `${macro.name}-${key}.wasm`);
|
|
375
|
+
if (!existsSync(cachedPlugin)) {
|
|
376
|
+
const deps = closure(macro);
|
|
377
|
+
for (const dependency of deps)
|
|
378
|
+
await buildForPlugin(dependency);
|
|
379
|
+
const sources = originalSources(macro).map((file) => guestIn('originals')(macro, file));
|
|
380
|
+
mkdirSync(path.join(work, 'plugin-packages', macro.name), { recursive: true });
|
|
381
|
+
// An executable whose `swift_wasm_macro_v1_pump` the host calls
|
|
382
|
+
// (swift-syntax's CompilerPlugin, 601 and later).
|
|
383
|
+
const built = await compiler.compile(host, {
|
|
384
|
+
moduleName: macro.name,
|
|
385
|
+
sources,
|
|
386
|
+
objectDir: `/work/plugin-packages/${macro.name}`,
|
|
387
|
+
output: `/work/plugins/${macro.name}.wasm`,
|
|
388
|
+
wholeModule: true,
|
|
389
|
+
frontendFlags: [
|
|
390
|
+
'-I', '/work/plugin-modules', ...macro.flags,
|
|
391
|
+
...macro.cModules.flatMap((name) => clangFlags(forPlugins.get(name))),
|
|
392
|
+
...PLUGIN_OPTIMIZATION, ...VERIFY_NONE, '-D', 'SWIFT_PACKAGE', '-package-name', macro.identity, ...loadPlugins(macro.macros),
|
|
393
|
+
...(sources.some((file) => file.endsWith('/main.swift')) ? [] : ['-parse-as-library']),
|
|
394
|
+
],
|
|
395
|
+
objects: deps.flatMap((m) => forPlugins.get(m.name).objects),
|
|
396
|
+
linkerFlags: ['--gc-sections'],
|
|
397
|
+
onStep,
|
|
398
|
+
});
|
|
399
|
+
steps.push(...built.steps.map((step) => ({ ...step, tool: `${macro.name} plugin (${step.tool})` })));
|
|
400
|
+
if (!built.ok)
|
|
401
|
+
fail(built.output);
|
|
402
|
+
mkdirSync(cachedPlugins, { recursive: true });
|
|
403
|
+
copyFileSync(plugin, cachedPlugin);
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
copyFileSync(cachedPlugin, plugin);
|
|
407
|
+
}
|
|
408
|
+
host.addPlugin(`/work/plugins/${macro.name}.wasm`, plugin);
|
|
409
|
+
};
|
|
410
|
+
// CoreFoundation's headers include wasi-libc's signal.h and sys/mman.h,
|
|
411
|
+
// which stop without the emulations' defines (the link has their
|
|
412
|
+
// libraries). Only a module that imports it gets them: other modules keep
|
|
413
|
+
// the module cache's usual configuration.
|
|
414
|
+
const usesCoreFoundation = (module) => {
|
|
415
|
+
const { dir } = packages.get(module.identity);
|
|
416
|
+
return module.sources.some((file) => file.endsWith('.swift') && importsCoreFoundation(readFileSync(path.join(dir, file), 'utf8')));
|
|
417
|
+
};
|
|
418
|
+
const clangModules = new Map();
|
|
419
|
+
for (const module of modules) {
|
|
420
|
+
if (module.macro) {
|
|
421
|
+
await buildPlugin(module);
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
if (module.pluginOnly)
|
|
425
|
+
continue;
|
|
426
|
+
mkdirSync(path.join(work, 'packages', module.name), { recursive: true });
|
|
427
|
+
const reached = module.cModules.map((name) => clangModules.get(name));
|
|
428
|
+
if (module.clang) {
|
|
429
|
+
const { objects, ...c } = await compileClang(module, 'packages', 'packages', reached);
|
|
430
|
+
packageObjects.push(...objects);
|
|
431
|
+
clangModules.set(module.name, c);
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
const sources = module.sources.map((file) => guest(module, file));
|
|
435
|
+
if (module.resourceBundle) {
|
|
436
|
+
writeFileSync(path.join(work, 'packages', module.name, 'resource_bundle_accessor.swift'), resourceBundleAccessor(module.resourceBundle));
|
|
437
|
+
sources.push(`/work/packages/${module.name}/resource_bundle_accessor.swift`);
|
|
438
|
+
}
|
|
439
|
+
const emitModule = `/work/modules/${module.name}.swiftmodule`;
|
|
440
|
+
const frontendFlags = [
|
|
441
|
+
'-I', kitModules, '-I', '/work/modules', ...module.flags,
|
|
442
|
+
// The C modules it imports, through their module maps.
|
|
443
|
+
...reached.flatMap(clangFlags),
|
|
444
|
+
...optimization, ...VERIFY_NONE,
|
|
445
|
+
'-D', 'SWIFT_PACKAGE', '-package-name', module.identity, ...pluginFlags, ...loadPlugins(module.macros),
|
|
446
|
+
...(usesCoreFoundation(module) ? WASI_EMULATION_FLAGS : []),
|
|
447
|
+
];
|
|
448
|
+
const outputs = await cached(module, moduleKey(module, frontendFlags, sources.map(hostOf)), async () => {
|
|
449
|
+
const built = await compiler.compile(host, { moduleName: module.name, sources, objectDir: `/work/packages/${module.name}`, emitModule, frontendFlags, onStep });
|
|
450
|
+
steps.push(...built.steps.map((step) => ({ ...step, tool: `${module.name} (${step.tool})` })));
|
|
451
|
+
if (!built.ok)
|
|
452
|
+
fail(built.output);
|
|
453
|
+
return [...built.objects, emitModule];
|
|
454
|
+
});
|
|
455
|
+
packageObjects.push(...outputs.filter((file) => file !== emitModule));
|
|
456
|
+
}
|
|
457
|
+
// What the app's own compile needs of the C modules its packages reach, and their macros.
|
|
458
|
+
const linked = modules.filter((module) => !module.macro && !module.pluginOnly);
|
|
459
|
+
const appMacros = loadPlugins([...new Set(linked.flatMap((module) => module.macros))]);
|
|
460
|
+
const appClangFlags = [...new Set(linked.flatMap((module) => (module.clang ? [] : module.cModules)))].flatMap((name) => clangFlags(clangModules.get(name)));
|
|
461
|
+
return { compiler, host, work, kitModules, pluginFlags: [...pluginFlags, ...appClangFlags, ...appMacros], modules, packageObjects, steps, reused, hostPaths };
|
|
462
|
+
}
|
|
463
|
+
/** What each tool took: the numbers Milestone 2 records (docs/in-browser-compiler.md); and the package modules that took none. */
|
|
464
|
+
function describeSteps(steps, reused) {
|
|
465
|
+
if (reused.length > 0)
|
|
466
|
+
info(dim(` ${reused.length === 1 ? '1 package module' : `${reused.length} package modules`} unchanged, from the cache`));
|
|
467
|
+
info(dim(` WebAssembly compiler: ${steps.map((step) => `${step.tool} ${step.seconds.toFixed(1)} s${step.memoryBytes ? `, ${Math.round(step.memoryBytes / 1e6)} MB` : ''}`).join('; ')}`));
|
|
468
|
+
}
|
|
469
|
+
/** The kit's archives for the products the app and its packages link, as the tools see them. */
|
|
470
|
+
function kitArchives(kit, staged, modules) {
|
|
471
|
+
const products = [...new Set([...linkedProducts(staged.features), ...modules.flatMap((module) => module.kitProducts)])];
|
|
472
|
+
return kitTargets(kit, products).map((target) => kitArchive('/kit', target));
|
|
473
|
+
}
|
|
474
|
+
/** Compiles and links the staged app with the WebAssembly tools; returns the module it wrote. */
|
|
475
|
+
export async function buildWithWasmCompiler(options) {
|
|
476
|
+
const { staged, app, kit, icu } = options;
|
|
477
|
+
if (!kit)
|
|
478
|
+
throw new CliError('the WebAssembly compiler builds against the kit; unset SWIFTBROWSER_KIT=0');
|
|
479
|
+
if (!icu)
|
|
480
|
+
throw new CliError('the WebAssembly compiler needs the ICU data the build prepares');
|
|
481
|
+
const work = path.join(staged.dir, '.build', 'wasm-compiler');
|
|
482
|
+
const { compiler, host, kitModules, pluginFlags, modules, packageObjects, steps, reused, hostPaths } = await compilePackages(options, kit, work);
|
|
483
|
+
writeFileSync(path.join(work, 'icudt.o'), compiler.dataObject(icu.symbol, readFileSync(icu.file)));
|
|
484
|
+
const sources = swiftSources(path.join(staged.dir, 'Sources')).map((file) => `/app/Sources/${file.split(path.sep).join('/')}`);
|
|
485
|
+
const archives = kitArchives(kit, staged, modules);
|
|
486
|
+
const result = await compiler.compile(host, {
|
|
487
|
+
moduleName: app.name,
|
|
488
|
+
sources,
|
|
489
|
+
objectDir: '/work',
|
|
490
|
+
output: `/work/${app.name}.wasm`,
|
|
491
|
+
wholeModule: true,
|
|
492
|
+
frontendFlags: [...appFrontendFlags(app, sources, kitModules, { debug: options.debug ?? false }), ...(modules.length > 0 ? ['-I', '/work/modules'] : []), ...pluginFlags],
|
|
493
|
+
objects: [...packageObjects, '/work/icudt.o'],
|
|
494
|
+
linkerFlags: [
|
|
495
|
+
'--gc-sections',
|
|
496
|
+
...appLinkerFlags(),
|
|
497
|
+
...archives.flatMap((archive) => ['--whole-archive', archive, '--no-whole-archive']),
|
|
498
|
+
],
|
|
499
|
+
onStep: (step) => options.onOutput?.(`${step.tool} ${step.args.join(' ')}\n`),
|
|
500
|
+
});
|
|
501
|
+
steps.push(...result.steps);
|
|
502
|
+
describeSteps(steps, reused);
|
|
503
|
+
const output = hostPaths(result.output);
|
|
504
|
+
if (!result.ok)
|
|
505
|
+
throw new CliError(remapDiagnostics(diagnosticsOnly(output) || output, staged));
|
|
506
|
+
const built = path.join(work, `${app.name}.wasm`);
|
|
507
|
+
if (!existsSync(built))
|
|
508
|
+
throw new CliError(`expected ${built} after the link`);
|
|
509
|
+
return built;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* `swiftbrowser test` with the WebAssembly tools: what SwiftPM's debug
|
|
513
|
+
* `--build-tests` does for the staged test package. The app compiles as a
|
|
514
|
+
* testable module whose entry point is renamed (`<App>_main`), so that a
|
|
515
|
+
* test runner's is the program's; each test target compiles against it, and
|
|
516
|
+
* links with a runner (testDiscovery.ts) into `<Target>-test-runner.wasm`.
|
|
517
|
+
* Returns the runners, as `buildTests` does.
|
|
518
|
+
*/
|
|
519
|
+
export async function buildTestsWithWasmCompiler(options) {
|
|
520
|
+
const { staged, app, kit } = options;
|
|
521
|
+
if (!kit)
|
|
522
|
+
throw new CliError('the WebAssembly compiler builds against the kit; unset SWIFTBROWSER_KIT=0');
|
|
523
|
+
const work = path.join(staged.dir, '.build', 'wasm-compiler');
|
|
524
|
+
const context = await compilePackages({ ...options, debug: true }, kit, work);
|
|
525
|
+
const { compiler, host, kitModules, pluginFlags, modules, packageObjects, steps, reused, hostPaths } = context;
|
|
526
|
+
const fail = (output) => {
|
|
527
|
+
const text = hostPaths(output);
|
|
528
|
+
throw new CliError(remapDiagnostics(diagnosticsOnly(text) || text, staged));
|
|
529
|
+
};
|
|
530
|
+
const onStep = (step) => options.onOutput?.(`${step.tool} ${step.args.join(' ')}\n`);
|
|
531
|
+
// Debug, as SwiftPM builds tests: unoptimized, DEBUG defined, testable.
|
|
532
|
+
const flags = (sources) => [
|
|
533
|
+
...appFrontendFlags(app, sources, kitModules, { debug: true }),
|
|
534
|
+
'-D', 'DEBUG', '-enable-testing', '-I', '/work/modules', ...pluginFlags,
|
|
535
|
+
];
|
|
536
|
+
const appSources = swiftSources(path.join(staged.dir, 'Sources')).map((file) => `/app/Sources/${file.split(path.sep).join('/')}`);
|
|
537
|
+
mkdirSync(path.join(work, 'app'), { recursive: true });
|
|
538
|
+
const appBuilt = await compiler.compile(host, {
|
|
539
|
+
moduleName: app.name,
|
|
540
|
+
sources: appSources,
|
|
541
|
+
objectDir: '/work/app',
|
|
542
|
+
emitModule: `/work/modules/${app.name}.swiftmodule`,
|
|
543
|
+
// A main.swift keeps its top-level code (appFrontendFlags decides).
|
|
544
|
+
parseAsLibrary: false,
|
|
545
|
+
frontendFlags: [...flags(appSources), '-entry-point-function-name', `${app.name}_main`],
|
|
546
|
+
onStep,
|
|
547
|
+
});
|
|
548
|
+
steps.push(...appBuilt.steps.map((step) => ({ ...step, tool: `${app.name} (${step.tool})` })));
|
|
549
|
+
if (!appBuilt.ok)
|
|
550
|
+
fail(appBuilt.output);
|
|
551
|
+
const archives = kitArchives(kit, staged, modules);
|
|
552
|
+
const runners = [];
|
|
553
|
+
for (const target of options.tests) {
|
|
554
|
+
const dir = path.join(work, 'tests', target.name);
|
|
555
|
+
rmSync(path.join(dir, 'symbols'), { recursive: true, force: true });
|
|
556
|
+
mkdirSync(path.join(dir, 'symbols'), { recursive: true });
|
|
557
|
+
const testDir = testTargetPath(target.name);
|
|
558
|
+
const sources = swiftSources(path.join(staged.dir, testDir)).map((file) => `/app/${[...testDir.split(path.sep), ...file.split(path.sep)].join('/')}`);
|
|
559
|
+
const built = await compiler.compile(host, {
|
|
560
|
+
moduleName: target.name,
|
|
561
|
+
sources,
|
|
562
|
+
objectDir: `/work/tests/${target.name}`,
|
|
563
|
+
emitModule: `/work/modules/${target.name}.swiftmodule`,
|
|
564
|
+
// The XCTest classes, for the runner to list.
|
|
565
|
+
frontendFlags: [...flags(sources), '-emit-symbol-graph', '-emit-symbol-graph-dir', `/work/tests/${target.name}/symbols`, '-symbol-graph-minimum-access-level', 'internal'],
|
|
566
|
+
onStep,
|
|
567
|
+
});
|
|
568
|
+
steps.push(...built.steps.map((step) => ({ ...step, tool: `${target.name} (${step.tool})` })));
|
|
569
|
+
if (!built.ok)
|
|
570
|
+
fail(built.output);
|
|
571
|
+
const graph = JSON.parse(readFileSync(path.join(dir, 'symbols', `${target.name}.symbols.json`), 'utf8'));
|
|
572
|
+
const runnerDir = path.join(work, 'tests', `${target.name}-runner`);
|
|
573
|
+
mkdirSync(runnerDir, { recursive: true });
|
|
574
|
+
writeFileSync(path.join(runnerDir, 'runner.swift'), testRunnerSource(target.name, xctestCases(graph)));
|
|
575
|
+
const runner = `${target.name}-test-runner.wasm`;
|
|
576
|
+
const linked = await compiler.compile(host, {
|
|
577
|
+
moduleName: `${target.name}_test_runner`,
|
|
578
|
+
sources: [`/work/tests/${target.name}-runner/runner.swift`],
|
|
579
|
+
objectDir: `/work/tests/${target.name}-runner`,
|
|
580
|
+
output: `/work/${runner}`,
|
|
581
|
+
wholeModule: true,
|
|
582
|
+
frontendFlags: flags([]),
|
|
583
|
+
// The SDK's whole ICU data, as SwiftPM's test builds link it.
|
|
584
|
+
objects: [...packageObjects, ...appBuilt.objects, ...built.objects],
|
|
585
|
+
linkerFlags: ['--gc-sections', ...appLinkerFlags(), ...archives.flatMap((archive) => ['--whole-archive', archive, '--no-whole-archive'])],
|
|
586
|
+
onStep,
|
|
587
|
+
});
|
|
588
|
+
steps.push(...linked.steps.map((step) => ({ ...step, tool: `${target.name} runner (${step.tool})` })));
|
|
589
|
+
if (!linked.ok)
|
|
590
|
+
fail(linked.output);
|
|
591
|
+
runners.push(path.join(work, runner));
|
|
592
|
+
}
|
|
593
|
+
describeSteps(steps, reused);
|
|
594
|
+
return runners;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Whether the WebAssembly compiler reads a released kit's modules: type-checks
|
|
598
|
+
* a file importing every module of the kit against it, as `probeKit` does
|
|
599
|
+
* with a native toolchain (kit.ts).
|
|
600
|
+
*/
|
|
601
|
+
export async function probeKitWithWasmCompiler(sdk, kit) {
|
|
602
|
+
const dir = path.join(cacheDir(), 'wasm-compiler', 'probe');
|
|
603
|
+
mkdirSync(dir, { recursive: true });
|
|
604
|
+
const file = path.join(dir, 'Probe.swift');
|
|
605
|
+
writeFileSync(file, `${Object.keys(kit.manifest.targets).map((module) => `import ${module}`).join('\n')}\n`);
|
|
606
|
+
const result = await wasmTypecheck({ sdk, kit, dir, files: [file], moduleName: 'Probe', flags: ['-parse-as-library'] });
|
|
607
|
+
return { ok: result.ok, output: result.output };
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* `check` with the WebAssembly compiler: type-checks the staged app against
|
|
611
|
+
* the kit with the flags its build compiles with. Throws a CliError with the
|
|
612
|
+
* diagnostics, named for the user's files, when it does not type-check.
|
|
613
|
+
*/
|
|
614
|
+
export async function checkWithWasmCompiler(options) {
|
|
615
|
+
const { staged, app } = options;
|
|
616
|
+
const files = swiftSources(path.join(staged.dir, 'Sources')).map((file) => path.join(staged.dir, 'Sources', file));
|
|
617
|
+
const result = await wasmTypecheck({
|
|
618
|
+
sdk: options.sdk,
|
|
619
|
+
kit: options.kit,
|
|
620
|
+
dir: staged.dir,
|
|
621
|
+
files,
|
|
622
|
+
moduleName: app.name,
|
|
623
|
+
flags: appFrontendFlags(app, files, `/kit/${KIT_TRIPLE}/Modules`),
|
|
624
|
+
});
|
|
625
|
+
if (!result.ok)
|
|
626
|
+
throw new CliError(remapDiagnostics(diagnosticsOnly(result.output) || result.output, staged));
|
|
627
|
+
}
|
|
628
|
+
//# sourceMappingURL=wasmCompiler.js.map
|