@agoric/xsnap 0.14.3-dev-fa6d3d3.0.fa6d3d3 → 0.14.3-dev-e3099cb.0.e3099cb
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/package.json +5 -5
- package/scripts/test-package.sh +1 -1
- package/src/build.js +207 -121
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/xsnap",
|
|
3
|
-
"version": "0.14.3-dev-
|
|
3
|
+
"version": "0.14.3-dev-e3099cb.0.e3099cb",
|
|
4
4
|
"description": "Snapshotting VM worker based on Moddable's XS Javascript engine",
|
|
5
5
|
"author": "Agoric",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"scripts": {
|
|
14
14
|
"repl": "node src/xsrepl.js",
|
|
15
15
|
"build:bin": "/bin/sh -c 'if test -d ./test; then node src/build.js; else yarn build:from-env; fi'",
|
|
16
|
-
"build:env": "node src/build.js --show-env > build.env",
|
|
16
|
+
"build:env": "/bin/sh -c 'node src/build.js --show-env > build.env.new && mv build.env.new build.env'",
|
|
17
17
|
"build:from-env": "{ cat build.env; echo node src/build.js; } | xargs env",
|
|
18
18
|
"build": "yarn build:bin && yarn build:env",
|
|
19
19
|
"check-version": "/bin/sh -c 'if test \"${npm_package_version}\" != \"$(./scripts/get_xsnap_version.sh)\"; then echo \"xsnap version mismatch; expected '${npm_package_version}'\"; exit 1; fi'",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"test:xs": "exit 0"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@agoric/internal": "0.3.3-dev-
|
|
32
|
-
"@agoric/xsnap-lockdown": "0.14.1-dev-
|
|
31
|
+
"@agoric/internal": "0.3.3-dev-e3099cb.0.e3099cb",
|
|
32
|
+
"@agoric/xsnap-lockdown": "0.14.1-dev-e3099cb.0.e3099cb",
|
|
33
33
|
"@endo/bundle-source": "^4.1.2",
|
|
34
34
|
"@endo/errors": "^1.2.13",
|
|
35
35
|
"@endo/eventual-send": "^1.3.4",
|
|
@@ -80,5 +80,5 @@
|
|
|
80
80
|
"engines": {
|
|
81
81
|
"node": "^20.9 || ^22.11"
|
|
82
82
|
},
|
|
83
|
-
"gitHead": "
|
|
83
|
+
"gitHead": "e3099cb9b5af514813cf606753b3743fd361bcf8"
|
|
84
84
|
}
|
package/scripts/test-package.sh
CHANGED
package/src/build.js
CHANGED
|
@@ -31,7 +31,6 @@ const ModdableSDK = {
|
|
|
31
31
|
platforms: {
|
|
32
32
|
Linux: { path: 'lin' },
|
|
33
33
|
Darwin: { path: 'mac' },
|
|
34
|
-
Windows_NT: { path: 'win', make: 'nmake' },
|
|
35
34
|
},
|
|
36
35
|
buildGoals: ['release', 'debug'],
|
|
37
36
|
};
|
|
@@ -101,130 +100,119 @@ function makeCLI(command, { spawn }) {
|
|
|
101
100
|
});
|
|
102
101
|
}
|
|
103
102
|
|
|
103
|
+
/** @param {string} repoUrl */
|
|
104
|
+
const canonicalRepoUrl = repoUrl =>
|
|
105
|
+
repoUrl.replace(/\/+$/, '').replace(/\.git$/, '');
|
|
106
|
+
|
|
104
107
|
/**
|
|
105
|
-
* @param {string} path
|
|
106
108
|
* @param {string} repoUrl
|
|
107
|
-
* @param {
|
|
109
|
+
* @param {string} commitHash
|
|
108
110
|
*/
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const [hash, statusPath, ...describe] = line.slice(1).split(' ');
|
|
132
|
-
return {
|
|
133
|
-
prefix,
|
|
134
|
-
hash,
|
|
135
|
-
path: statusPath,
|
|
136
|
-
describe: describe.join(' '),
|
|
137
|
-
};
|
|
138
|
-
},
|
|
139
|
-
/**
|
|
140
|
-
* Read a specific configuration value for this submodule (e.g., "path" or
|
|
141
|
-
* "url") from the top-level .gitmodules.
|
|
142
|
-
*
|
|
143
|
-
* @param {string} leaf
|
|
144
|
-
*/
|
|
145
|
-
config: async leaf => {
|
|
146
|
-
// git rev-parse --show-toplevel
|
|
147
|
-
const repoRoot = await git.pipe(['rev-parse', '--show-toplevel']);
|
|
148
|
-
if (!path.startsWith(`${repoRoot}/`)) {
|
|
149
|
-
throw Error(
|
|
150
|
-
`Expected submodule path ${path} to be a subdirectory of repository ${repoRoot}`,
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
const relativePath = path.slice(repoRoot.length + 1);
|
|
154
|
-
// git config -f ../../.gitmodules --get submodule.${relativePath}.${leaf}
|
|
155
|
-
const value = await git.pipe([
|
|
156
|
-
'config',
|
|
157
|
-
'-f',
|
|
158
|
-
`${repoRoot}/.gitmodules`,
|
|
159
|
-
'--get',
|
|
160
|
-
`submodule.${relativePath}.${leaf}`,
|
|
161
|
-
]);
|
|
162
|
-
return value;
|
|
163
|
-
},
|
|
164
|
-
});
|
|
111
|
+
const defaultArchiveUrl = (repoUrl, commitHash) =>
|
|
112
|
+
`${canonicalRepoUrl(repoUrl)}/archive/${commitHash}.tar.gz`;
|
|
113
|
+
|
|
114
|
+
const SOURCE_STAMP_FILE = '.agoric-source-stamp.json';
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @param {string} text
|
|
118
|
+
* @returns {Record<string, string>}
|
|
119
|
+
*/
|
|
120
|
+
const parseEnvText = text => {
|
|
121
|
+
/** @type {Record<string, string>} */
|
|
122
|
+
const envMap = {};
|
|
123
|
+
for (const line of text.split('\n')) {
|
|
124
|
+
const trimmed = line.trim();
|
|
125
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
126
|
+
const index = trimmed.indexOf('=');
|
|
127
|
+
if (index <= 0) continue;
|
|
128
|
+
const key = trimmed.slice(0, index);
|
|
129
|
+
const value = trimmed.slice(index + 1);
|
|
130
|
+
envMap[key] = value;
|
|
131
|
+
}
|
|
132
|
+
return envMap;
|
|
165
133
|
};
|
|
166
134
|
|
|
167
135
|
/**
|
|
168
136
|
* @typedef {{
|
|
169
137
|
* url: string,
|
|
170
138
|
* path: string,
|
|
171
|
-
* commitHash
|
|
139
|
+
* commitHash: string,
|
|
140
|
+
* archiveUrl: string,
|
|
172
141
|
* envPrefix: string,
|
|
173
|
-
* }}
|
|
142
|
+
* }} SourceDescriptor
|
|
174
143
|
*/
|
|
175
144
|
|
|
176
145
|
/**
|
|
177
|
-
* @param {
|
|
146
|
+
* @param {SourceDescriptor[]} sources
|
|
178
147
|
* @param {{
|
|
179
|
-
* git: ReturnType<typeof makeCLI>,
|
|
180
148
|
* stdout: typeof process.stdout,
|
|
181
149
|
* }} io
|
|
182
150
|
*/
|
|
183
|
-
const showEnv = async (
|
|
151
|
+
const showEnv = async (sources, { stdout }) => {
|
|
184
152
|
await null;
|
|
185
|
-
for (const
|
|
186
|
-
const { path, envPrefix } = desc;
|
|
187
|
-
let { url, commitHash } = desc;
|
|
188
|
-
if (!commitHash) {
|
|
189
|
-
// We need to glean the commitHash and url from Git.
|
|
190
|
-
const submodule = makeSubmodule(path, '?', { git });
|
|
191
|
-
const [{ hash }, gitUrl] = await Promise.all([
|
|
192
|
-
submodule.status(),
|
|
193
|
-
submodule.config('url'),
|
|
194
|
-
]);
|
|
195
|
-
commitHash = hash;
|
|
196
|
-
url = gitUrl;
|
|
197
|
-
}
|
|
153
|
+
for (const { envPrefix, url, commitHash, archiveUrl } of sources) {
|
|
198
154
|
stdout.write(`${envPrefix}URL=${url}\n`);
|
|
199
155
|
stdout.write(`${envPrefix}COMMIT_HASH=${commitHash}\n`);
|
|
156
|
+
const defaultUrl = defaultArchiveUrl(url, commitHash);
|
|
157
|
+
if (archiveUrl && archiveUrl !== defaultUrl) {
|
|
158
|
+
stdout.write(`${envPrefix}ARCHIVE_URL=${archiveUrl}\n`);
|
|
159
|
+
}
|
|
200
160
|
}
|
|
201
161
|
};
|
|
202
162
|
|
|
203
163
|
/**
|
|
204
|
-
* @param {
|
|
164
|
+
* @param {SourceDescriptor[]} sources
|
|
205
165
|
* @param {{
|
|
206
|
-
* fs: Pick<typeof import('fs'), 'existsSync'
|
|
207
|
-
*
|
|
166
|
+
* fs: Pick<typeof import('fs'), 'existsSync'> &
|
|
167
|
+
* Pick<typeof promises, 'mkdir' | 'rm' | 'readFile' | 'writeFile' | 'rename'>,
|
|
168
|
+
* curl: ReturnType<typeof makeCLI>,
|
|
169
|
+
* tar: ReturnType<typeof makeCLI>,
|
|
208
170
|
* }} io
|
|
209
171
|
*/
|
|
210
|
-
const
|
|
172
|
+
const updateSources = async (sources, { fs, curl, tar }) => {
|
|
211
173
|
await null;
|
|
212
|
-
for (const {
|
|
213
|
-
const
|
|
174
|
+
for (const { archiveUrl, path, commitHash, url } of sources) {
|
|
175
|
+
const stamp = JSON.stringify({ url, commitHash, archiveUrl }, null, 2);
|
|
176
|
+
const tmpPath = `${path}.tmp.${process.pid}.${Date.now()}`;
|
|
177
|
+
const oldPath = `${path}.old.${process.pid}.${Date.now()}`;
|
|
178
|
+
const archivePath = `${tmpPath}.tar.gz`;
|
|
179
|
+
const hadExistingPath = fs.existsSync(path);
|
|
214
180
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
181
|
+
await fs.rm(tmpPath, { recursive: true, force: true });
|
|
182
|
+
await fs.rm(oldPath, { recursive: true, force: true });
|
|
183
|
+
await fs.mkdir(tmpPath, { recursive: true });
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
await curl.run(['-fsSL', archiveUrl, '-o', archivePath]);
|
|
187
|
+
await tar.run([
|
|
188
|
+
'-xzf',
|
|
189
|
+
archivePath,
|
|
190
|
+
'--strip-components=1',
|
|
191
|
+
'-C',
|
|
192
|
+
tmpPath,
|
|
193
|
+
]);
|
|
194
|
+
await fs.writeFile(`${tmpPath}/${SOURCE_STAMP_FILE}`, `${stamp}\n`);
|
|
195
|
+
|
|
196
|
+
if (hadExistingPath) {
|
|
197
|
+
await fs.rename(path, oldPath);
|
|
223
198
|
}
|
|
224
|
-
|
|
225
|
-
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
await fs.rename(tmpPath, path);
|
|
202
|
+
} catch (err) {
|
|
203
|
+
if (hadExistingPath && fs.existsSync(oldPath)) {
|
|
204
|
+
await fs.rename(oldPath, path);
|
|
205
|
+
}
|
|
206
|
+
throw err;
|
|
226
207
|
}
|
|
227
|
-
|
|
208
|
+
} catch (err) {
|
|
209
|
+
throw Error(
|
|
210
|
+
`Failed to fetch archive for ${path} @ ${commitHash} from ${archiveUrl}: ${err}`,
|
|
211
|
+
);
|
|
212
|
+
} finally {
|
|
213
|
+
await fs.rm(archivePath, { force: true });
|
|
214
|
+
await fs.rm(tmpPath, { recursive: true, force: true });
|
|
215
|
+
await fs.rm(oldPath, { recursive: true, force: true });
|
|
228
216
|
}
|
|
229
217
|
}
|
|
230
218
|
};
|
|
@@ -282,8 +270,8 @@ const buildXsnap = async (platform, force, { fs, make }) => {
|
|
|
282
270
|
* env: Record<string, string | undefined>,
|
|
283
271
|
* stdout: typeof process.stdout,
|
|
284
272
|
* spawn: typeof spawn,
|
|
285
|
-
* fs: Pick<typeof import('fs'), 'existsSync'
|
|
286
|
-
* Pick<typeof promises, 'readFile' | 'writeFile'>,
|
|
273
|
+
* fs: Pick<typeof import('fs'), 'existsSync'> &
|
|
274
|
+
* Pick<typeof promises, 'readFile' | 'writeFile' | 'mkdir' | 'rm' | 'rename'>,
|
|
287
275
|
* os: Pick<typeof import('os'), 'type'>,
|
|
288
276
|
* }} io
|
|
289
277
|
*/
|
|
@@ -298,60 +286,156 @@ async function main(args, { env, stdout, spawn, fs, os }) {
|
|
|
298
286
|
throw Error(`xsnap does not support OS ${osType}`);
|
|
299
287
|
}
|
|
300
288
|
|
|
301
|
-
const
|
|
289
|
+
const curl = makeCLI('curl', { spawn });
|
|
290
|
+
const tar = makeCLI('tar', { spawn });
|
|
302
291
|
const make = makeCLI(platform.make || 'make', { spawn });
|
|
303
292
|
|
|
293
|
+
/** @type {Record<string, string>} */
|
|
294
|
+
let pinnedEnvFromFile = {};
|
|
295
|
+
let hasPinnedEnvFile = false;
|
|
296
|
+
try {
|
|
297
|
+
const text = await fs.readFile(asset('../build.env'), 'utf-8');
|
|
298
|
+
pinnedEnvFromFile = parseEnvText(text);
|
|
299
|
+
hasPinnedEnvFile = true;
|
|
300
|
+
} catch (_err) {
|
|
301
|
+
// Allow explicit environment overrides to run without a checked-in build.env.
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const moddableUrl =
|
|
305
|
+
env.MODDABLE_URL ||
|
|
306
|
+
pinnedEnvFromFile.MODDABLE_URL ||
|
|
307
|
+
'https://github.com/agoric-labs/moddable.git';
|
|
308
|
+
const moddableCommitHash =
|
|
309
|
+
env.MODDABLE_COMMIT_HASH || pinnedEnvFromFile.MODDABLE_COMMIT_HASH;
|
|
310
|
+
if (!moddableCommitHash) {
|
|
311
|
+
throw Error(
|
|
312
|
+
'Missing MODDABLE_COMMIT_HASH; set it in env or packages/xsnap/build.env',
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const xsnapNativeUrl =
|
|
317
|
+
env.XSNAP_NATIVE_URL ||
|
|
318
|
+
pinnedEnvFromFile.XSNAP_NATIVE_URL ||
|
|
319
|
+
'https://github.com/agoric-labs/xsnap-pub';
|
|
320
|
+
const xsnapNativeCommitHash =
|
|
321
|
+
env.XSNAP_NATIVE_COMMIT_HASH || pinnedEnvFromFile.XSNAP_NATIVE_COMMIT_HASH;
|
|
322
|
+
if (!xsnapNativeCommitHash) {
|
|
323
|
+
throw Error(
|
|
324
|
+
'Missing XSNAP_NATIVE_COMMIT_HASH; set it in env or packages/xsnap/build.env',
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
304
328
|
// When changing/adding entries here, make sure to search the whole project
|
|
305
|
-
// for `@@AGORIC_DOCKER_SUBMODULES@@`
|
|
306
|
-
|
|
329
|
+
// for `@@AGORIC_DOCKER_SUBMODULES@@` in container build wiring.
|
|
330
|
+
/** @type {SourceDescriptor[]} */
|
|
331
|
+
const sources = [
|
|
307
332
|
{
|
|
308
|
-
url:
|
|
333
|
+
url: moddableUrl,
|
|
309
334
|
path: ModdableSDK.MODDABLE,
|
|
310
|
-
commitHash:
|
|
335
|
+
commitHash: moddableCommitHash,
|
|
336
|
+
archiveUrl:
|
|
337
|
+
env.MODDABLE_ARCHIVE_URL ||
|
|
338
|
+
defaultArchiveUrl(moddableUrl, moddableCommitHash),
|
|
311
339
|
envPrefix: 'MODDABLE_',
|
|
312
340
|
},
|
|
313
341
|
{
|
|
314
|
-
url:
|
|
315
|
-
env.XSNAP_NATIVE_URL || 'https://github.com/agoric-labs/xsnap-pub.git',
|
|
342
|
+
url: xsnapNativeUrl,
|
|
316
343
|
path: asset('../xsnap-native'),
|
|
317
|
-
commitHash:
|
|
344
|
+
commitHash: xsnapNativeCommitHash,
|
|
345
|
+
archiveUrl:
|
|
346
|
+
env.XSNAP_NATIVE_ARCHIVE_URL ||
|
|
347
|
+
defaultArchiveUrl(xsnapNativeUrl, xsnapNativeCommitHash),
|
|
318
348
|
envPrefix: 'XSNAP_NATIVE_',
|
|
319
349
|
},
|
|
320
350
|
];
|
|
321
351
|
|
|
322
352
|
// We build both release and debug executables, so checking for only the
|
|
323
353
|
// former is fine.
|
|
324
|
-
// XXX This will need to account for the .exe extension if we recover support
|
|
325
|
-
// for Windows.
|
|
326
354
|
const bin = asset(
|
|
327
355
|
`../xsnap-native/xsnap/build/bin/${platform.path}/release/xsnap-worker`,
|
|
328
356
|
);
|
|
357
|
+
/** @type {Map<string, SourceDescriptor>} */
|
|
358
|
+
const sourceByPrefix = new Map(
|
|
359
|
+
sources.map(source => [source.envPrefix, source]),
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* @param {SourceDescriptor} source
|
|
364
|
+
* @returns {Promise<boolean>} whether the source needs to be refreshed from
|
|
365
|
+
* its archive URL based on whether the existing source stamp matches the
|
|
366
|
+
* expected URL and commit hash
|
|
367
|
+
* @throws if the source stamp exists but cannot be read or parsed
|
|
368
|
+
*/
|
|
369
|
+
const needsSourceRefresh = async source => {
|
|
370
|
+
await null;
|
|
371
|
+
try {
|
|
372
|
+
const stampText = await fs.readFile(
|
|
373
|
+
`${source.path}/${SOURCE_STAMP_FILE}`,
|
|
374
|
+
'utf-8',
|
|
375
|
+
);
|
|
376
|
+
const stamp = JSON.parse(stampText);
|
|
377
|
+
return (
|
|
378
|
+
stamp.url !== source.url ||
|
|
379
|
+
stamp.commitHash !== source.commitHash ||
|
|
380
|
+
stamp.archiveUrl !== source.archiveUrl
|
|
381
|
+
);
|
|
382
|
+
} catch {
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
|
|
329
387
|
const hasBin = fs.existsSync(bin);
|
|
330
388
|
const hasSource = fs.existsSync(asset('../moddable/xs/includes/xs.h'));
|
|
331
|
-
const
|
|
389
|
+
const hasRepoGit = fs.existsSync(asset('../../../.git'));
|
|
390
|
+
const hasExplicitOverride = prefix => {
|
|
391
|
+
const source = sourceByPrefix.get(prefix);
|
|
392
|
+
if (!source) return false;
|
|
393
|
+
const urlKey = `${prefix}URL`;
|
|
394
|
+
const hashKey = `${prefix}COMMIT_HASH`;
|
|
395
|
+
const archiveKey = `${prefix}ARCHIVE_URL`;
|
|
396
|
+
const fileUrl = pinnedEnvFromFile[urlKey];
|
|
397
|
+
const fileHash = pinnedEnvFromFile[hashKey];
|
|
398
|
+
const fileArchive =
|
|
399
|
+
pinnedEnvFromFile[archiveKey] ||
|
|
400
|
+
defaultArchiveUrl(source.url, source.commitHash);
|
|
401
|
+
const envUrl = env[urlKey];
|
|
402
|
+
const envHash = env[hashKey];
|
|
403
|
+
const envArchive = env[archiveKey];
|
|
404
|
+
return (
|
|
405
|
+
(typeof envUrl === 'string' &&
|
|
406
|
+
envUrl !== '' &&
|
|
407
|
+
(!hasPinnedEnvFile || envUrl !== fileUrl)) ||
|
|
408
|
+
(typeof envHash === 'string' &&
|
|
409
|
+
envHash !== '' &&
|
|
410
|
+
(!hasPinnedEnvFile || envHash !== fileHash)) ||
|
|
411
|
+
(typeof envArchive === 'string' &&
|
|
412
|
+
envArchive !== '' &&
|
|
413
|
+
(!hasPinnedEnvFile || envArchive !== fileArchive))
|
|
414
|
+
);
|
|
415
|
+
};
|
|
332
416
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
417
|
+
const hasSourceOverride =
|
|
418
|
+
hasExplicitOverride('MODDABLE_') || hasExplicitOverride('XSNAP_NATIVE_');
|
|
419
|
+
const needsPinnedRefresh =
|
|
420
|
+
hasRepoGit &&
|
|
421
|
+
(await Promise.all(sources.map(needsSourceRefresh))).some(needed => needed);
|
|
422
|
+
const shouldFetchSources =
|
|
423
|
+
hasSourceOverride || needsPinnedRefresh || (!hasSource && !hasBin);
|
|
337
424
|
|
|
338
|
-
// --show-env reports
|
|
425
|
+
// --show-env reports effective URL/hash pins without making changes.
|
|
339
426
|
if (args.includes('--show-env')) {
|
|
340
|
-
|
|
341
|
-
throw Error('XSnap requires a working copy and git to --show-env');
|
|
342
|
-
}
|
|
343
|
-
await showEnv(submodules, { git, stdout });
|
|
427
|
+
await showEnv(sources, { stdout });
|
|
344
428
|
return;
|
|
345
429
|
}
|
|
346
430
|
|
|
347
|
-
// Fetch/update source files via
|
|
348
|
-
if (
|
|
349
|
-
await
|
|
431
|
+
// Fetch/update source files via pinned source archives as appropriate.
|
|
432
|
+
if (shouldFetchSources) {
|
|
433
|
+
await updateSources(sources, { fs, curl, tar });
|
|
350
434
|
}
|
|
351
435
|
|
|
352
436
|
// If we now have source files, (re)build from them.
|
|
353
437
|
// Otherwise, require presence of a previously-built executable.
|
|
354
|
-
if (hasSource ||
|
|
438
|
+
if (hasSource || shouldFetchSources) {
|
|
355
439
|
// Force a rebuild if for some reason the binary is out of date
|
|
356
440
|
// since the make checks may not always detect that situation.
|
|
357
441
|
const npm = makeCLI('npm', { spawn });
|
|
@@ -374,9 +458,11 @@ const run = () =>
|
|
|
374
458
|
spawn: childProcessTop.spawn,
|
|
375
459
|
fs: {
|
|
376
460
|
existsSync: fsTop.existsSync,
|
|
377
|
-
rmdirSync: fsTop.rmdirSync,
|
|
378
461
|
readFile: fsTop.promises.readFile,
|
|
379
462
|
writeFile: fsTop.promises.writeFile,
|
|
463
|
+
mkdir: fsTop.promises.mkdir,
|
|
464
|
+
rm: fsTop.promises.rm,
|
|
465
|
+
rename: fsTop.promises.rename,
|
|
380
466
|
},
|
|
381
467
|
os: {
|
|
382
468
|
type: osTop.type,
|