编译运行
- fork vscode的源代码,并clone到本地, checkout到master分支
- 执行
yarn install
安装依赖,具体node环境查看官方文档 - 执行
yarn watch
编译代码并且执行实时监控修改进行增量编译 - 执行
./script/code.sh
启动electron程序
1. yarn watch
分析
利用gulp watch监控目录变化,实时编译最新的代码
gulp watch --max_old_space_size=4095
gulp会自动读取执行目录下的gulpfile.js
文件,并执行,下面分析gulpfile.js
gulpfile.js
定义了compile-client, watch-client, compile, watch,default五种任务,先看watch,也就是我们上面执行的命令
- default
可以看到,执行watch的时候并没有执行编译步骤,应该是在default中定义了执行watch前需要执行的流程。
// monacoTypecheckTask monaco编辑器编译
// compileClientTask 客户端基础代码编译
// compileExtensionsTask 客户端基础扩展插件代码编译
const compileTask = task.define('compile', task.parallel(monacoTypecheckTask, compileClientTask, compileExtensionsTask));
gulp.task(compileTask);
- monacoTypecheckTask (gulpfile.editor.js)
// 利用tsc,以及tsconfig.monaco.json对monaco编辑器进行编译检查
// 主要代码在src/vs/editor下面,另外的依赖可查看tsconfig.monaco.json
const args = ['./node_modules/.bin/tsc', '-p', './src/tsconfig.monaco.json', '--noEmit'];
- compileClientTask(compilation.ts)
function createCompile(src: string, build: boolean, emitError?: boolean): (token?: util.ICancellationToken) => NodeJS.ReadWriteStream {
//获取typescript编译选项
const opts = _.clone(getTypeScriptCompilerOptions(src));
//是否代码映射
opts.inlineSources = !!build;
opts.noFilesystemLookup = true;
//利用gulp-tsb编译typescript代码
const ts = tsb.create(opts, true, undefined, err => reporter(err.toString()));
...................................
}
这三步编译后的js代码输出在out文件夹下
- watch
包含了watchClientTask
和watchExtensionTask
,watchClientTask
两个Task,分别监控客户端代码和插件代码的文件夹。核心代码在build/lib/compilation.ts
下,
- watchClientTask
核心逻辑在build/lib/compilations.ts
的watchTask
方法内
// watch核心逻辑, 变化后编译
const watchSrc = watch('src/**', { base: 'src' });
let generator = new MonacoGenerator(true);
generator.execute();
return watchSrc
.pipe(generator.stream)
//incremental增量编译, 暂时不细看
.pipe(util.incremental(compile, src, true))
//输出目录
.pipe(gulp.dest(out));
- watchExtensionsTask
核心逻辑在gulpfile.extansions.js
下
// 遍历extensions文件夹,找到有tsconfig.json文件的插件
const compilations = glob.sync('**/tsconfig.json', {
cwd: extensionsPath,
ignore: ['**/out/**', '**/node_modules/**']
});
const getBaseUrl = out => `https://ticino.blob.core.windows.net/sourcemaps/${commit}/${out}`;
//找到需要编译的文件夹后生成对应extensions的compile\watch\build任务
//watch的核心其实也是watcher
const tasks = compilations.map(function (tsconfigFile) {
.......
const watchTask = task.define(`watch-extension:${name}`, task.series(cleanTask, () => {
const pipeline = createPipeline(false);
const input = gulp.src(src, srcOpts);
const watchInput = watcher(src, srcOpts);
return watchInput
.pipe(util.incremental(pipeline, input))
.pipe(gulp.dest(out));
}));
.......
}
自此所有的ts和css的编译完成,并输出到out
文件夹,一旦修改了src以及extensions文件夹下的文件,watch会自动重新进行增量编译
2. ./script/code.sh
分析
- 找到对应平台的electron可执行文件
if [[ "$OSTYPE" == "darwin"* ]]; then
NAME=`node -p "require('./product.json').nameLong"`
CODE="./.build/electron/$NAME.app/Contents/MacOS/Electron"
else
NAME=`node -p "require('./product.json').applicationName"`
CODE=".build/electron/$NAME"
fi
- 执行一系列的单元测试
- 启动
# Launch Code 从哪里启动的? 直接根据package.json中指定的 "main": "./out/main" 启动main进程
exec "$CODE" . "$@"
自此,整个程序就已经完成了编译和启动。
3. 调试
为了方便以后的修改或者看源码,就需要能动态调试整个项目,下面介绍用WebStorm结合Chrome DevTools来动态调试主进程和渲染进程的方法
- 调试渲染进程
每个渲染进程都运行了一个Chromium实例,其自带了完整的Chrome DevTools套件,只需要在焦点窗口处按下 'Cmd + Opt + I’即可打开,也可以通过菜单’Help -> Toggle Developer Tools’打开。
接下来切换到Sources,或者按下’Cmd + P’查找desktop.main.ts
,在onWindowResize方法处下断点,如图
然后调整vscode客户端的大小,就会发现跑到了断点处,渲染进程调试成功
- 调试Node进程
Electron的主进程运行在Node环境中,某些耗时操作运行在fork出来的Node进程中,例如搜索,在启动./script/code.sh后,出现类似的如下日志
Debugger listening on ws://127.0.0.1:5876/bd5aa4a9-d80e-4bfa-bb68-5a0e783de665
For help, see: https://nodejs.org/en/docs/inspector
可知node利用 inspector参数开启了5876端口监听debug(注意:如果5876端口被占用会自动开启其他端口,要根据日志输出,设置不同的端口)利用WebStorm的Debug连接上5876端口即可实现Node进程调试,配置如下
Attach成功,则会出现如下日志
Debugger attached.
接下来尝试在vs/services/search/node/searchIpc.ts
中的SearchChannel -> listene
方法打断点,接下来执行vscode中的搜索功能,则可以看到运行到了断点处
至此整个vscode的编译运行调试完成