|
| 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 | +//@ts-check |
| 9 | + |
| 10 | +const es = require('event-stream'); |
| 11 | +const gulp = require('gulp'); |
| 12 | +const path = require('path'); |
| 13 | +const fancyLog = require('fancy-log'); |
| 14 | +const ansiColors = require('ansi-colors'); |
| 15 | +const cp = require('child_process'); |
| 16 | +const { tmpdir } = require('os'); |
| 17 | +const { promises: fs, existsSync, mkdirSync, rmSync } = require('fs'); |
| 18 | + |
| 19 | +const task = require('./lib/task'); |
| 20 | +const watcher = require('./lib/watch'); |
| 21 | +const { debounce } = require('./lib/util'); |
| 22 | +const createReporter = require('./lib/reporter').createReporter; |
| 23 | + |
| 24 | +const root = 'cli'; |
| 25 | +const rootAbs = path.resolve(__dirname, '..', root); |
| 26 | +const src = `${root}/src`; |
| 27 | +const targetCliPath = path.join(root, 'target', 'debug', process.platform === 'win32' ? 'code.exe' : 'code'); |
| 28 | + |
| 29 | +const platformOpensslDirName = |
| 30 | + process.platform === 'win32' ? ( |
| 31 | + process.arch === 'arm64' |
| 32 | + ? 'arm64-windows-static-md' |
| 33 | + : process.arch === 'ia32' |
| 34 | + ? 'x86-windows-static-md' |
| 35 | + : 'x64-windows-static-md') |
| 36 | + : process.platform === 'darwin' ? ( |
| 37 | + process.arch === 'arm64' |
| 38 | + ? 'arm64-osx' |
| 39 | + : 'x64-osx') |
| 40 | + : (process.arch === 'arm64' |
| 41 | + ? 'arm64-linux' |
| 42 | + : process.arch === 'arm' |
| 43 | + ? 'arm-linux' |
| 44 | + : 'x64-linux'); |
| 45 | +const platformOpensslDir = path.join(rootAbs, 'openssl', 'package', 'out', platformOpensslDirName); |
| 46 | + |
| 47 | +const hasLocalRust = (() => { |
| 48 | + /** @type boolean | undefined */ |
| 49 | + let result = undefined; |
| 50 | + return () => { |
| 51 | + if (result !== undefined) { |
| 52 | + return result; |
| 53 | + } |
| 54 | + |
| 55 | + try { |
| 56 | + const r = cp.spawnSync('cargo', ['--version']); |
| 57 | + result = r.status === 0; |
| 58 | + } catch (e) { |
| 59 | + result = false; |
| 60 | + } |
| 61 | + |
| 62 | + return result; |
| 63 | + }; |
| 64 | +})(); |
| 65 | + |
| 66 | +const debounceEsStream = (fn, duration = 100) => { |
| 67 | + let handle = undefined; |
| 68 | + let pending = []; |
| 69 | + const sendAll = (pending) => (event, ...args) => { |
| 70 | + for (const stream of pending) { |
| 71 | + pending.emit(event, ...args); |
| 72 | + } |
| 73 | + }; |
| 74 | + |
| 75 | + return es.map(function (_, callback) { |
| 76 | + console.log('defer'); |
| 77 | + if (handle !== undefined) { |
| 78 | + clearTimeout(handle); |
| 79 | + } |
| 80 | + |
| 81 | + handle = setTimeout(() => { |
| 82 | + handle = undefined; |
| 83 | + |
| 84 | + const previous = pending; |
| 85 | + pending = []; |
| 86 | + fn() |
| 87 | + .on('error', sendAll('error')) |
| 88 | + .on('data', sendAll('data')) |
| 89 | + .on('end', sendAll('end')); |
| 90 | + }, duration); |
| 91 | + |
| 92 | + pending.push(this); |
| 93 | + }); |
| 94 | +}; |
| 95 | + |
| 96 | +const compileFromSources = (callback) => { |
| 97 | + const proc = cp.spawn('cargo', ['--color', 'always', 'build'], { |
| 98 | + cwd: root, |
| 99 | + stdio: ['ignore', 'pipe', 'pipe'], |
| 100 | + env: existsSync(platformOpensslDir) ? { OPENSSL_DIR: platformOpensslDir, ...process.env } : process.env |
| 101 | + }); |
| 102 | + |
| 103 | + /** @type Buffer[] */ |
| 104 | + const stdoutErr = []; |
| 105 | + proc.stdout.on('data', d => stdoutErr.push(d)); |
| 106 | + proc.stderr.on('data', d => stdoutErr.push(d)); |
| 107 | + proc.on('error', callback); |
| 108 | + proc.on('exit', code => { |
| 109 | + if (code !== 0) { |
| 110 | + callback(Buffer.concat(stdoutErr).toString()); |
| 111 | + } else { |
| 112 | + callback(); |
| 113 | + } |
| 114 | + }); |
| 115 | +}; |
| 116 | + |
| 117 | +const acquireBuiltOpenSSL = (callback) => { |
| 118 | + const untar = require('gulp-untar'); |
| 119 | + const gunzip = require('gulp-gunzip'); |
| 120 | + const dir = path.join(tmpdir(), 'vscode-openssl-download'); |
| 121 | + mkdirSync(dir, { recursive: true }); |
| 122 | + |
| 123 | + cp.spawnSync( |
| 124 | + process.platform === 'win32' ? 'npm.cmd' : 'npm', |
| 125 | + ['pack', '@vscode/openssl-prebuilt'], |
| 126 | + { stdio: ['ignore', 'ignore', 'inherit'], cwd: dir } |
| 127 | + ); |
| 128 | + |
| 129 | + gulp.src('*.tgz', { cwd: dir }) |
| 130 | + .pipe(gunzip()) |
| 131 | + .pipe(untar()) |
| 132 | + .pipe(gulp.dest(`${root}/openssl`)) |
| 133 | + .on('error', callback) |
| 134 | + .on('end', () => { |
| 135 | + rmSync(dir, { recursive: true, force: true }); |
| 136 | + callback(); |
| 137 | + }); |
| 138 | +}; |
| 139 | + |
| 140 | +const compileWithOpenSSLCheck = (/** @type import('./lib/reporter').IReporter */ reporter) => es.map((_, callback) => { |
| 141 | + compileFromSources(err => { |
| 142 | + if (!err) { |
| 143 | + // no-op |
| 144 | + } else if (err.toString().includes('Could not find directory of OpenSSL installation') && !existsSync(platformOpensslDir)) { |
| 145 | + fancyLog(ansiColors.yellow(`[cli]`), 'OpenSSL libraries not found, acquiring prebuilt bits...'); |
| 146 | + acquireBuiltOpenSSL(err => { |
| 147 | + if (err) { |
| 148 | + callback(err); |
| 149 | + } else { |
| 150 | + compileFromSources(err => { |
| 151 | + if (err) { |
| 152 | + reporter(err.toString()); |
| 153 | + } |
| 154 | + callback(null, ''); |
| 155 | + }); |
| 156 | + } |
| 157 | + }); |
| 158 | + } else { |
| 159 | + reporter(err.toString()); |
| 160 | + } |
| 161 | + |
| 162 | + callback(null, ''); |
| 163 | + }); |
| 164 | +}); |
| 165 | + |
| 166 | +const warnIfRustNotInstalled = () => { |
| 167 | + if (!hasLocalRust()) { |
| 168 | + fancyLog(ansiColors.yellow(`[cli]`), 'No local Rust install detected, compilation may fail.'); |
| 169 | + fancyLog(ansiColors.yellow(`[cli]`), 'Get rust from: https://rustup.rs/'); |
| 170 | + } |
| 171 | +}; |
| 172 | + |
| 173 | +const compileCliTask = task.define('compile-cli', () => { |
| 174 | + warnIfRustNotInstalled(); |
| 175 | + const reporter = createReporter('cli'); |
| 176 | + return gulp.src(`${root}/Cargo.toml`) |
| 177 | + .pipe(compileWithOpenSSLCheck(reporter)) |
| 178 | + .pipe(reporter.end(true)); |
| 179 | +}); |
| 180 | + |
| 181 | + |
| 182 | +const watchCliTask = task.define('watch-cli', () => { |
| 183 | + warnIfRustNotInstalled(); |
| 184 | + return watcher(`${src}/**`, { read: false }) |
| 185 | + .pipe(debounce(compileCliTask)); |
| 186 | +}); |
| 187 | + |
| 188 | +gulp.task(compileCliTask); |
| 189 | +gulp.task(watchCliTask); |
0 commit comments