diff --git a/src/cli.js b/src/cli.js index db7b6cce206..1bb0476f61f 100644 --- a/src/cli.js +++ b/src/cli.js @@ -3,6 +3,39 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API) + +const fs = require('fs'); +const path = require('path'); +const product = require('../product.json'); +const appRoot = path.dirname(__dirname); + +function getApplicationPath() { + if (process.env['VSCODE_DEV']) { + return appRoot; + } else if (process.platform === 'darwin') { + return path.dirname(path.dirname(path.dirname(appRoot))); + } else { + return path.dirname(path.dirname(appRoot)); + } +} + +const portableDataName = product.portable || `${product.applicationName}-portable-data`; +const portableDataPath = path.join(path.dirname(getApplicationPath()), portableDataName); +const isPortable = fs.existsSync(portableDataPath); +const portableTempPath = path.join(portableDataPath, 'tmp'); +const isTempPortable = isPortable && fs.existsSync(portableTempPath); + +if (isPortable) { + process.env['VSCODE_PORTABLE'] = portableDataPath; +} else { + delete process.env['VSCODE_PORTABLE']; +} + +if (isTempPortable) { + process.env[process.platform === 'win32' ? 'TEMP' : 'TMPDIR'] = portableTempPath; +} + //#region Add support for using node_modules.asar (function () { const path = require('path'); diff --git a/src/main.js b/src/main.js index 228dc60348e..268e143d53c 100644 --- a/src/main.js +++ b/src/main.js @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -let perf = require('./vs/base/common/performance'); +const perf = require('./vs/base/common/performance'); perf.mark('main:started'); // Perf measurements @@ -12,6 +12,37 @@ global.perfStartTime = Date.now(); Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API) +const fs = require('fs'); +const path = require('path'); +const product = require('../product.json'); +const appRoot = path.dirname(__dirname); + +function getApplicationPath() { + if (process.env['VSCODE_DEV']) { + return appRoot; + } else if (process.platform === 'darwin') { + return path.dirname(path.dirname(path.dirname(appRoot))); + } else { + return path.dirname(path.dirname(appRoot)); + } +} + +const portableDataName = product.portable || `${product.applicationName}-portable-data`; +const portableDataPath = process.env['VSCODE_PORTABLE'] || path.join(path.dirname(getApplicationPath()), portableDataName); +const isPortable = fs.existsSync(portableDataPath); +const portableTempPath = path.join(portableDataPath, 'tmp'); +const isTempPortable = isPortable && fs.existsSync(portableTempPath); + +if (isPortable) { + process.env['VSCODE_PORTABLE'] = portableDataPath; +} else { + delete process.env['VSCODE_PORTABLE']; +} + +if (isTempPortable) { + process.env[process.platform === 'win32' ? 'TEMP' : 'TMPDIR'] = portableTempPath; +} + //#region Add support for using node_modules.asar (function () { const path = require('path'); @@ -36,7 +67,7 @@ Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https: })(); //#endregion -let app = require('electron').app; +const app = require('electron').app; // TODO@Ben Electron 2.0.x: prevent localStorage migration from SQLite to LevelDB due to issues app.commandLine.appendSwitch('disable-mojo-local-storage'); @@ -44,15 +75,19 @@ app.commandLine.appendSwitch('disable-mojo-local-storage'); // TODO@Ben Electron 2.0.x: force srgb color profile (for https://github.com/Microsoft/vscode/issues/51791) app.commandLine.appendSwitch('force-color-profile', 'srgb'); -let fs = require('fs'); -let path = require('path'); -let minimist = require('minimist'); -let paths = require('./paths'); +const minimist = require('minimist'); +const paths = require('./paths'); -let args = minimist(process.argv, { - string: ['user-data-dir', 'locale'] +const args = minimist(process.argv, { + string: [ + 'user-data-dir', + 'locale', + 'js-flags', + 'max-memory' + ] }); +//#region NLS function stripComments(content) { let regexp = /("(?:[^\\\"]*(?:\\.)?)*")|('(?:[^\\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g; let result = content.replace(regexp, function (match, m1, m2, m3, m4) { @@ -77,113 +112,38 @@ function stripComments(content) { return result; } -let _commit; -function getCommit() { - if (_commit) { - return _commit; - } - if (_commit === null) { - return undefined; - } - try { - let productJson = require(path.join(__dirname, '../product.json')); - if (productJson.commit) { - _commit = productJson.commit; - } else { - _commit = null; - } - } catch (exp) { - _commit = null; - } - return _commit || undefined; -} +const mkdir = dir => new Promise((c, e) => fs.mkdir(dir, err => (err && err.code !== 'EEXIST') ? e(err) : c())); +const exists = file => new Promise(c => fs.exists(file, c)); +const readFile = file => new Promise((c, e) => fs.readFile(file, 'utf8', (err, data) => err ? e(err) : c(data))); +const writeFile = (file, content) => new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c())); +const touch = file => new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); }); function mkdirp(dir) { - return mkdir(dir) - .then(null, (err) => { - if (err && err.code === 'ENOENT') { - let parent = path.dirname(dir); - if (parent !== dir) { // if not arrived at root - return mkdirp(parent) - .then(() => { - return mkdir(dir); - }); - } - } - throw err; - }); -} + return mkdir(dir).then(null, err => { + if (err && err.code === 'ENOENT') { + const parent = path.dirname(dir); -function mkdir(dir) { - return new Promise((resolve, reject) => { - fs.mkdir(dir, (err) => { - if (err && err.code !== 'EEXIST') { - reject(err); - } else { - resolve(dir); + if (parent !== dir) { // if not arrived at root + return mkdirp(parent).then(() => mkdir(dir)); } - }); - }); -} + } -function exists(file) { - return new Promise((resolve) => { - fs.exists(file, (result) => { - resolve(result); - }); - }); -} - -function readFile(file) { - return new Promise((resolve, reject) => { - fs.readFile(file, 'utf8', (err, data) => { - if (err) { - reject(err); - return; - } - resolve(data); - }); - }); -} - -function writeFile(file, content) { - return new Promise((resolve, reject) => { - fs.writeFile(file, content, 'utf8', (err) => { - if (err) { - reject(err); - return; - } - resolve(undefined); - }); - }); -} - -function touch(file) { - return new Promise((resolve, reject) => { - let d = new Date(); - fs.utimes(file, d, d, (err) => { - if (err) { - reject(err); - return; - } - resolve(undefined); - }); + throw err; }); } function resolveJSFlags() { - let jsFlags = []; + const jsFlags = []; + if (args['js-flags']) { jsFlags.push(args['js-flags']); } + if (args['max-memory'] && !/max_old_space_size=(\d+)/g.exec(args['js-flags'])) { jsFlags.push(`--max_old_space_size=${args['max-memory']}`); } - if (jsFlags.length > 0) { - return jsFlags.join(' '); - } else { - return null; - } + + return jsFlags.length > 0 ? jsFlags.join(' ') : null; } // Language tags are case insensitve however an amd loader is case sensitive @@ -288,7 +248,7 @@ function getNLSConfiguration(locale) { } perf.mark('nlsGeneration:start'); - let defaultResult = function(locale) { + let defaultResult = function (locale) { let isCoreLanguage = true; if (locale) { isCoreLanguage = ['de', 'es', 'fr', 'it', 'ja', 'ko', 'ru', 'zh-cn', 'zh-tw'].some((language) => { @@ -299,13 +259,13 @@ function getNLSConfiguration(locale) { let result = resolveLocale(locale); perf.mark('nlsGeneration:end'); return Promise.resolve(result); - } else { + } else { perf.mark('nlsGeneration:end'); return Promise.resolve({ locale: locale, availableLanguages: {} }); } }; try { - let commit = getCommit(); + let commit = product.commit; if (!commit) { return defaultResult(locale); } @@ -342,7 +302,7 @@ function getNLSConfiguration(locale) { return exists(coreLocation).then((fileExists) => { if (fileExists) { // We don't wait for this. No big harm if we can't touch - touch(coreLocation).catch(() => {}); + touch(coreLocation).catch(() => { }); perf.mark('nlsGeneration:end'); return result; } @@ -395,7 +355,9 @@ function getNLSConfiguration(locale) { return defaultResult(locale); } } +//#endregion +//#region Cached Data Dir function getNodeCachedDataDir() { // flag to disable cached data support if (process.argv.indexOf('--no-cached-data') > 0) { @@ -408,7 +370,7 @@ function getNodeCachedDataDir() { } // find commit id - let commit = getCommit(); + let commit = product.commit; if (!commit) { return Promise.resolve(undefined); } @@ -417,10 +379,18 @@ function getNodeCachedDataDir() { return mkdirp(dir).then(undefined, function () { /*ignore*/ }); } +//#endregion + +function getUserDataPath() { + if (isPortable) { + return path.join(portableDataPath, 'user-data'); + } + + return path.resolve(args['user-data-dir'] || paths.getDefaultUserDataPath(process.platform)); +} // Set userData path before app 'ready' event and call to process.chdir -let userData = path.resolve(args['user-data-dir'] || paths.getDefaultUserDataPath(process.platform)); -app.setPath('userData', userData); +app.setPath('userData', getUserDataPath()); // Update cwd based on environment and platform try { diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 9eb5162971c..632ecd517ab 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -323,6 +323,11 @@ function main() { VSCODE_NLS_CONFIG: process.env['VSCODE_NLS_CONFIG'], VSCODE_LOGS: process.env['VSCODE_LOGS'] }; + + if (process.env['VSCODE_PORTABLE']) { + instanceEnv['VSCODE_PORTABLE'] = process.env['VSCODE_PORTABLE']; + } + assign(process.env, instanceEnv); // Startup diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 381202a6d1b..06e2171dfb5 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -90,7 +90,13 @@ export class EnvironmentService implements IEnvironmentService { get userHome(): string { return os.homedir(); } @memoize - get userDataPath(): string { return parseUserDataDir(this._args, process); } + get userDataPath(): string { + if (process.env['VSCODE_PORTABLE']) { + return path.join(process.env['VSCODE_PORTABLE'], 'user-data'); + } + + return parseUserDataDir(this._args, process); + } get appNameLong(): string { return product.nameLong; } @@ -127,7 +133,19 @@ export class EnvironmentService implements IEnvironmentService { get installSourcePath(): string { return path.join(this.userDataPath, 'installSource'); } @memoize - get extensionsPath(): string { return parsePathArg(this._args['extensions-dir'], process) || process.env['VSCODE_EXTENSIONS'] || path.join(this.userHome, product.dataFolderName, 'extensions'); } + get extensionsPath(): string { + const fromArgs = parsePathArg(this._args['extensions-dir'], process); + + if (fromArgs) { + return fromArgs; + } else if (process.env['VSCODE_EXTENSIONS']) { + return process.env['VSCODE_EXTENSIONS']; + } else if (process.env['VSCODE_PORTABLE']) { + return path.join(process.env['VSCODE_PORTABLE'], 'extensions'); + } else { + return path.join(this.userHome, product.dataFolderName, 'extensions'); + } + } @memoize get extensionDevelopmentPath(): string { return this._args.extensionDevelopmentPath ? path.normalize(this._args.extensionDevelopmentPath) : this._args.extensionDevelopmentPath; } diff --git a/src/vs/platform/node/product.ts b/src/vs/platform/node/product.ts index f85d99660c5..685ccc5d374 100644 --- a/src/vs/platform/node/product.ts +++ b/src/vs/platform/node/product.ts @@ -73,6 +73,7 @@ export interface IProductConfiguration { 'darwin': string; }; logUploaderUrl: string; + portable?: string; } export interface ISurveyData { diff --git a/src/vs/workbench/parts/terminal/node/terminalProcess.ts b/src/vs/workbench/parts/terminal/node/terminalProcess.ts index e3a73831538..2ad362e73d2 100644 --- a/src/vs/workbench/parts/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/parts/terminal/node/terminalProcess.ts @@ -116,7 +116,8 @@ function cleanEnv() { 'PTYCOLS', 'PTYROWS', 'PTYSHELLCMDLINE', - 'VSCODE_LOGS' + 'VSCODE_LOGS', + 'VSCODE_PORTABLE' ]; keys.forEach(function (key) { if (process.env[key]) {