Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/build/gulpfile.reh.js
3520 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
'use strict';
7
8
const gulp = require('gulp');
9
const path = require('path');
10
const es = require('event-stream');
11
const util = require('./lib/util');
12
const { getVersion } = require('./lib/getVersion');
13
const task = require('./lib/task');
14
const optimize = require('./lib/optimize');
15
const { inlineMeta } = require('./lib/inlineMeta');
16
const product = require('../product.json');
17
const rename = require('gulp-rename');
18
const replace = require('gulp-replace');
19
const filter = require('gulp-filter');
20
const { getProductionDependencies } = require('./lib/dependencies');
21
const { readISODate } = require('./lib/date');
22
const vfs = require('vinyl-fs');
23
const packageJson = require('../package.json');
24
const flatmap = require('gulp-flatmap');
25
const gunzip = require('gulp-gunzip');
26
const File = require('vinyl');
27
const fs = require('fs');
28
const glob = require('glob');
29
const { compileBuildWithManglingTask } = require('./gulpfile.compile');
30
const { cleanExtensionsBuildTask, compileNonNativeExtensionsBuildTask, compileNativeExtensionsBuildTask, compileExtensionMediaBuildTask } = require('./gulpfile.extensions');
31
const { vscodeWebResourceIncludes, createVSCodeWebFileContentMapper } = require('./gulpfile.vscode.web');
32
const cp = require('child_process');
33
const log = require('fancy-log');
34
const buildfile = require('./buildfile');
35
36
const REPO_ROOT = path.dirname(__dirname);
37
const commit = getVersion(REPO_ROOT);
38
const BUILD_ROOT = path.dirname(REPO_ROOT);
39
const REMOTE_FOLDER = path.join(REPO_ROOT, 'remote');
40
41
// Targets
42
43
const BUILD_TARGETS = [
44
{ platform: 'win32', arch: 'x64' },
45
{ platform: 'win32', arch: 'arm64' },
46
{ platform: 'darwin', arch: 'x64' },
47
{ platform: 'darwin', arch: 'arm64' },
48
{ platform: 'linux', arch: 'x64' },
49
{ platform: 'linux', arch: 'armhf' },
50
{ platform: 'linux', arch: 'arm64' },
51
{ platform: 'alpine', arch: 'arm64' },
52
// legacy: we use to ship only one alpine so it was put in the arch, but now we ship
53
// multiple alpine images and moved to a better model (alpine as the platform)
54
{ platform: 'linux', arch: 'alpine' },
55
];
56
57
const serverResourceIncludes = [
58
59
// NLS
60
'out-build/nls.messages.json',
61
'out-build/nls.keys.json',
62
63
// Process monitor
64
'out-build/vs/base/node/cpuUsage.sh',
65
'out-build/vs/base/node/ps.sh',
66
67
// External Terminal
68
'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt',
69
70
// Terminal shell integration
71
'out-build/vs/workbench/contrib/terminal/common/scripts/shellIntegration.ps1',
72
'out-build/vs/workbench/contrib/terminal/common/scripts/CodeTabExpansion.psm1',
73
'out-build/vs/workbench/contrib/terminal/common/scripts/GitTabExpansion.psm1',
74
'out-build/vs/workbench/contrib/terminal/common/scripts/shellIntegration-bash.sh',
75
'out-build/vs/workbench/contrib/terminal/common/scripts/shellIntegration-env.zsh',
76
'out-build/vs/workbench/contrib/terminal/common/scripts/shellIntegration-profile.zsh',
77
'out-build/vs/workbench/contrib/terminal/common/scripts/shellIntegration-rc.zsh',
78
'out-build/vs/workbench/contrib/terminal/common/scripts/shellIntegration-login.zsh',
79
'out-build/vs/workbench/contrib/terminal/common/scripts/shellIntegration.fish',
80
81
];
82
83
const serverResourceExcludes = [
84
'!out-build/vs/**/{electron-browser,electron-main,electron-utility}/**',
85
'!out-build/vs/editor/standalone/**',
86
'!out-build/vs/workbench/**/*-tb.png',
87
'!**/test/**'
88
];
89
90
const serverResources = [
91
...serverResourceIncludes,
92
...serverResourceExcludes
93
];
94
95
const serverWithWebResourceIncludes = [
96
...serverResourceIncludes,
97
'out-build/vs/code/browser/workbench/*.html',
98
...vscodeWebResourceIncludes
99
];
100
101
const serverWithWebResourceExcludes = [
102
...serverResourceExcludes,
103
'!out-build/vs/code/**/*-dev.html'
104
];
105
106
const serverWithWebResources = [
107
...serverWithWebResourceIncludes,
108
...serverWithWebResourceExcludes
109
];
110
const serverEntryPoints = buildfile.codeServer;
111
112
const webEntryPoints = [
113
buildfile.workerEditor,
114
buildfile.workerExtensionHost,
115
buildfile.workerNotebook,
116
buildfile.workerLanguageDetection,
117
buildfile.workerLocalFileSearch,
118
buildfile.workerOutputLinks,
119
buildfile.workerBackgroundTokenization,
120
buildfile.keyboardMaps,
121
buildfile.codeWeb
122
].flat();
123
124
const serverWithWebEntryPoints = [
125
126
// Include all of server
127
...serverEntryPoints,
128
129
// Include all of web
130
...webEntryPoints,
131
].flat();
132
133
const bootstrapEntryPoints = [
134
'out-build/server-main.js',
135
'out-build/server-cli.js',
136
'out-build/bootstrap-fork.js'
137
];
138
139
function getNodeVersion() {
140
const npmrc = fs.readFileSync(path.join(REPO_ROOT, 'remote', '.npmrc'), 'utf8');
141
const nodeVersion = /^target="(.*)"$/m.exec(npmrc)[1];
142
const internalNodeVersion = /^ms_build_id="(.*)"$/m.exec(npmrc)[1];
143
return { nodeVersion, internalNodeVersion };
144
}
145
146
function getNodeChecksum(expectedName) {
147
const nodeJsChecksums = fs.readFileSync(path.join(REPO_ROOT, 'build', 'checksums', 'nodejs.txt'), 'utf8');
148
for (const line of nodeJsChecksums.split('\n')) {
149
const [checksum, name] = line.split(/\s+/);
150
if (name === expectedName) {
151
return checksum;
152
}
153
}
154
return undefined;
155
}
156
157
function extractAlpinefromDocker(nodeVersion, platform, arch) {
158
const imageName = arch === 'arm64' ? 'arm64v8/node' : 'node';
159
log(`Downloading node.js ${nodeVersion} ${platform} ${arch} from docker image ${imageName}`);
160
const contents = cp.execSync(`docker run --rm ${imageName}:${nodeVersion}-alpine /bin/sh -c 'cat \`which node\`'`, { maxBuffer: 100 * 1024 * 1024, encoding: 'buffer' });
161
return es.readArray([new File({ path: 'node', contents, stat: { mode: parseInt('755', 8) } })]);
162
}
163
164
const { nodeVersion, internalNodeVersion } = getNodeVersion();
165
166
BUILD_TARGETS.forEach(({ platform, arch }) => {
167
gulp.task(task.define(`node-${platform}-${arch}`, () => {
168
const nodePath = path.join('.build', 'node', `v${nodeVersion}`, `${platform}-${arch}`);
169
170
if (!fs.existsSync(nodePath)) {
171
util.rimraf(nodePath);
172
173
return nodejs(platform, arch)
174
.pipe(vfs.dest(nodePath));
175
}
176
177
return Promise.resolve(null);
178
}));
179
});
180
181
const defaultNodeTask = gulp.task(`node-${process.platform}-${process.arch}`);
182
183
if (defaultNodeTask) {
184
gulp.task(task.define('node', defaultNodeTask));
185
}
186
187
function nodejs(platform, arch) {
188
const { fetchUrls, fetchGithub } = require('./lib/fetch');
189
const untar = require('gulp-untar');
190
191
if (arch === 'armhf') {
192
arch = 'armv7l';
193
} else if (arch === 'alpine') {
194
platform = 'alpine';
195
arch = 'x64';
196
}
197
198
log(`Downloading node.js ${nodeVersion} ${platform} ${arch} from ${product.nodejsRepository}...`);
199
200
const glibcPrefix = process.env['VSCODE_NODE_GLIBC'] ?? '';
201
let expectedName;
202
switch (platform) {
203
case 'win32':
204
expectedName = product.nodejsRepository !== 'https://nodejs.org' ?
205
`win-${arch}-node.exe` : `win-${arch}/node.exe`;
206
break;
207
208
case 'darwin':
209
expectedName = `node-v${nodeVersion}-${platform}-${arch}.tar.gz`;
210
break;
211
case 'linux':
212
expectedName = `node-v${nodeVersion}${glibcPrefix}-${platform}-${arch}.tar.gz`;
213
break;
214
case 'alpine':
215
expectedName = `node-v${nodeVersion}-linux-${arch}-musl.tar.gz`;
216
break;
217
}
218
const checksumSha256 = getNodeChecksum(expectedName);
219
220
if (checksumSha256) {
221
log(`Using SHA256 checksum for checking integrity: ${checksumSha256}`);
222
} else {
223
log.warn(`Unable to verify integrity of downloaded node.js binary because no SHA256 checksum was found!`);
224
}
225
226
switch (platform) {
227
case 'win32':
228
return (product.nodejsRepository !== 'https://nodejs.org' ?
229
fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: expectedName, checksumSha256 }) :
230
fetchUrls(`/dist/v${nodeVersion}/win-${arch}/node.exe`, { base: 'https://nodejs.org', checksumSha256 }))
231
.pipe(rename('node.exe'));
232
case 'darwin':
233
case 'linux':
234
return (product.nodejsRepository !== 'https://nodejs.org' ?
235
fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: expectedName, checksumSha256 }) :
236
fetchUrls(`/dist/v${nodeVersion}/node-v${nodeVersion}-${platform}-${arch}.tar.gz`, { base: 'https://nodejs.org', checksumSha256 })
237
).pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar())))
238
.pipe(filter('**/node'))
239
.pipe(util.setExecutableBit('**'))
240
.pipe(rename('node'));
241
case 'alpine':
242
return product.nodejsRepository !== 'https://nodejs.org' ?
243
fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: expectedName, checksumSha256 })
244
.pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar())))
245
.pipe(filter('**/node'))
246
.pipe(util.setExecutableBit('**'))
247
.pipe(rename('node'))
248
: extractAlpinefromDocker(nodeVersion, platform, arch);
249
}
250
}
251
252
function packageTask(type, platform, arch, sourceFolderName, destinationFolderName) {
253
const destination = path.join(BUILD_ROOT, destinationFolderName);
254
255
return () => {
256
const json = require('gulp-json-editor');
257
258
const src = gulp.src(sourceFolderName + '/**', { base: '.' })
259
.pipe(rename(function (path) { path.dirname = path.dirname.replace(new RegExp('^' + sourceFolderName), 'out'); }))
260
.pipe(util.setExecutableBit(['**/*.sh']))
261
.pipe(filter(['**', '!**/*.{js,css}.map']));
262
263
const workspaceExtensionPoints = ['debuggers', 'jsonValidation'];
264
const isUIExtension = (manifest) => {
265
switch (manifest.extensionKind) {
266
case 'ui': return true;
267
case 'workspace': return false;
268
default: {
269
if (manifest.main) {
270
return false;
271
}
272
if (manifest.contributes && Object.keys(manifest.contributes).some(key => workspaceExtensionPoints.indexOf(key) !== -1)) {
273
return false;
274
}
275
// Default is UI Extension
276
return true;
277
}
278
}
279
};
280
const localWorkspaceExtensions = glob.sync('extensions/*/package.json')
281
.filter((extensionPath) => {
282
if (type === 'reh-web') {
283
return true; // web: ship all extensions for now
284
}
285
286
// Skip shipping UI extensions because the client side will have them anyways
287
// and they'd just increase the download without being used
288
const manifest = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, extensionPath)).toString());
289
return !isUIExtension(manifest);
290
}).map((extensionPath) => path.basename(path.dirname(extensionPath)))
291
.filter(name => name !== 'vscode-api-tests' && name !== 'vscode-test-resolver'); // Do not ship the test extensions
292
const marketplaceExtensions = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, 'product.json'), 'utf8')).builtInExtensions
293
.filter(entry => !entry.platforms || new Set(entry.platforms).has(platform))
294
.filter(entry => !entry.clientOnly)
295
.map(entry => entry.name);
296
const extensionPaths = [...localWorkspaceExtensions, ...marketplaceExtensions]
297
.map(name => `.build/extensions/${name}/**`);
298
299
const extensions = gulp.src(extensionPaths, { base: '.build', dot: true });
300
const extensionsCommonDependencies = gulp.src('.build/extensions/node_modules/**', { base: '.build', dot: true });
301
const sources = es.merge(src, extensions, extensionsCommonDependencies)
302
.pipe(filter(['**', '!**/*.{js,css}.map'], { dot: true }));
303
304
let version = packageJson.version;
305
const quality = product.quality;
306
307
if (quality && quality !== 'stable') {
308
version += '-' + quality;
309
}
310
311
const name = product.nameShort;
312
313
let packageJsonContents;
314
const packageJsonStream = gulp.src(['remote/package.json'], { base: 'remote' })
315
.pipe(json({ name, version, dependencies: undefined, optionalDependencies: undefined, type: 'module' }))
316
.pipe(es.through(function (file) {
317
packageJsonContents = file.contents.toString();
318
this.emit('data', file);
319
}));
320
321
let productJsonContents;
322
const productJsonStream = gulp.src(['product.json'], { base: '.' })
323
.pipe(json({ commit, date: readISODate('out-build'), version }))
324
.pipe(es.through(function (file) {
325
productJsonContents = file.contents.toString();
326
this.emit('data', file);
327
}));
328
329
const license = gulp.src(['remote/LICENSE'], { base: 'remote', allowEmpty: true });
330
331
const jsFilter = util.filter(data => !data.isDirectory() && /\.js$/.test(data.path));
332
333
const productionDependencies = getProductionDependencies(REMOTE_FOLDER);
334
const dependenciesSrc = productionDependencies.map(d => path.relative(REPO_ROOT, d)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`, `!${d}/.bin/**`]).flat();
335
const deps = gulp.src(dependenciesSrc, { base: 'remote', dot: true })
336
// filter out unnecessary files, no source maps in server build
337
.pipe(filter(['**', '!**/package-lock.json', '!**/*.{js,css}.map']))
338
.pipe(util.cleanNodeModules(path.join(__dirname, '.moduleignore')))
339
.pipe(util.cleanNodeModules(path.join(__dirname, `.moduleignore.${process.platform}`)))
340
.pipe(jsFilter)
341
.pipe(util.stripSourceMappingURL())
342
.pipe(jsFilter.restore);
343
344
const nodePath = `.build/node/v${nodeVersion}/${platform}-${arch}`;
345
const node = gulp.src(`${nodePath}/**`, { base: nodePath, dot: true });
346
347
let web = [];
348
if (type === 'reh-web') {
349
web = [
350
'resources/server/favicon.ico',
351
'resources/server/code-192.png',
352
'resources/server/code-512.png',
353
'resources/server/manifest.json'
354
].map(resource => gulp.src(resource, { base: '.' }).pipe(rename(resource)));
355
}
356
357
const all = es.merge(
358
packageJsonStream,
359
productJsonStream,
360
license,
361
sources,
362
deps,
363
node,
364
...web
365
);
366
367
let result = all
368
.pipe(util.skipDirectories())
369
.pipe(util.fixWin32DirectoryPermissions());
370
371
if (platform === 'win32') {
372
result = es.merge(result,
373
gulp.src('resources/server/bin/remote-cli/code.cmd', { base: '.' })
374
.pipe(replace('@@VERSION@@', version))
375
.pipe(replace('@@COMMIT@@', commit))
376
.pipe(replace('@@APPNAME@@', product.applicationName))
377
.pipe(rename(`bin/remote-cli/${product.applicationName}.cmd`)),
378
gulp.src('resources/server/bin/helpers/browser.cmd', { base: '.' })
379
.pipe(replace('@@VERSION@@', version))
380
.pipe(replace('@@COMMIT@@', commit))
381
.pipe(replace('@@APPNAME@@', product.applicationName))
382
.pipe(rename(`bin/helpers/browser.cmd`)),
383
gulp.src('resources/server/bin/code-server.cmd', { base: '.' })
384
.pipe(rename(`bin/${product.serverApplicationName}.cmd`)),
385
);
386
} else if (platform === 'linux' || platform === 'alpine' || platform === 'darwin') {
387
result = es.merge(result,
388
gulp.src(`resources/server/bin/remote-cli/${platform === 'darwin' ? 'code-darwin.sh' : 'code-linux.sh'}`, { base: '.' })
389
.pipe(replace('@@VERSION@@', version))
390
.pipe(replace('@@COMMIT@@', commit))
391
.pipe(replace('@@APPNAME@@', product.applicationName))
392
.pipe(rename(`bin/remote-cli/${product.applicationName}`))
393
.pipe(util.setExecutableBit()),
394
gulp.src(`resources/server/bin/helpers/${platform === 'darwin' ? 'browser-darwin.sh' : 'browser-linux.sh'}`, { base: '.' })
395
.pipe(replace('@@VERSION@@', version))
396
.pipe(replace('@@COMMIT@@', commit))
397
.pipe(replace('@@APPNAME@@', product.applicationName))
398
.pipe(rename(`bin/helpers/browser.sh`))
399
.pipe(util.setExecutableBit()),
400
gulp.src(`resources/server/bin/${platform === 'darwin' ? 'code-server-darwin.sh' : 'code-server-linux.sh'}`, { base: '.' })
401
.pipe(rename(`bin/${product.serverApplicationName}`))
402
.pipe(util.setExecutableBit())
403
);
404
}
405
406
if (platform === 'linux' || platform === 'alpine') {
407
result = es.merge(result,
408
gulp.src(`resources/server/bin/helpers/check-requirements-linux.sh`, { base: '.' })
409
.pipe(rename(`bin/helpers/check-requirements.sh`))
410
.pipe(util.setExecutableBit())
411
);
412
}
413
414
result = inlineMeta(result, {
415
targetPaths: bootstrapEntryPoints,
416
packageJsonFn: () => packageJsonContents,
417
productJsonFn: () => productJsonContents
418
});
419
420
return result.pipe(vfs.dest(destination));
421
};
422
}
423
424
/**
425
* @param {object} product The parsed product.json file contents
426
*/
427
function tweakProductForServerWeb(product) {
428
const result = { ...product };
429
delete result.webEndpointUrlTemplate;
430
return result;
431
}
432
433
['reh', 'reh-web'].forEach(type => {
434
const bundleTask = task.define(`bundle-vscode-${type}`, task.series(
435
util.rimraf(`out-vscode-${type}`),
436
optimize.bundleTask(
437
{
438
out: `out-vscode-${type}`,
439
esm: {
440
src: 'out-build',
441
entryPoints: [
442
...(type === 'reh' ? serverEntryPoints : serverWithWebEntryPoints),
443
...bootstrapEntryPoints
444
],
445
resources: type === 'reh' ? serverResources : serverWithWebResources,
446
fileContentMapper: createVSCodeWebFileContentMapper('.build/extensions', type === 'reh-web' ? tweakProductForServerWeb(product) : product)
447
}
448
}
449
)
450
));
451
452
const minifyTask = task.define(`minify-vscode-${type}`, task.series(
453
bundleTask,
454
util.rimraf(`out-vscode-${type}-min`),
455
optimize.minifyTask(`out-vscode-${type}`, `https://main.vscode-cdn.net/sourcemaps/${commit}/core`)
456
));
457
gulp.task(minifyTask);
458
459
BUILD_TARGETS.forEach(buildTarget => {
460
const dashed = (str) => (str ? `-${str}` : ``);
461
const platform = buildTarget.platform;
462
const arch = buildTarget.arch;
463
464
['', 'min'].forEach(minified => {
465
const sourceFolderName = `out-vscode-${type}${dashed(minified)}`;
466
const destinationFolderName = `vscode-${type}${dashed(platform)}${dashed(arch)}`;
467
468
const serverTaskCI = task.define(`vscode-${type}${dashed(platform)}${dashed(arch)}${dashed(minified)}-ci`, task.series(
469
compileNativeExtensionsBuildTask,
470
gulp.task(`node-${platform}-${arch}`),
471
util.rimraf(path.join(BUILD_ROOT, destinationFolderName)),
472
packageTask(type, platform, arch, sourceFolderName, destinationFolderName)
473
));
474
gulp.task(serverTaskCI);
475
476
const serverTask = task.define(`vscode-${type}${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series(
477
compileBuildWithManglingTask,
478
cleanExtensionsBuildTask,
479
compileNonNativeExtensionsBuildTask,
480
compileExtensionMediaBuildTask,
481
minified ? minifyTask : bundleTask,
482
serverTaskCI
483
));
484
gulp.task(serverTask);
485
});
486
});
487
});
488
489