はじめに
Windows のデスクトップアプリを作るのに自分は、以前は Delphi を使っていて、仕事は WinForms+VB.NET を使っていて、趣味は Electron も使っています。新たにアプリを作り始めるのに、別の開発手段も試してみようと思いました。
Wails とは
Wails は、Go 言語とウェブ技術を使用して、デスクトップアプリの構築を可能にします。「Go の力によって、Electron が軽量かつ高速になったようなもの」と考えるとよいでしょう。
(イントロダクション | Wails)
アプリ全体のうち Go の部分は、アプリのコードとランタイムライブラリで構成されています。 フロントエンドは webkit ウィンドウであり、フロンドエンドアセットをウィンドウ上に表示します。Go のメソッドはフロントエンドにバインドされ、ローカルの JavaScript メソッドであるかのようにフロントエンドから呼び出すことができます。
(どうやって動いているの? | Wails)
いわゆる「OS のネイティブアプリ」は、OS 上で実行できるバイナリファイルで、OS の機能を呼出して動作します。開発環境や開発言語が OS ごとに用意され、その OS 用の実行ファイルが作成されます。同じソースコードのプログラムを異なる OS で動作できません。
これに対して、いわゆる「ウェブアプリ」は、HTML と JavaScript で書かれたプログラムが、ウェブブラウザに読込され実行されます。同じ動作するブラウザがあれば同じウェブアプリのプログラムが異なる OS で動作します。ウェブブラウザが呼出できない OS の機能は利用できません。
「Electron」を使ったアプリは、ウェブアプリ+ウェブブラウザが実行ファイルになってネイティブアプリのように OS 上で動作します。さらに Node.js アプリを内包できて、ウェブブラウザで呼出できない OS の機能も呼出できるようになっています。ウェブブラウザおよび Node.js エンジンを OS ごとに切替して実行ファイルを作成することで、同じ HTML+JavaScript のプログラムで異なる OS で動作するアプリを作成できます。ウェブブラウザおよび Node.js エンジンを内包するため、OS ネイティブアプリに比べて実行ファイルが大きくなってしまいます。
「Tauri」アプリは、Electron の Node.js アプリの部分が、Rust アプリになっています。また Electron アプリはブラウザ機能を内容しているのに対して、OS が持つブラウザ機能を呼出します。このため、Electron アプリに比べて実行ファイルが小さくなります。
「Wails」アプリは、Tauri の Rust アプリの部分が、Go アプリになっています。
Wails を始めてみた
- Wails 2.9
開発および実行できる環境
Wails は、以下の環境で開発できて、作成したアプリが実行できます。
- Windows 10 および 11 (x64 および ARM64)
- MacOS 10.13 以降 (x64) 、MacOS 11.0 以降 (ARM64)
- Linux (x64 および ARM64)
開発環境を準備する
以下の手順で開発環境を準備します。
Go 環境をインストールする
まず Go 環境をインストールします。
Download and install - The Go Programming Language
npm をインストールする
続いて npm をインストールします。
WebView2 ランタイムをインストールする(Windows 環境の場合)
Windows 11 は WebView2 ランタイムを標準で持っています。そうでなければ Windows 環境に WebView2 ランタイムをインストールします。
Microsoft Edge WebView2 | Microsoft Edge Developer
xcode コマンドラインツールをインストールする(macOS 環境の場合)
macOS 環境に xcode コマンドラインツールをインストールします。
XcodeのCommand Line Toolsのみインストールする #Mac - Qiita
プロジェクトを新規作成する
プロジェクトを作成したいフォルダに移動してコマンドを実行します。
wails init -n アプリ名 -t フレームワーク名
アプリ名で指定された名前のフォルダが作成されてソースコード一式がセットされます。
例えば
wails init -n HelloWails -t vanilla
新規プロジェクトの内容を確認する
アプリのベースになるプロジェクトは簡単に作成できましたが、ファイルが幾つもあります。どのファイルを変更すればいいのか。それを知るために、プロジェクトに含まれるファイルを調べてみました。
main.go
エントリポイントは、main.go
にあります。
package main
(中略)
func main() {
// Create an instance of the app structure
app := NewApp()
// Create application with options
err := wails.Run(&options.App{
Title: "HelloWails",
(中略)
OnStartup: app.startup,
Bind: []interface{}{
app,
},
})
if err != nil {
println("Error:", err.Error())
}
アプリが起動するときの状態を指定できそうです。
app.go
同じパッケージ main
が main.go
と別に app.go
に分けて記述されています。
package main
(中略)
// App struct
type App struct {
ctx context.Context
}
// NewApp creates a new App application struct
func NewApp() *App {
return &App{}
}
// startup is called when the app starts. The context is saved // so we can call the runtime methods
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
}
ここまでは手を入れる余地なさそうです。main.go
に記述してあっていいように思いました。
(前略)
// Greet returns a greeting for the given name
func (a *App) Greet(name string) string {
return fmt.Sprintf("Hello %s, It's show time!", name)
}
ここからアプリの独自のコードのようです。↑
frontend/wailsjs/go/main/App.js
上記の app.go
のコードがフロントエンドのプログラムで使用できるよう設定されます。
export function Greet(arg1) {
return window['go']['main']['App']['Greet'](arg1);
}
export function Greet(arg1:string):Promise<string>;
この設定は wails generate module
すると app.go
の内容を読んで自動で作成されます。
frontend/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>HelloWails</title>
</head>
<body>
<div id="app"></div>
<script src="./src/main.js" type="module"></script>
</body>
</html>
フレームワーク Vanilla JS
を指定しましたが、HTML ファイルは ID app
の div
タグだけ用意するスタイルになっています。他のフレームワークを指定しても同じ内容でした。
frontend/src/main.js
import {Greet} from '../wailsjs/go/main/App';
(中略)
// Call App.Greet(name)
try {
Greet(name)
.then((result) => {
// Update result with data back from App.Greet()
resultElement.innerText = result;
})
.catch((err) => {
console.error(err);
});
} catch (err) {
console.error(err);
}
};
上記の app.go
のコードがフロントエンドのプログラムで使用できています。↑
アプリのアイコンを変更する
アプリのアイコンを変更できます。
Wailsでアプリをビルドするときにアイコンを変更する方法 - at backyard
アプリを開発モードで起動する
プロジェクトのフォルダに移動してコマンドを実行します。
wails dev
開発モードの実行ファイルが作成されて起動します。
アプリをビルド(コンパイル)する
プロジェクトのフォルダに移動してコマンドを実行します。
wails build
配布するための実行ファイルが、プロジェクトのフォルダ内の build/bin
に作成されます。
VS Code で開発する
上記のコードをエディタで編集してコマンド実行すればアプリが作成できますが、コードの編集やデバッグは IDE(統合開発環境)を使いたいものです。
VS Code (Visual Studio Code) を使うことにします。
VS Code をインストールする
VS Code をダウンロードしてインストールします。
Download Visual Studio Code - Mac, Linux, Windows
拡張機能をインストールする
Go アプリをデバッグするための拡張機能をインストールします。
Visual Studio Code for Go 開発のインストールと構成 - Go on Azure | Microsoft Learn
プロジェクトのフォルダを開く
作成したプロジェクトのフォルダを VS Code で開きます。
VSCodeの基本~プロジェクトフォルダの設定方法について解説します! | プログラミング入門ナビ by Proglus(プログラス)
launch.json を用意する①
効率よくデバッグできるように VS Code のデバッグ構成 launch.json
を用意します。
公式サイトで紹介されているものを使ってみます。統合開発環境 | Wails
{
version: "0.2.0",
configurations: [
{
"name": "Debug ●●●●",
"type": "go",
"request": "launch",
"mode": "exec",
"program": "build/bin/●●.exe",
"cwd": "${workspaceFolder}",
"preLaunchTask": "build",
"env": {}
}
]
}
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"command": "go",
"args": [
"build",
"-tags", "dev",
"-gcflags", "all=-N -l", "-o",
"build/bin/●●.exe"
]
"options": {
"cwd": "${workspaceFolder}"
},
}
]
}
●●.exe
は実際のプロジェクトに合わせて変更します。macOS の場合は ●●.app
になります。↑
この設定の準備して デバッグ実行
します。
バックエンドの Go のプログラムは、ステップ実行したり変数ウォッチできます。フロントエンドの JavaScript のプログラムは、だめでした。
launch.json を用意する②
フロントエンドの JavaScript のプログラムにアタッチしてデバッグできないものでしょうか。
Wails アプリのフロントエンドは WebView を使っています。これにデバッガがアタッチできないか調べていくと、以下の記事を見つけました。
(前略)
configurations: [
{
"name": "Debug ●● frontend",
"type": "msedge",
"request": "launch",
"runtimeExecutable": "build/bin/●●.exe",
"env": {
"Path": "%path%;build/bin/;"
},
"useWebView": true,
"port": 9222, // The port value is optional, and the default value is 9222.
"url": "file:///${workspaceFolder}/frontend/index.html",
"webRoot": "${workspaceFolder}/frontend"
}
(後略)
上記①と併せて、バックエンドとフロントエンドのどちらもデバッグ実行できるようになると思ったのですが、
・Windows 環境しか設定できない
・VS Code の設定だけでなく Windows でレジストリを変更しないといけない
なので、使うのは断念しました。
launch.json を用意する③
wails dev
して開発モードで起動すると、ブラウザからアプリを操作できるようにするウェブサーバを起動する。これにより任意のブラウザ拡張機能を利用できる、と書かれています。
これを使えば、フロントエンドのプログラムを VS Code でデバッグできるようです。
How to debug frontend through vite on vscode · wailsapp/wails · Discussion #2877 · GitHub
つまり、
①アプリを開発モードで起動する
②これにアタッチする。バックエンドのプログラムがデバッグできる
③開発モードのアプリが起動するウェブサーバにウェブブラウザを起動して開く
④これにアタッチする。フロントエンドのプログラムがデバッグできる
まず、wails dev
を VS Code から実行(①)できるように設定を用意します。
{
"version": "2.0.0",
"tasks": [
{
"label": "wails dev",
"type": "shell",
"command": "wails",
"args": [ "dev" ]
"options": {
"cwd": "${workspaceFolder}"
},
}
]
}
続いて、wails dev
で起動したアプリのバックエンドのプログラムにアタッチする(②)設定です。
(前略)
{
"name": "attach to backend",
"type": "go",
"request": "attach",
"mode": "local",
"processId": "●●-dev.exe",
},
proccessId
は、実行時点でプロセスを選択させるしかないと思っていましたが、プロセス名で OK でした。wails dev
実行して build/bin
フォルダに作成されるアプリの実行ファイルの名前を指定します。↑
続いて、上記のアプリがウェブサーバになるので、ウェブブラウザを起動してフロントエンドのプログラムを開きます(③)。既にアプリが起動してフロントエンドのプログラムの画面が開いていて、これを別にブラウザでフロントエンドの画面を開くので、スマートでありませんが仕方ありません。
(前略)
{
"name": "launch chrome",
"type": "chrome",
"request": "launch",
"url": "http://localhost:34115",
"runtimeArgs": [
--remote-debugging-port=9222,
],
},
VS Code からアタッチできるよう --remote-debugging-port
を指定します。↑
続いて、上記のウェブブラウザで実行しているフロントエンドのプログラムにアタッチする(④)設定です。
(前略)
{
"name": "attach to chrome",
"type": "chrome",
"request": "attach",
"port": 9222,
"webRoot": "${workspaceFolder}/frontend",
"sourceMaps": true,
},
これを順に実行すればいいのですが、面倒なので連続して実行できないか試してみました。
例えば、上記②の設定に "preLaunchTask": "wails dev"
を加えてみたのですが、wails dev
が終了して初めて②の設定の attach
が実行されるので、これはだめでした。
試行錯誤した結果、以下の設定で使っています。
{
"version": "0.2.0",
"configurations": [
{
"name": "Start Program",
"type": "node-terminal",
"request": "launch",
"command": "wails dev",
"cwd": "${workspaceFolder}",
},
{
"name": "attach to backend",
"type": "go",
"request": "attach",
"mode": "local",
"processId": "HelloWails-dev.exe",
"presentation": {
"hidden": true,
}
},
{
"name": "launch chrome",
"type": "chrome",
"request": "launch",
"url": "http://localhost:34115",
"runtimeArgs": [
"--remote-debugging-port=9222",
],
"presentation": {
"hidden": true,
}
},
{
"name": "attach to chrome",
"type": "chrome",
"request": "attach",
"port": 9222,
"webRoot": "${workspaceFolder}/frontend",
"sourceMaps": true,
"presentation": {
"hidden": true,
}
},
],
"compounds": [
{
"name": "Debug Program",
"configurations": [
"attach to backend",
"launch chrome",
"attach to chrome",
],
"stopAll": true,
},
],
}
tasks.json
でなく launch.json
に wails dev
実行の設定しています。↑
VS Code の「実行とデバッグ」のメニューに Start Program
と Debug Program
が表示されます。ビルドして実行してテストするなら Start Program
を実行します。実行されたアプリにアタッチしてデバッグするとき Debug Program
を実行します。