ネガティブインフォメーションも価値があるよね。
やりたかったこと
- 環境はWindowsかMac、両方で開発。
- IDEは問わない。
- ボタンひとつ押すだけでElectronを作成->C++のコードをビルド->Electron立ち上げ->C++をデバッグ
- C++のコードを必ずデバッグするようにすると時間がかかるので、その部分だけビルドしたり飛ばしたりしたい。
- 自動で既に起動している特定のプロセスをアタッチ(デバッグ)したい。
出来たこと
- Electronをビルド->Electronを立ち上げ
- Electronを作成->C++をビルド->Electron立ち上げ。
- IDEはCLionとVScodeである程度は出来た。
- Process IDを指定すれば、Electron起動後にC++のコードをアタッチしてデバッグ可能。でもいちいちProcess IDを特定するのが面倒。
出来なかったこと
- Electronは起動するとMainとRendererで別のプロセスが起動し、Renderer側にC++を実行させたのだが、複数のRendererが立ち上がっているので「どれか1つを特定して」アタッチさせる。
VSCodeでやってみたこと
以下のファイルをプロジェクトの直下において、適当にnpm install
した。
package.json
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build:dev": "./node_modules/.bin/node-gyp rebuild --target=5.0.8 --arch=x64 --dist-url=https://electronjs.org/headers --debug"
},
"devDependencies": {
"electron": "5.0.8",
"electron-prebuilt": "^1.4.13",
"electron-rebuild": "^1.8.5",
"node-gyp": "5.0.0",
"rebuild": "^0.1.2"
},
"dependencies": {}
}
test.cc
#include <node.h>
#include <v8.h>
#include <string.h>
#include <memory>
int data = 0;
void Method(const v8::FunctionCallbackInfo <v8::Value> &args) {
v8::Isolate *isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
std::string strings = "world" + std::to_string(data);
args.GetReturnValue().Set(v8::String::NewFromUtf8(isolate, strings.c_str()));
}
void CountUp(const v8::FunctionCallbackInfo <v8::Value> &args) {
// このへんにブレークポイント設置。
data = data + 1;
}
void init(v8::Local <v8::Object> exports) {
NODE_SET_METHOD(exports, "hello", Method);
NODE_SET_METHOD(exports, "countUp", CountUp);
}
NODE_MODULE(binding, init
);
index.html
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>test</title></head>
<body>
<div id='test'></div>
<button onclick="testFunc()">test button</button>
<button onclick="test2()">Count up</button>
<script>
var remote = require('electron').remote;
var testC = require('./build/Debug/testFunction');
function test2(){
testC.countUp();
}
function testFunc(){
document.getElementById('test').innerText = "hello " + testC.hello();
}
</script>
</body>
</html>
index.js
"use strict";
const electron = require("electron");
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
let mainWindow;
app.on('window-all-closed', function() {
if (process.platform != 'darwin') {
app.quit();
}
});
app.on('ready', function() {
mainWindow = new BrowserWindow({
width: 800, height: 600, title: 'test',darkTheme:true,
webPreferences: {
nodeIntegration: true
}
});
mainWindow.on('page-title-updated', (evt) => {
evt.preventDefault();
});
mainWindow.webContents.openDevTools();
mainWindow.loadURL('file://' + __dirname + '/index.html');
mainWindow.on('closed', function() {
mainWindow = null;
});
});
binding.gyp
{
"targets": [
{
"target_name": "testFunction",
"sources": [
"test.cc"
]
},
]
}
launch.json
実行環境はMac
VSCodeのビルドとかデバッグは、「.vscode/launch.json」に記述してます。
やってみた結果とかをコメントで書いてます。
launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "electron start",
"type": "node",
"request": "launch",
"cwd": "${workspaceRoot}",
"program": "${workspaceRoot}/index.js",
// 実行前にpackage.jsonで指定したビルドを実行
"preLaunchTask": "npm: build:dev",
// Electronを指定
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
// 子プロセスもアタッチしてみた(ただしlldbではなくnode.jsとして)
"autoAttachChildProcesses":true,
"runtimeArgs": [
"--enable-logging",
"--debug=5858",
"--remote-debugging-port=9223",
],
"args": [
".",
],
"windows": {
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
},
"console": "internalConsole",
},
{
"name": "native addon",
// lldbでアタッチ(Mac用)
"type": "lldb",
// 実行前にC++をビルド
"preLaunchTask": "npm: build:dev",
// attachに指定すると、以下の"program"が有効になる。
"request": "attach",
// ↓process name が Electron Helperだからこれでアタッチできるんじゃね?->1個に絞れと怒られた。
// "program": "pgrep -n 'Electron Helper'",
// ↓ダメ元でプログラムの位置を指定してみた->ダメだった。
// "program": "/Applications/Visual Studio Code.app/Contents/MacOS/Electron",
// "program": "${workspaceRoot}/node_modules/electron/dist/Electron.app/Contents/MacOS/Electron",
// "pid": 7669,
// でも上記が上手く行かなかったので手動でprocess idを指定することに・・・
"pid":"${command:pickMyProcess}",
"expressions": "native",
},
{
"name": "native addon custom (error)",
// lldbでアタッチ(Mac用)
"type": "lldb",
// customに指定すると、lldbの細かい動作を指定できる。。
"request": "custom",
"preLaunchTask": "npm: build:dev",
// 1つに絞れと怒られる。
"targetCreateCommands":["process attach --name \"Electron Helper\""],
// pgrepでprocess idを表示はできるが、結局何処に使えば・・・
// pgrep 'Electron Helper'
// shellで実行出来なくはない。でもその値をlldbに持っていけない
// "targetCreateCommands":["platform shell pgrep 'Electron Helper'"],
// process id一覧表示
// "targetCreateCommands":["platform list"],
// 配列で複数の内容を記述も可能。
// "targetCreateCommands":["env ELECTRON_HELPER='Electron Helper'",
// "attach --name 'Electron Helper'",
// "platform shell pgrep 'Electron Helper'",
// "attach ${command:pickProcess}",
// "attach --name '${input:pickProgram}'"
//],
// target createでファイルでの指定はできるが、Electronは一度に複数のHelperを起動するので、その全てをどうやってか指定する必要がある。
// "targetCreateCommands":["target create 'path'"],
"expressions": "native",
}
],
// ちなみに、自分で作ったリストを表示させる方法もあるが、pidは起動ごとに変わるので意味はないかな。
"inputs": [
{
"id": "pickData",
"description": "select",
"type": "pickString",
"options": ["Electron Helper","Electron"],
"default": "Electron Helper"
}
]
}
参考
https://github.com/vadimcn/vscode-lldb/blob/v1.2.3/MANUAL.md
https://electronjs.org/docs/development/debugging-instructions-macos
https://yokaze.github.io/2018/01/06/