gulpfile.vscode.js 24.5 KB
Newer Older
I
isidor 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

J
Joao Moreno 已提交
6 7 8 9
'use strict';

const gulp = require('gulp');
const fs = require('fs');
10 11
const os = require('os');
const cp = require('child_process');
J
Joao Moreno 已提交
12 13 14 15
const path = require('path');
const es = require('event-stream');
const azure = require('gulp-azure-storage');
const electron = require('gulp-atom-electron');
J
Joao Moreno 已提交
16
const vfs = require('vinyl-fs');
J
Joao Moreno 已提交
17 18 19 20 21 22
const rename = require('gulp-rename');
const replace = require('gulp-replace');
const filter = require('gulp-filter');
const json = require('gulp-json-editor');
const _ = require('underscore');
const util = require('./lib/util');
A
Alex Dima 已提交
23
const task = require('./lib/task');
J
Joao Moreno 已提交
24
const ext = require('./lib/extensions');
J
Joao Moreno 已提交
25
const buildfile = require('../src/buildfile');
26
const common = require('./lib/optimize');
J
Joao Moreno 已提交
27 28
const root = path.dirname(__dirname);
const commit = util.getVersion(root);
J
Joao Moreno 已提交
29 30
const packageJson = require('../package.json');
const product = require('../product.json');
A
Alex Dima 已提交
31
const crypto = require('crypto');
32
const i18n = require('./lib/i18n');
33
const deps = require('./dependencies');
J
Joao Moreno 已提交
34
const getElectronVersion = require('./lib/electron').getElectronVersion;
A
Alex Dima 已提交
35
const createAsar = require('./lib/asar').createAsar;
36
const minimist = require('minimist');
A
Alex Dima 已提交
37
const { compileBuildTask } = require('./gulpfile.compile');
J
Joao Moreno 已提交
38

39
const productionDependencies = deps.getProductionDependencies(path.dirname(__dirname));
J
Joao Moreno 已提交
40
// @ts-ignore
J
Joao Moreno 已提交
41 42
const baseModules = Object.keys(process.binding('natives')).filter(n => !/^_|\//.test(n));
const nodeModules = ['electron', 'original-fs']
43
	// @ts-ignore JSON checking: dependencies property is optional
44 45
	.concat(Object.keys(product.dependencies || {}))
	.concat(_.uniq(productionDependencies.map(d => d.name)))
J
Joao Moreno 已提交
46
	.concat(baseModules);
I
isidor 已提交
47 48

// Build
J
Joao Moreno 已提交
49
const vscodeEntryPoints = _.flatten([
50
	buildfile.entrypoint('vs/workbench/workbench.main'),
I
isidor 已提交
51
	buildfile.base,
52 53
	buildfile.workbench,
	buildfile.code
I
isidor 已提交
54 55
]);

J
Joao Moreno 已提交
56
const vscodeResources = [
J
Joao Moreno 已提交
57
	'out-build/main.js',
J
Joao Moreno 已提交
58
	'out-build/cli.js',
J
Joao Moreno 已提交
59
	'out-build/driver.js',
I
isidor 已提交
60
	'out-build/bootstrap.js',
61
	'out-build/bootstrap-fork.js',
J
Joao Moreno 已提交
62
	'out-build/bootstrap-amd.js',
63
	'out-build/bootstrap-window.js',
J
Joao Moreno 已提交
64
	'out-build/paths.js',
J
Joao Moreno 已提交
65
	'out-build/vs/**/*.{svg,png,cur,html}',
A
Alex Dima 已提交
66
	'!out-build/vs/code/browser/**/*.html',
67
	'out-build/vs/base/common/performance.js',
68
	'out-build/vs/base/node/languagePacks.js',
R
Rachel Macfarlane 已提交
69
	'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}',
70
	'out-build/vs/base/browser/ui/octiconLabel/octicons/**',
I
isidor 已提交
71
	'out-build/vs/workbench/browser/media/*-theme.css',
72
	'out-build/vs/workbench/contrib/debug/**/*.json',
73
	'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt',
74 75
	'out-build/vs/workbench/contrib/webview/browser/pre/*.js',
	'out-build/vs/workbench/contrib/webview/electron-browser/pre/*.js',
J
Joao Moreno 已提交
76
	'out-build/vs/**/markdown.css',
77 78
	'out-build/vs/workbench/contrib/tasks/**/*.json',
	'out-build/vs/workbench/contrib/welcome/walkThrough/**/*.md',
B
Benjamin Pasero 已提交
79 80
	'out-build/vs/workbench/services/files/**/*.exe',
	'out-build/vs/workbench/services/files/**/*.md',
81
	'out-build/vs/code/electron-browser/workbench/**',
82
	'out-build/vs/code/electron-browser/sharedProcess/sharedProcess.js',
83
	'out-build/vs/code/electron-browser/issue/issueReporter.js',
84
	'out-build/vs/code/electron-browser/processExplorer/processExplorer.js',
I
isidor 已提交
85 86 87
	'!**/test/**'
];

J
Joao Moreno 已提交
88
const BUNDLED_FILE_HEADER = [
I
isidor 已提交
89 90 91 92 93
	'/*!--------------------------------------------------------',
	' * Copyright (C) Microsoft Corporation. All rights reserved.',
	' *--------------------------------------------------------*/'
].join('\n');

94
const optimizeVSCodeTask = task.define('optimize-vscode', task.series(
A
Alex Dima 已提交
95
	task.parallel(
A
Alex Dima 已提交
96 97 98 99 100 101 102 103 104 105 106 107
		util.rimraf('out-vscode'),
		compileBuildTask
	),
	common.optimizeTask({
		src: 'out-build',
		entryPoints: vscodeEntryPoints,
		resources: vscodeResources,
		loaderConfig: common.loaderConfig(nodeModules),
		header: BUNDLED_FILE_HEADER,
		out: 'out-vscode',
		bundleInfo: undefined
	})
108
));
A
Alex Dima 已提交
109 110


111
const optimizeIndexJSTask = task.define('optimize-index-js', task.series(
A
Alex Dima 已提交
112 113 114 115 116 117 118
	optimizeVSCodeTask,
	() => {
		const fullpath = path.join(process.cwd(), 'out-vscode/bootstrap-window.js');
		const contents = fs.readFileSync(fullpath).toString();
		const newContents = contents.replace('[/*BUILD->INSERT_NODE_MODULES*/]', JSON.stringify(nodeModules));
		fs.writeFileSync(fullpath, newContents);
	}
119
));
120

121
const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${commit}`;
122
const minifyVSCodeTask = task.define('minify-vscode', task.series(
A
Alex Dima 已提交
123
	task.parallel(
A
Alex Dima 已提交
124 125 126 127
		util.rimraf('out-vscode-min'),
		optimizeIndexJSTask
	),
	common.minifyTask('out-vscode', `${sourceMappingURLBase}/core`)
128
));
I
isidor 已提交
129 130

// Package
131 132

// @ts-ignore JSON checking: darwinCredits is optional
J
Joao Moreno 已提交
133
const darwinCreditsTemplate = product.darwinCredits && _.template(fs.readFileSync(path.join(root, product.darwinCredits), 'utf8'));
I
isidor 已提交
134

135 136 137 138 139 140 141 142 143 144
function darwinBundleDocumentType(extensions, icon) {
	return {
		name: product.nameLong + ' document',
		role: 'Editor',
		ostypes: ["TEXT", "utxt", "TUTX", "****"],
		extensions: extensions,
		iconFile: icon
	};
}

J
Joao Moreno 已提交
145
const config = {
J
Joao Moreno 已提交
146
	version: getElectronVersion(),
I
isidor 已提交
147
	productAppName: product.nameLong,
J
Joao Moreno 已提交
148
	companyName: 'Microsoft Corporation',
H
Henk Mollema 已提交
149
	copyright: 'Copyright (C) 2019 Microsoft. All rights reserved',
J
Joao Moreno 已提交
150
	darwinIcon: 'resources/darwin/code.icns',
I
isidor 已提交
151
	darwinBundleIdentifier: product.darwinBundleIdentifier,
J
Joao Moreno 已提交
152
	darwinApplicationCategoryType: 'public.app-category.developer-tools',
153 154
	darwinHelpBookFolder: 'VS Code HelpBook',
	darwinHelpBookName: 'VS Code HelpBook',
155 156 157 158 159 160 161 162 163
	darwinBundleDocumentTypes: [
		darwinBundleDocumentType(["bat", "cmd"], 'resources/darwin/bat.icns'),
		darwinBundleDocumentType(["bowerrc"], 'resources/darwin/bower.icns'),
		darwinBundleDocumentType(["c", "h"], 'resources/darwin/c.icns'),
		darwinBundleDocumentType(["config", "editorconfig", "gitattributes", "gitconfig", "gitignore", "ini"], 'resources/darwin/config.icns'),
		darwinBundleDocumentType(["cc", "cpp", "cxx", "hh", "hpp", "hxx"], 'resources/darwin/cpp.icns'),
		darwinBundleDocumentType(["cs", "csx"], 'resources/darwin/csharp.icns'),
		darwinBundleDocumentType(["css"], 'resources/darwin/css.icns'),
		darwinBundleDocumentType(["go"], 'resources/darwin/go.icns'),
B
Benjamin Pasero 已提交
164
		darwinBundleDocumentType(["asp", "aspx", "cshtml", "htm", "html", "jshtm", "jsp", "phtml", "shtml"], 'resources/darwin/html.icns'),
165 166
		darwinBundleDocumentType(["jade"], 'resources/darwin/jade.icns'),
		darwinBundleDocumentType(["jav", "java"], 'resources/darwin/java.icns'),
B
Benjamin Pasero 已提交
167
		darwinBundleDocumentType(["js", "jscsrc", "jshintrc", "mjs"], 'resources/darwin/javascript.icns'),
168 169 170 171 172 173 174
		darwinBundleDocumentType(["json"], 'resources/darwin/json.icns'),
		darwinBundleDocumentType(["less"], 'resources/darwin/less.icns'),
		darwinBundleDocumentType(["markdown", "md", "mdoc", "mdown", "mdtext", "mdtxt", "mdwn", "mkd", "mkdn"], 'resources/darwin/markdown.icns'),
		darwinBundleDocumentType(["php"], 'resources/darwin/php.icns'),
		darwinBundleDocumentType(["ps1", "psd1", "psm1"], 'resources/darwin/powershell.icns'),
		darwinBundleDocumentType(["py"], 'resources/darwin/python.icns'),
		darwinBundleDocumentType(["gemspec", "rb"], 'resources/darwin/ruby.icns'),
M
Miguel Solorio 已提交
175
		darwinBundleDocumentType(["scss"], 'resources/darwin/sass.icns'),
176 177 178 179 180 181 182
		darwinBundleDocumentType(["bash", "bash_login", "bash_logout", "bash_profile", "bashrc", "profile", "rhistory", "rprofile", "sh", "zlogin", "zlogout", "zprofile", "zsh", "zshenv", "zshrc"], 'resources/darwin/shell.icns'),
		darwinBundleDocumentType(["sql"], 'resources/darwin/sql.icns'),
		darwinBundleDocumentType(["ts"], 'resources/darwin/typescript.icns'),
		darwinBundleDocumentType(["tsx", "jsx"], 'resources/darwin/react.icns'),
		darwinBundleDocumentType(["vue"], 'resources/darwin/vue.icns'),
		darwinBundleDocumentType(["ascx", "csproj", "dtd", "wxi", "wxl", "wxs", "xml", "xaml"], 'resources/darwin/xml.icns'),
		darwinBundleDocumentType(["eyaml", "eyml", "yaml", "yml"], 'resources/darwin/yaml.icns'),
B
Benjamin Pasero 已提交
183
		darwinBundleDocumentType(["clj", "cljs", "cljx", "clojure", "code-workspace", "coffee", "ctp", "dockerfile", "dot", "edn", "fs", "fsi", "fsscript", "fsx", "handlebars", "hbs", "lua", "m", "makefile", "ml", "mli", "pl", "pl6", "pm", "pm6", "pod", "pp", "properties", "psgi", "pug", "r", "rs", "rt", "svg", "svgz", "t", "txt", "vb", "xcodeproj", "xcworkspace"], 'resources/darwin/default.icns')
184
	],
J
Joao Moreno 已提交
185 186 187
	darwinBundleURLTypes: [{
		role: 'Viewer',
		name: product.nameLong,
J
Joao Moreno 已提交
188
		urlSchemes: [product.urlProtocol]
J
Joao Moreno 已提交
189
	}],
190
	darwinForceDarkModeSupport: true,
R
Rob Lourens 已提交
191
	darwinCredits: darwinCreditsTemplate ? Buffer.from(darwinCreditsTemplate({ commit: commit, date: new Date().toISOString() })) : undefined,
192
	linuxExecutableName: product.applicationName,
J
Joao Moreno 已提交
193
	winIcon: 'resources/win32/code.ico',
R
Rob Lourens 已提交
194
	token: process.env['VSCODE_MIXIN_PASSWORD'] || process.env['GITHUB_TOKEN'] || undefined,
195 196

	// @ts-ignore JSON checking: electronRepository is optional
R
Rob Lourens 已提交
197
	repo: product.electronRepository || undefined
I
isidor 已提交
198 199
};

J
fix es6  
Joao Moreno 已提交
200
function getElectron(arch) {
J
Joao Moreno 已提交
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
	return () => {
		const electronOpts = _.extend({}, config, {
			platform: process.platform,
			arch,
			ffmpegChromium: true,
			keepDefaultApp: true
		});

		return gulp.src('package.json')
			.pipe(json({ name: product.nameShort }))
			.pipe(electron(electronOpts))
			.pipe(filter(['**', '!**/app/package.json']))
			.pipe(vfs.dest('.build/electron'));
	};
}
216

217 218 219
gulp.task(task.define('electron', task.series(util.rimraf('.build/electron'), getElectron(process.arch))));
gulp.task(task.define('electron-ia32', task.series(util.rimraf('.build/electron'), getElectron('ia32'))));
gulp.task(task.define('electron-x64', task.series(util.rimraf('.build/electron'), getElectron('x64'))));
J
João Moreno 已提交
220
gulp.task(task.define('electron-arm', task.series(util.rimraf('.build/electron'), getElectron('armv7l'))));
221
gulp.task(task.define('electron-arm64', task.series(util.rimraf('.build/electron'), getElectron('arm64'))));
I
isidor 已提交
222

A
Alex Dima 已提交
223 224 225 226 227 228 229 230 231
/**
 * Compute checksums for some files.
 *
 * @param {string} out The out folder to read the file from.
 * @param {string[]} filenames The paths to compute a checksum for.
 * @return {Object} A map of paths to checksums.
 */
function computeChecksums(out, filenames) {
	var result = {};
232
	filenames.forEach(function (filename) {
A
Alex Dima 已提交
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
		var fullPath = path.join(process.cwd(), out, filename);
		result[filename] = computeChecksum(fullPath);
	});
	return result;
}

/**
 * Compute checksum for a file.
 *
 * @param {string} filename The absolute path to a filename.
 * @return {string} The checksum for `filename`.
 */
function computeChecksum(filename) {
	var contents = fs.readFileSync(filename);

	var hash = crypto
		.createHash('md5')
		.update(contents)
		.digest('base64')
		.replace(/=+$/, '');

	return hash;
}

A
Alex Dima 已提交
257
function packageTask(platform, arch, sourceFolderName, destinationFolderName, opts) {
J
fix es6  
Joao Moreno 已提交
258 259
	opts = opts || {};

A
Alex Dima 已提交
260
	const destination = path.join(path.dirname(root), destinationFolderName);
I
isidor 已提交
261 262
	platform = platform || process.platform;

J
Joao Moreno 已提交
263
	return () => {
A
Alex Dima 已提交
264
		const out = sourceFolderName;
I
isidor 已提交
265

A
Alex Dima 已提交
266
		const checksums = computeChecksums(out, [
267 268
			'vs/workbench/workbench.main.js',
			'vs/workbench/workbench.main.css',
269 270
			'vs/code/electron-browser/workbench/workbench.html',
			'vs/code/electron-browser/workbench/workbench.js'
A
Alex Dima 已提交
271 272
		]);

J
Joao Moreno 已提交
273
		const src = gulp.src(out + '/**', { base: '.' })
274 275 276
			.pipe(rename(function (path) { path.dirname = path.dirname.replace(new RegExp('^' + out), 'out'); }))
			.pipe(util.setExecutableBit(['**/*.sh']))
			.pipe(filter(['**', '!**/*.js.map']));
I
isidor 已提交
277

278 279
		const root = path.resolve(path.join(__dirname, '..'));

280 281
		const sources = es.merge(src, ext.packageExtensionsStream({
			sourceMappingURLBase: sourceMappingURLBase
282 283
		}));

J
Joao Moreno 已提交
284
		let version = packageJson.version;
285
		// @ts-ignore JSON checking: quality is optional
J
Joao Moreno 已提交
286
		const quality = product.quality;
J
Joao Moreno 已提交
287 288 289 290 291

		if (quality && quality !== 'stable') {
			version += '-' + quality;
		}

J
Joao Moreno 已提交
292
		const name = product.nameShort;
293 294 295 296 297 298 299
		const packageJsonUpdates = { name, version };

		// for linux url handling
		if (platform === 'linux') {
			packageJsonUpdates.desktopName = `${product.applicationName}-url-handler.desktop`;
		}

J
Joao Moreno 已提交
300
		const packageJsonStream = gulp.src(['package.json'], { base: '.' })
301
			.pipe(json(packageJsonUpdates));
J
Joao Moreno 已提交
302 303

		const date = new Date().toISOString();
304 305
		const productJsonUpdate = { commit, date, checksums };

306
		if (shouldSetupSettingsSearch()) {
307 308 309
			productJsonUpdate.settingsSearchBuildId = getSettingsSearchBuildId(packageJson);
		}

J
Joao Moreno 已提交
310
		const productJsonStream = gulp.src(['product.json'], { base: '.' })
311
			.pipe(json(productJsonUpdate));
I
isidor 已提交
312

J
Joao Moreno 已提交
313
		const license = gulp.src(['LICENSES.chromium.html', product.licenseFileName, 'ThirdPartyNotices.txt', 'licenses/**'], { base: '.', allowEmpty: true });
I
isidor 已提交
314

J
Joao Moreno 已提交
315 316 317
		// TODO the API should be copied to `out` during compile, not here
		const api = gulp.src('src/vs/vscode.d.ts').pipe(rename('out/vs/vscode.d.ts'));

318 319
		const depsSrc = [
			..._.flatten(productionDependencies.map(d => path.relative(root, d.path)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`])),
320
			// @ts-ignore JSON checking: dependencies is optional
321 322
			..._.flatten(Object.keys(product.dependencies || {}).map(d => [`node_modules/${d}/**`, `!node_modules/${d}/**/{test,tests}/**`]))
		];
I
isidor 已提交
323

J
Joao Moreno 已提交
324
		const deps = gulp.src(depsSrc, { base: '.', dot: true })
J
Joao Moreno 已提交
325
			.pipe(filter(['**', '!**/package-lock.json']))
326
			.pipe(util.cleanNodeModules(path.join(__dirname, '.nativeignore')))
A
Alex Dima 已提交
327
			.pipe(createAsar(path.join(process.cwd(), 'node_modules'), ['**/*.node', '**/vscode-ripgrep/bin/*', '**/node-pty/build/Release/*'], 'app/node_modules.asar'));
I
isidor 已提交
328

J
Joao Moreno 已提交
329
		let all = es.merge(
330
		packageJsonStream,
J
Joao Moreno 已提交
331
			productJsonStream,
I
isidor 已提交
332
			license,
J
Joao Moreno 已提交
333
			api,
I
isidor 已提交
334
			sources,
J
Joao Moreno 已提交
335
			deps
J
Joao Moreno 已提交
336 337 338
		);

		if (platform === 'win32') {
J
Joao Moreno 已提交
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
			all = es.merge(all, gulp.src([
				'resources/win32/bower.ico',
				'resources/win32/c.ico',
				'resources/win32/config.ico',
				'resources/win32/cpp.ico',
				'resources/win32/csharp.ico',
				'resources/win32/css.ico',
				'resources/win32/default.ico',
				'resources/win32/go.ico',
				'resources/win32/html.ico',
				'resources/win32/jade.ico',
				'resources/win32/java.ico',
				'resources/win32/javascript.ico',
				'resources/win32/json.ico',
				'resources/win32/less.ico',
				'resources/win32/markdown.ico',
				'resources/win32/php.ico',
				'resources/win32/powershell.ico',
				'resources/win32/python.ico',
				'resources/win32/react.ico',
				'resources/win32/ruby.ico',
				'resources/win32/sass.ico',
				'resources/win32/shell.ico',
				'resources/win32/sql.ico',
				'resources/win32/typescript.ico',
				'resources/win32/vue.ico',
				'resources/win32/xml.ico',
				'resources/win32/yaml.ico',
				'resources/win32/code_70x70.png',
				'resources/win32/code_150x150.png'
			], { base: '.' }));
J
Joao Moreno 已提交
370 371
		} else if (platform === 'linux') {
			all = es.merge(all, gulp.src('resources/linux/code.png', { base: '.' }));
J
Joao Moreno 已提交
372
		} else if (platform === 'darwin') {
J
Joao Moreno 已提交
373
			const shortcut = gulp.src('resources/darwin/bin/code.sh')
J
Joao Moreno 已提交
374 375 376
				.pipe(rename('bin/code'));

			all = es.merge(all, shortcut);
J
Joao Moreno 已提交
377
		}
I
isidor 已提交
378

J
Joao Moreno 已提交
379
		let result = all
J
Joao Moreno 已提交
380
			.pipe(util.skipDirectories())
I
isidor 已提交
381
			.pipe(util.fixWin32DirectoryPermissions())
382
			.pipe(electron(_.extend({}, config, { platform, arch, ffmpegChromium: true })))
383
			.pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version']));
I
isidor 已提交
384

J
Joao Moreno 已提交
385
		// result = es.merge(result, gulp.src('resources/completions/**', { base: '.' }));
386

I
isidor 已提交
387
		if (platform === 'win32') {
A
Alex Dima 已提交
388
			result = es.merge(result, gulp.src('resources/win32/bin/code.js', { base: 'resources/win32', allowEmpty: true }));
J
Joao Moreno 已提交
389

J
typo  
João Moreno 已提交
390
			result = es.merge(result, gulp.src('resources/win32/bin/code.cmd', { base: 'resources/win32' })
J
Joao Moreno 已提交
391
				.pipe(replace('@@NAME@@', product.nameShort))
J
Joao Moreno 已提交
392 393
				.pipe(rename(function (f) { f.basename = product.applicationName; })));

J
typo  
João Moreno 已提交
394
			result = es.merge(result, gulp.src('resources/win32/bin/code.sh', { base: 'resources/win32' })
J
Joao Moreno 已提交
395
				.pipe(replace('@@NAME@@', product.nameShort))
M
Martin Aeschlimann 已提交
396 397
				.pipe(replace('@@PRODNAME@@', product.nameLong))
				.pipe(replace('@@VERSION@@', version))
A
Alex Dima 已提交
398 399
				.pipe(replace('@@COMMIT@@', commit))
				.pipe(replace('@@APPNAME@@', product.applicationName))
M
Martin Aeschlimann 已提交
400
				.pipe(replace('@@DATAFOLDER@@', product.dataFolderName))
M
Martin Aeschlimann 已提交
401
				.pipe(replace('@@QUALITY@@', quality))
J
Joao Moreno 已提交
402
				.pipe(rename(function (f) { f.basename = product.applicationName; f.extname = ''; })));
403 404

			result = es.merge(result, gulp.src('resources/win32/VisualElementsManifest.xml', { base: 'resources/win32' })
405
				.pipe(rename(product.nameShort + '.VisualElementsManifest.xml')));
406 407
		} else if (platform === 'linux') {
			result = es.merge(result, gulp.src('resources/linux/bin/code.sh', { base: '.' })
M
Martin Aeschlimann 已提交
408
				.pipe(replace('@@PRODNAME@@', product.nameLong))
409 410
				.pipe(replace('@@NAME@@', product.applicationName))
				.pipe(rename('bin/' + product.applicationName)));
I
isidor 已提交
411 412
		}

J
Johannes Rieken 已提交
413 414 415 416 417
		// submit all stats that have been collected
		// during the build phase
		if (opts.stats) {
			result.on('end', () => {
				const { submitAllStats } = require('./lib/stats');
J
Johannes Rieken 已提交
418
				submitAllStats(product, commit).then(() => console.log('Submitted bundle stats!'));
J
Johannes Rieken 已提交
419 420 421
			});
		}

J
Joao Moreno 已提交
422
		return result.pipe(vfs.dest(destination));
I
isidor 已提交
423 424 425
	};
}

J
Joao Moreno 已提交
426 427
const buildRoot = path.dirname(root);

A
Alex Dima 已提交
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
const BUILD_TARGETS = [
	{ platform: 'win32', arch: 'ia32' },
	{ platform: 'win32', arch: 'x64' },
	{ platform: 'darwin', arch: null, opts: { stats: true } },
	{ platform: 'linux', arch: 'ia32' },
	{ platform: 'linux', arch: 'x64' },
	{ platform: 'linux', arch: 'arm' },
	{ platform: 'linux', arch: 'arm64' },
];
BUILD_TARGETS.forEach(buildTarget => {
	const dashed = (str) => (str ? `-${str}` : ``);
	const platform = buildTarget.platform;
	const arch = buildTarget.arch;
	const opts = buildTarget.opts;

	['', 'min'].forEach(minified => {
		const sourceFolderName = `out-vscode${dashed(minified)}`;
		const destinationFolderName = `VSCode${dashed(platform)}${dashed(arch)}`;

447
		const vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series(
A
Alex Dima 已提交
448
			task.parallel(
A
Alex Dima 已提交
449 450 451 452
				minified ? minifyVSCodeTask : optimizeVSCodeTask,
				util.rimraf(path.join(buildRoot, destinationFolderName))
			),
			packageTask(platform, arch, sourceFolderName, destinationFolderName, opts)
453 454
		));
		gulp.task(vscodeTask);
A
Alex Dima 已提交
455 456
	});
});
I
isidor 已提交
457

458
// Transifex Localizations
D
Dirk Baeumer 已提交
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473

const innoSetupConfig = {
	'zh-cn': { codePage: 'CP936', defaultInfo: { name: 'Simplified Chinese', id: '$0804', } },
	'zh-tw': { codePage: 'CP950', defaultInfo: { name: 'Traditional Chinese', id: '$0404' } },
	'ko': { codePage: 'CP949', defaultInfo: { name: 'Korean', id: '$0412' } },
	'ja': { codePage: 'CP932' },
	'de': { codePage: 'CP1252' },
	'fr': { codePage: 'CP1252' },
	'es': { codePage: 'CP1252' },
	'ru': { codePage: 'CP1251' },
	'it': { codePage: 'CP1252' },
	'pt-br': { codePage: 'CP1252' },
	'hu': { codePage: 'CP1250' },
	'tr': { codePage: 'CP1254' }
};
474

475
const apiHostname = process.env.TRANSIFEX_API_URL;
476 477 478
const apiName = process.env.TRANSIFEX_API_NAME;
const apiToken = process.env.TRANSIFEX_API_TOKEN;

479 480
gulp.task(task.define(
	'vscode-translations-push',
A
Alex Dima 已提交
481
	task.series(
A
Alex Dima 已提交
482 483 484 485 486 487 488 489 490 491 492 493 494 495
		optimizeVSCodeTask,
		function () {
			const pathToMetadata = './out-vscode/nls.metadata.json';
			const pathToExtensions = './extensions/*';
			const pathToSetup = 'build/win32/**/{Default.isl,messages.en.isl}';

			return es.merge(
				gulp.src(pathToMetadata).pipe(i18n.createXlfFilesForCoreBundle()),
				gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()),
				gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions())
			).pipe(i18n.findObsoleteResources(apiHostname, apiName, apiToken)
			).pipe(i18n.pushXlfFiles(apiHostname, apiName, apiToken));
		}
	)
496
));
D
Dirk Baeumer 已提交
497

498 499
gulp.task(task.define(
	'vscode-translations-export',
A
Alex Dima 已提交
500
	task.series(
A
Alex Dima 已提交
501 502 503 504 505 506 507 508 509 510 511 512 513
		optimizeVSCodeTask,
		function () {
			const pathToMetadata = './out-vscode/nls.metadata.json';
			const pathToExtensions = './extensions/*';
			const pathToSetup = 'build/win32/**/{Default.isl,messages.en.isl}';

			return es.merge(
				gulp.src(pathToMetadata).pipe(i18n.createXlfFilesForCoreBundle()),
				gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()),
				gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions())
			).pipe(vfs.dest('../vscode-translations-export'));
		}
	)
514
));
D
Dirk Baeumer 已提交
515 516

gulp.task('vscode-translations-pull', function () {
517
	return es.merge([...i18n.defaultLanguages, ...i18n.extraLanguages].map(language => {
D
Dirk Baeumer 已提交
518
		let includeDefault = !!innoSetupConfig[language.id].defaultInfo;
519
		return i18n.pullSetupXlfFiles(apiHostname, apiName, apiToken, language, includeDefault).pipe(vfs.dest(`../vscode-translations-import/${language.id}/setup`));
520
	}));
521 522
});

523
gulp.task('vscode-translations-import', function () {
524 525 526 527 528 529
	var options = minimist(process.argv.slice(2), {
		string: 'location',
		default: {
			location: '../vscode-translations-import'
		}
	});
530
	return es.merge([...i18n.defaultLanguages, ...i18n.extraLanguages].map(language => {
531 532
		let id = language.transifexId || language.id;
		return gulp.src(`${options.location}/${id}/setup/*/*.xlf`)
D
Dirk Baeumer 已提交
533 534
			.pipe(i18n.prepareIslFiles(language, innoSetupConfig[language.id]))
			.pipe(vfs.dest(`./build/win32/i18n`));
535
	}));
536 537
});

I
isidor 已提交
538 539
// Sourcemaps

540
gulp.task('upload-vscode-sourcemaps', () => {
J
Joao Moreno 已提交
541 542
	const vs = gulp.src('out-vscode-min/**/*.map', { base: 'out-vscode-min' })
		.pipe(es.mapSync(f => {
543
			f.path = `${f.base}/core/${f.relative}`;
J
Joao Moreno 已提交
544 545 546
			return f;
		}));

547 548
	const extensionsOut = gulp.src(['extensions/**/out/**/*.map', '!extensions/**/node_modules/**'], { base: '.' });
	const extensionsDist = gulp.src(['extensions/**/dist/**/*.map', '!extensions/**/node_modules/**'], { base: '.' });
J
Joao Moreno 已提交
549

550
	return es.merge(vs, extensionsOut, extensionsDist)
J
Johannes Rieken 已提交
551 552 553 554 555
		.pipe(es.through(function (data) {
			// debug
			console.log('Uploading Sourcemap', data.relative);
			this.emit('data', data);
		}))
I
isidor 已提交
556 557 558 559
		.pipe(azure.upload({
			account: process.env.AZURE_STORAGE_ACCOUNT,
			key: process.env.AZURE_STORAGE_ACCESS_KEY,
			container: 'sourcemaps',
J
Joao Moreno 已提交
560
			prefix: commit + '/'
I
isidor 已提交
561
		}));
562
});
563

564
// This task is only run for the MacOS build
565
const generateVSCodeConfigurationTask = task.define('generate-vscode-configuration', () => {
566 567 568 569 570 571
	return new Promise((resolve, reject) => {
		const buildDir = process.env['AGENT_BUILDDIRECTORY'];
		if (!buildDir) {
			return reject(new Error('$AGENT_BUILDDIRECTORY not set'));
		}

J
Joao Moreno 已提交
572 573 574 575
		if (process.env.VSCODE_QUALITY !== 'insider' && process.env.VSCODE_QUALITY !== 'stable') {
			return resolve();
		}

576 577
		const userDataDir = path.join(os.tmpdir(), 'tmpuserdata');
		const extensionsDir = path.join(os.tmpdir(), 'tmpextdir');
578 579
		const appName = process.env.VSCODE_QUALITY === 'insider' ? 'Visual\\ Studio\\ Code\\ -\\ Insiders.app' : 'Visual\\ Studio\\ Code.app';
		const appPath = path.join(buildDir, `VSCode-darwin/${appName}/Contents/Resources/app/bin/code`);
580
		const codeProc = cp.exec(`${appPath} --export-default-configuration='${allConfigDetailsPath}' --wait --user-data-dir='${userDataDir}' --extensions-dir='${extensionsDir}'`);
581 582 583

		const timer = setTimeout(() => {
			codeProc.kill();
584
			reject(new Error('export-default-configuration process timed out'));
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
		}, 10 * 1000);

		codeProc.stdout.on('data', d => console.log(d.toString()));
		codeProc.stderr.on('data', d => console.log(d.toString()));

		codeProc.on('exit', () => {
			clearTimeout(timer);
			resolve();
		});

		codeProc.on('error', err => {
			clearTimeout(timer);
			reject(err);
		});
	});
600
});
A
Alex Dima 已提交
601 602

const allConfigDetailsPath = path.join(os.tmpdir(), 'configuration.json');
603 604
gulp.task(task.define(
	'upload-vscode-configuration',
A
Alex Dima 已提交
605
	task.series(
A
Alex Dima 已提交
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
		generateVSCodeConfigurationTask,
		() => {
			if (!shouldSetupSettingsSearch()) {
				const branch = process.env.BUILD_SOURCEBRANCH;
				console.log(`Only runs on master and release branches, not ${branch}`);
				return;
			}

			if (!fs.existsSync(allConfigDetailsPath)) {
				throw new Error(`configuration file at ${allConfigDetailsPath} does not exist`);
			}

			const settingsSearchBuildId = getSettingsSearchBuildId(packageJson);
			if (!settingsSearchBuildId) {
				throw new Error('Failed to compute build number');
			}

			return gulp.src(allConfigDetailsPath)
				.pipe(azure.upload({
					account: process.env.AZURE_STORAGE_ACCOUNT,
					key: process.env.AZURE_STORAGE_ACCESS_KEY,
					container: 'configuration',
					prefix: `${settingsSearchBuildId}/${commit}/`
				}));
		}
	)
632
));
A
Alex Dima 已提交
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656

function shouldSetupSettingsSearch() {
	const branch = process.env.BUILD_SOURCEBRANCH;
	return branch && (/\/master$/.test(branch) || branch.indexOf('/release/') >= 0);
}

function getSettingsSearchBuildId(packageJson) {
	try {
		const branch = process.env.BUILD_SOURCEBRANCH;
		const branchId = branch.indexOf('/release/') >= 0 ? 0 :
			/\/master$/.test(branch) ? 1 :
				2; // Some unexpected branch

		const out = cp.execSync(`git rev-list HEAD --count`);
		const count = parseInt(out.toString());

		// <version number><commit count><branchId (avoid unlikely conflicts)>
		// 1.25.1, 1,234,567 commits, master = 1250112345671
		return util.versionStringToNumber(packageJson.version) * 1e8 + count * 10 + branchId;
	} catch (e) {
		throw new Error('Could not determine build number: ' + e.toString());
	}
}