2024年3月29日金曜日

ttyd の main 関数を Rust プログラムから呼び出す(Rust から C の関数を呼び出す)

開発環境の準備

開発用コンテナの起動

docker run -it --rm --workdir /work -p 0.0.0.0:7681:7681 rust:1.77.0-slim-bookworm bash
WORKDIR=/work

ttyd のソースコード取得

git インストール。

apt update
apt install -y git

ttyd のリポジトリを clone.

cd $WORKDIR
git clone --depth 1 -b 1.7.5 https://github.com/tsl0922/ttyd

プロジェクトディレクトリへ移動。

cd $WORKDIR/ttyd

ttyd をライブラリとしてビルド

ttyd の README を参照しながら、ビルドに必要なパッケージをインストールする。

apt install -y build-essential cmake git libjson-c-dev libwebsockets-dev

前回の apt install と重複があるが、気にせず ttyd の README からそのままコピペ。

CMakeLists.txt を修正

実行バイナリではなく、 .a を生成するように CMakeLists.txt を修正する。

sed -i -e 's/add_executable(${PROJECT_NAME} ${SOURCE_FILES})/add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES})/' ./CMakeLists.txt
  • add_executable: 実行バイナリを生成する
  • add_library: .a を生成する

関数 main の名前変更

別プログラムに組み込みたいので、エントリーポイントである main が存在すると困る。

なので、 mainttyd_main へリネームする。

sed -i -e 's/main(/ttyd_main(/' ./src/server.c

別プログラムから、 ttyd_main(2, ["ttyd", "-W", "bash"]) のような感じで呼び出すイメージ。

ttyd のビルド

mkdir build && cd build
cmake ..
make

これで、 libttyd.a が生成される。

ttyd を組み込む Rust プログラムを作る

ttydwrapper というプロジェクトを作って、 固定値で ttyd -W -t enableSixel=true bash コマンドを実行するのと同じように ttyd を呼び出すプログラムを作る。

bindgen に必要なパッケージのインストール

C 言語との FFI をするにあたり、 bindgen というコマンドを利用するので、それに必要なパッケージをインストールする。

apt update
apt install -y libclang-16-dev

Cargo プロジェクトの作成

今回は、 ttydwrapper という名前で作成。

cd $WORKDIR
cargo new ttydwrapper
cd ttydwrapper
rustup component add rustfmt

FFI 用のヘッダーファイルを作成

Rust から C の ttyd_main を呼び出すので、 ttyd_main の定義が記載されているヘッダーファイルを作成する。

Rust では、一般的に、 binding.h に必要なヘッダーファイルを列挙するらしい。

なので今回は、 ttyd.httyd_main を定義し、そのヘッダーファイルを binding.h へ記載する構成でヘッダーを作成した。

mkdir include
cat << EOF > ./include/ttyd.h
void ttyd_main(int argc, char **argv);
EOF

cat << EOF > ./include/binding.h
#include "ttyd.h"
EOF

バインディングを生成

bindgen コマンドで、ヘッダーから Rust のコードを生成する。

cargo install bindgen-cli
bindgen ./include/binding.h > ./src/bindings.rs

以下のような、 binding.rs が生成される。

/* automatically generated by rust-bindgen 0.69.4 */

extern "C" {
    pub fn ttyd_main(argc: ::std::os::raw::c_int, argv: *mut *mut ::std::os::raw::c_char);
}

生成された binding.rslib.rs からインクルードさせるのが一般的らしいので libs.rs を作る。

cat << EOF > ./src/lib.rs
include!("bindings.rs");
EOF

ライブラリをコピー

ライブラリは lib 以下に無いとダメなので .a を移動する。

mkdir $WORKDIR/ttydwrapper/lib
cp $WORKDIR/ttyd/build/libttyd.a $WORKDIR/ttydwrapper/lib/

ライブラリの情報を作成

build.rs にビルド時に必要なライブラリ情報の列挙をする。

cat << EOF > ./build.rs
fn main() {
    println!("cargo:rustc-link-search=native=$WORKDIR/ttydwrapper/lib");
    println!("cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu");
    println!("cargo:rustc-link-lib=static=ttyd");
    println!("cargo:rustc-link-lib=dylib=uv");
    println!("cargo:rustc-link-lib=dylib=websockets");
    println!("cargo:rustc-link-lib=static=ssl");
    println!("cargo:rustc-link-lib=static=crypto");
    println!("cargo:rustc-link-lib=static=z");
    println!("cargo:rustc-link-lib=static=json-c");
}
EOF

ビルド時に build.rs の設定を使用するように修正

Cargo.toml[package] の末尾に build = "build.rs" を追加する。

(sed で実現できなかったので vim で編集した)

main 関数の作成

Rust の main 関数を作成し、 ttyd_main を呼び出すプログラムを作る。

cat << EOF > ./src/main.rs
use std::ffi::CString;
use std::os::raw::c_char;
use ttydwrapper::ttyd_main;

fn main() {

    let args = vec!["tty", "-W", "-t", "enableSixel=true", "bash"];

    let c_args: Vec<CString> = args.into_iter()
        .map(|arg| CString::new(arg).expect("CString::new failed"))
        .collect();

    let mut argv: Vec<*mut c_char> = c_args.iter()
        .map(|arg| arg.as_ptr() as *mut c_char)
        .collect();

    let argv_ptr: *mut *mut c_char = argv.as_mut_ptr();

    unsafe {
        ttyd_main(5, argv_ptr);
    }
}
EOF

ビルド

cargo build

これで ./target/debug/ttydwrapper が生成される。

動作確認

コンテナ起動時に 7681 をポートフォワーディングしているので、 ttydwrapper を実行した後 http://localhost:7681 へアクセスすると ttyd のターミナルが開く。

以上。

ここまで書いたところで、本当にやりたいのは Windows でだった事に気付いた…。

参考資料

2024年3月23日土曜日

Windows コンテナで Node.js アプリケーションをシングルバイナリにする

今回は、 Node.js アプリケーションのシングルバイナリ化をやってみる。

対象のアプリケーションは devcontainers/cli

nexe/nexeNode.js v21 の Single executable applications の 2 パターンでやってみる。

前提

  • OS: Windows 11 Pro 23H2 ビルド 22631.3155
  • Docker Desktop: Version 4.28.0 (139021)
  • 使用イメージ: mcr.microsoft.com/windows/servercore:ltsc2022

コンテナ起動

Node.js のビルドにはたくさんメモリが必要なので、 -m で明示的に指定。

docker run -it --rm --cpus 20 -m 16G -v "$(pwd):C:\dist" mcr.microsoft.com/windows/servercore:ltsc2022 powershell.exe

ビルド環境構築

開発ツール一式をインストール

Set-ExecutionPolicy Unrestricted -Force
iex ((New-Object System.Net.WebClient).DownloadString('https://boxstarter.org/bootstrapper.ps1'))
get-boxstarter -Force
Install-BoxstarterPackage https://raw.githubusercontent.com/nodejs/node/HEAD/tools/bootstrap/windows_boxstarter -DisableReboots
refreshenv

Visual Studio に必要なコンポーネントを追加

& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\setup.exe' modify `
    --installPath 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools' `
    --add Microsoft.VisualStudio.Component.Windows11SDK.22621 `
    --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 `
    --add Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre `
    --quiet

Node.js のインストール

choco install nodejs

パス環境変数の更新

$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")

devcontainers/cli をシングルバイナリにしてみる

nexe/nexe で作る

git clone --depth 1 -b v0.58.0 https://github.com/devcontainers/cli devcontainers-cli
cd devcontainers-cli
npm i -g yarn
yarn
yarn compile-prod
npx nexe -i ./devcontainer.js -t windows-x64-21.7.1 -b -o /dist/devcontainer-windows-x64-21.7.1-nexe.exe

~\.nexe\21.7.1\out\Release\node.exe に nexe 向けの node.exe が配置される。 これを ~\.nexe\windows-x64-21.7.1 に移動し、 -b を外すことで、次回以降、このバイナリを利用してシングルバイナリを作るようになる。

mv ~\.nexe\21.7.1\out\Release\node.exe ~\.nexe\windows-x64-21.7.1

Node.js の Single executable applications で作る

# シングルバイナリに使うための node 実行ファイルをコピー
Copy-Item 'C:\Program Files\nodejs\node.exe' .\devcontainer-windows-x64-21.7.1-sea.exe

# sea 用の設定ファイル作成
Write-Output '{"main": "./dist/spec-node/devContainersSpecCLI.js", "output": "sea-prep.blob"}' | Out-File -Encoding ascii .\sea-config.json

# blob の作成
node --experimental-sea-config sea-config.json

# BLOB を注入し、シングルバイナリを作る
npx postject devcontainer-windows-x64-21.7.1-sea.exe NODE_SEA_BLOB sea-prep.blob `
    --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2

# コンテナの外にコピー
Copy-Item .\devcontainer-windows-x64-21.7.1-sea.exe \dist

参考資料

変更履歴

日付 内容
2024/3/23 新規作成
2024/4/2 nexe 用 node バイナリ格納場所の間違いを修正(~/nexe -> ~/.nexe)

2024年3月22日金曜日

Vim に Ex コマンドを追加してビルドする

追加するために修正が必要な箇所を把握するのが目的なので、 echo をそのまま呼び出す myecho コマンドを追加する。

前提

  • OS: Windows 11 Pro 23H2 ビルド 22631.3155
  • Docker Desktop: Version 4.28.0 (139021)
  • 使用イメージ: debian:bookworm-slim
  • ビルドする Vim のバージョン: v9.1.0196

ドキュメントの追加・更新はスコープ外とする。

コンテナ起動

docker run -it --rm debian:bookworm-slim

Vim をビルドできる環境を整える

必要なパッケージのインストール

apt update
apt install -y build-essential libncurses-dev vim git

※ Vim は、 Ex コマンド追加後のコマンドのインデックス作り直し処理で必要になる

作業用ディレクトリ作成

mkdir ~/work
cd ~/work

Vim のソースコード入手

v9.1.0196 の最新のコミットのみをダウンロード。

git clone -b v9.1.0196 --depth 1 https://github.com/vim/vim.git
cd vim

ビルド・実行

./configure
make -j16
src/vim

vim が起動した。 OK.

Ex コマンドの追加

Ex コマンドの実装ファイルを作成

引数をそのまま既存関数 ex_echo へ丸投げする ex_myecho 関数を作る。

src/myecho.c:

#include "vim.h"

void
ex_myecho(exarg_T *eap)
{
  ex_echo(eap);
}

src/proto/myecho.pro:

/* myecho.c */
void ex_myecho(exarg_T *eap);
/* vim: set ft=c : */

作成した Ex コマンドを Vim に組み込む

src/proto.h へ追加

19 行目から 123 行目までの # include の並びに、 回作成した myecho.pro を追加する。

# include "myecho.pro"

src/ex_cmds.h へ Ex コマンドを追加

117 行目からの EXCMD マクロの並びに、今回追加するコマンドを追加する。

今回は末尾に追加した。

EXCMD(CMD_myecho,   "myecho",       ex_myecho,
    EX_EXTRA|EX_NOTRLCOM|EX_EXPR_ARG|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
    ADDR_NONE),

フラグの意味は、このヘッダーファイルの先頭に記載されている。

今回は、 echo のモノをそのままコピペ。

Ex コマンドインデックスの更新

src ディレクトリへ移動し、 make cmdidxs コマンドを実行する。

以下のように、 Ex コマンドのインデックスが更新される。

diff --git a/src/ex_cmdidxs.h b/src/ex_cmdidxs.h
index 658f05e..26031d8 100644
--- a/src/ex_cmdidxs.h
+++ b/src/ex_cmdidxs.h
@@ -53,7 +53,7 @@ static const unsigned char cmdidxs2[26][26] =
   /* j */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  0,  0,  0,  0,  0 },
   /* k */ {  0,  0,  0,  0,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
   /* l */ {  3, 11, 15, 19, 20, 25, 28, 33,  0,  0,  0, 35, 38, 41, 45, 51,  0, 53, 62, 54, 55, 59, 61,  0,  0,  0 },
-  /* m */ {  1,  0,  0,  0,  7,  0,  0,  0,  0,  0, 10,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 16 },
+  /* m */ {  1,  0,  0,  0,  7,  0,  0,  0,  0,  0, 10,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,288, 16 },
   /* n */ {  0,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  5,  8, 10,  0,  0,  0,  0,  0, 17,  0,  0,  0,  0,  0 },
   /* o */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  2,  5,  0,  0,  0,  0,  0,  0,  9,  0, 11,  0,  0,  0 },
   /* p */ {  1,  0,  3,  0,  4,  0,  0,  0,  0,  0,  0,  0,  0,  0,  7,  9,  0,  0, 16, 17, 26,  0, 28,  0, 29,  0 },
@@ -69,4 +69,4 @@ static const unsigned char cmdidxs2[26][26] =
   /* z */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 }
 };

-static const int command_count = 594;
+static const int command_count = 595;

ビルドスクリプトの更新

以下ビルドスクリプトに、 myecho.cmyecho.o(または myecho.obj) を追加する。

move. で検索かけて、ヤンク・ペースト、そのご myhello. に書き換える感じ。

  • src/Make_ami.mak
  • src/Make_cyg_ming.mak
  • src/Make_mvc.mak
  • src/Make_vms.mms
  • src/Makefile

ビルド

make distclean
./configure
make -j16

これで、 src/vim に出力されたバイナリを実行し、 :myecho "abcd" とすると、 abcd がエコーされる。

以上。

最終的な差分

差分.patch
diff --git a/src/Make_ami.mak b/src/Make_ami.mak
index 9e9ebe3..d99501a 100644
--- a/src/Make_ami.mak
+++ b/src/Make_ami.mak
@@ -138,6 +138,7 @@ SRC += \
        misc2.c \
        mouse.c \
        move.c \
+       myecho.c \
        normal.c \
        ops.c \
        option.c \
diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak
index 7afb6e0..b64eb93 100644
--- a/src/Make_cyg_ming.mak
+++ b/src/Make_cyg_ming.mak
@@ -821,6 +821,7 @@ OBJ = \
        $(OUTDIR)/misc2.o \
        $(OUTDIR)/mouse.o \
        $(OUTDIR)/move.o \
+       $(OUTDIR)/myecho.o \
        $(OUTDIR)/mbyte.o \
        $(OUTDIR)/normal.o \
        $(OUTDIR)/ops.o \
diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak
index 4db2298..0aacd2b 100644
--- a/src/Make_mvc.mak
+++ b/src/Make_mvc.mak
@@ -698,6 +698,7 @@ OBJ = \
        $(OUTDIR)\misc2.obj \
        $(OUTDIR)\mouse.obj \
        $(OUTDIR)\move.obj \
+       $(OUTDIR)\myecho.obj \
        $(OUTDIR)\normal.obj \
        $(OUTDIR)\ops.obj \
        $(OUTDIR)\option.obj \
@@ -1622,6 +1623,8 @@ $(OUTDIR)/mouse.obj:      $(OUTDIR) mouse.c  $(INCL)

 $(OUTDIR)/move.obj:    $(OUTDIR) move.c  $(INCL)

+$(OUTDIR)/myecho.obj:  $(OUTDIR) myecho.c  $(INCL)
+
 $(OUTDIR)/mbyte.obj:   $(OUTDIR) mbyte.c  $(INCL)

 $(OUTDIR)/netbeans.obj:        $(OUTDIR) netbeans.c $(NBDEBUG_SRC) $(INCL) version.h
@@ -1889,6 +1892,7 @@ proto.h: \
        proto/misc2.pro \
        proto/mouse.pro \
        proto/move.pro \
+       proto/myecho.pro \
        proto/mbyte.pro \
        proto/normal.pro \
        proto/ops.pro \
diff --git a/src/Make_vms.mms b/src/Make_vms.mms
index f050c9d..b15d7b0 100644
--- a/src/Make_vms.mms
+++ b/src/Make_vms.mms
@@ -396,6 +396,7 @@ SRC = \
        misc2.c \
        mouse.c \
        move.c \
+       myecho.c \
        normal.c \
        ops.c \
        option.c \
@@ -528,6 +529,7 @@ OBJ = \
        misc2.obj \
        mouse.obj \
        move.obj \
+       myecho.obj \
        normal.obj \
        ops.obj \
        option.obj \
@@ -1033,6 +1035,9 @@ mouse.obj : mouse.c vim.h [.auto]config.h feature.h os_unix.h   \
 move.obj : move.c vim.h [.auto]config.h feature.h os_unix.h   \
  ascii.h keymap.h termdefs.h macros.h structs.h regexp.h gui.h beval.h \
  [.proto]gui_beval.pro option.h ex_cmds.h proto.h errors.h globals.h
+myecho.obj : myecho.c vim.h [.auto]config.h feature.h os_unix.h   \
+ ascii.h keymap.h termdefs.h macros.h structs.h regexp.h gui.h beval.h \
+ [.proto]gui_beval.pro option.h ex_cmds.h proto.h errors.h globals.h
 mbyte.obj : mbyte.c vim.h [.auto]config.h feature.h os_unix.h   \
  ascii.h keymap.h termdefs.h macros.h structs.h regexp.h gui.h beval.h \
  [.proto]gui_beval.pro option.h ex_cmds.h proto.h errors.h globals.h
diff --git a/src/Makefile b/src/Makefile
index 33903d3..18a714e 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1546,6 +1546,7 @@ BASIC_SRC = \
        misc2.c \
        mouse.c \
        move.c \
+       myecho.c \
        normal.c \
        ops.c \
        option.c \
@@ -1704,6 +1705,7 @@ OBJ_COMMON = \
        objects/misc2.o \
        objects/mouse.o \
        objects/move.o \
+       objects/myecho.o \
        objects/normal.o \
        objects/ops.o \
        objects/option.o \
@@ -1893,6 +1895,7 @@ PRO_AUTO = \
        misc2.pro \
        mouse.pro \
        move.pro \
+       myecho.pro \
        netbeans.pro \
        normal.pro \
        ops.pro \
@@ -3374,6 +3377,9 @@ objects/mouse.o: mouse.c
 objects/move.o: move.c
        $(CCC) -o $@ move.c

+objects/myecho.o: myecho.c
+       $(CCC) -o $@ myecho.c
+
 objects/mbyte.o: mbyte.c
        $(CCC) -o $@ mbyte.c

@@ -3999,6 +4005,11 @@ objects/move.o: move.c vim.h protodef.h auto/config.h feature.h os_unix.h \
  proto/gui_beval.pro structs.h regexp.h gui.h libvterm/include/vterm.h \
  libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \
  globals.h errors.h
+objects/myecho.o: myecho.c vim.h protodef.h auto/config.h feature.h os_unix.h \
+ auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h beval.h \
+ proto/gui_beval.pro structs.h regexp.h gui.h libvterm/include/vterm.h \
+ libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \
+ globals.h errors.h
 objects/normal.o: normal.c vim.h protodef.h auto/config.h feature.h os_unix.h \
  auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h beval.h \
  proto/gui_beval.pro structs.h regexp.h gui.h libvterm/include/vterm.h \
diff --git a/src/ex_cmdidxs.h b/src/ex_cmdidxs.h
index 658f05e..26031d8 100644
--- a/src/ex_cmdidxs.h
+++ b/src/ex_cmdidxs.h
@@ -53,7 +53,7 @@ static const unsigned char cmdidxs2[26][26] =
   /* j */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  0,  0,  0,  0,  0 },
   /* k */ {  0,  0,  0,  0,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
   /* l */ {  3, 11, 15, 19, 20, 25, 28, 33,  0,  0,  0, 35, 38, 41, 45, 51,  0, 53, 62, 54, 55, 59, 61,  0,  0,  0 },
-  /* m */ {  1,  0,  0,  0,  7,  0,  0,  0,  0,  0, 10,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 16 },
+  /* m */ {  1,  0,  0,  0,  7,  0,  0,  0,  0,  0, 10,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,288, 16 },
   /* n */ {  0,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  5,  8, 10,  0,  0,  0,  0,  0, 17,  0,  0,  0,  0,  0 },
   /* o */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  2,  5,  0,  0,  0,  0,  0,  0,  9,  0, 11,  0,  0,  0 },
   /* p */ {  1,  0,  3,  0,  4,  0,  0,  0,  0,  0,  0,  0,  0,  0,  7,  9,  0,  0, 16, 17, 26,  0, 28,  0, 29,  0 },
@@ -69,4 +69,4 @@ static const unsigned char cmdidxs2[26][26] =
   /* z */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 }
 };

-static const int command_count = 594;
+static const int command_count = 595;
diff --git a/src/ex_cmds.h b/src/ex_cmds.h
index bd26e81..0f9c1dc 100644
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -1904,6 +1904,10 @@ EXCMD(CMD_decrement,     "--",           ex_incdec,
        EX_EXTRA|EX_TRLBAR|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
        ADDR_NONE),

+EXCMD(CMD_myecho,              "myecho",               ex_myecho,
+       EX_EXTRA|EX_NOTRLCOM|EX_EXPR_ARG|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
+       ADDR_NONE),
+
 #undef EXCMD

 #ifndef DO_DECLARE_EXCMD
diff --git a/src/proto.h b/src/proto.h
index 50802ce..656e210 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -121,6 +121,7 @@ extern int _stricoll(char *a, char *b);
 # ifdef FEAT_VIMINFO
 #  include "viminfo.pro"
 # endif
+# include "myecho.pro"

 // These prototypes cannot be produced automatically.
 int smsg(const char *, ...) ATTRIBUTE_COLD ATTRIBUTE_FORMAT_PRINTF(1, 2);

参考資料

2024年3月20日水曜日

Visual Studio のコンポーネントを CLI でインストールする

TL;DR

setup.exe を使って setup.exe modify --installPath <対象バージョンのインストールパス> --add <コンポーネントID> --quit

例えば、 Visual Studio BuildTools に Windows11 SDK と MSVC C++ Build Tools をインストールしたい場合は、以下のコマンドを実行する。

& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\setup.exe' modify `
    --installPath 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools' `
    --add Microsoft.VisualStudio.Component.Windows11SDK.22621 `
    --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 `
    --quiet

コンポーネントID は以下サイトにて確認できる。

Windows サンドボックスで実践してみる

TL;DR の例を実行してみる。

前提

winget のインストール

今回は、 Visual Studio BuildTools を winget でインストールするので、 Windows サンドボックスで利用可能にする手順を実施する。

$progressPreference = 'silentlyContinue'
Write-Information "Downloading WinGet and its dependencies..."
Invoke-WebRequest -Uri https://aka.ms/getwinget -OutFile Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle
Invoke-WebRequest -Uri https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx -OutFile Microsoft.VCLibs.x64.14.00.Desktop.appx
Invoke-WebRequest -Uri https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.6/Microsoft.UI.Xaml.2.8.x64.appx -OutFile Microsoft.UI.Xaml.2.8.x64.appx
Add-AppxPackage Microsoft.VCLibs.x64.14.00.Desktop.appx
Add-AppxPackage Microsoft.UI.Xaml.2.8.x64.appx
Add-AppxPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle

Visual Studio BuildTools のインストール

winget install --silent --accept-source-agreements Microsoft.VisualStudio.2022.BuildTools

これで、 VisualStudio BuildTools と Visual Studio Installre がそれぞれ下記に配置される。

  • Visual Studio BuildTools: C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools
  • Visual Studio Installer: C:\Program Files (x86)\Microsoft Visual Studio\Installer\setup.exe

コンポーネントのインストール

本題。TL;DR に記載のコマンドを実行する。

& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\setup.exe' modify `
    --installPath 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools' `
    --add Microsoft.VisualStudio.Component.Windows11SDK.22621 `
    --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 `
    --quiet

これで MSVC C++ Build ToolsWindows 11 SDK のインストールが完了する。

インストールと同時にコンポーネントをインストールする

今回の例だと、「未インストールの状態からコンポーネントのインストールまでを実施する」なので、 実は、「インストールと同時にコンポーネントのインストール」もできる。

winget のインストールコマンドを以下のように変更する。

winget install --silent --accept-source-agreements `
    Microsoft.VisualStudio.2022.BuildTools `
    --override " `
        --add Microsoft.VisualStudio.Component.Windows11SDK.22621 `
        --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 `
        --quiet"

参考資料

2024年3月19日火曜日

zig cc と musl を使って Vim を静的リンクする

開発環境の起動

今回は debian:bookworm-slim を使う。

docker run -it --rm debian:bookworm-slim

必要パッケージのインストール

apt update
apt install -y curl xz-utils make git file
  • curl: zig, ncurses のダウンロードのために使う
  • xz-utils: zig の展開のために使う
  • make: ncurses のビルドのために使う
  • git: Vim のソースコード取得のために使う
  • file: Vim の実行バイナリの形式確認に使う

作業用ディレクトリの作成

mkdir ~/work
cd ~/work

zig のインストール

zig 公式が配布しているバイナリを取得し、 /opt に配置する。

curl -L https://ziglang.org/download/0.11.0/zig-linux-x86_64-0.11.0.tar.xz -O
tar xf zig-linux-x86_64-0.11.0.tar.xz -C /opt
export PATH=$PATH:/opt/zig-linux-x86_64-0.11.0

ncurses のビルド

静的ビルドのためには静的ライブラリとしてビルドした ncurses が必要なので、ソースからビルドする。

# ダウンロード・展開
curl -LO https://invisible-island.net/datafiles/release/ncurses.tar.gz
tar xfv ncurses.tar.gz
cd ncurses-6.3/

# ビルド
./configure \
    CC="zig cc -target x86_64-linux-musl" \
    AR="zig ar" \
    --enable-widec
make -j16

これで、 ncurses-6.3/liblibncursesw.a が作成される。

vim のビルド

ダウンロード

cd ~/work
git clone --depth 1 https://github.com/vim/vim
cd vim

ビルド

./configure \
    CC="zig cc -target x86_64-linux-musl" \
    LDFLAGS="-L$(cd ~; pwd)/work/ncurses-6.3/lib -static -s -w" \
    --with-features=huge \
    --enable-terminal \
    --enable-gpm=no \
    --enable-fail-if-missing \
    --prefix /vim \
    --with-tlib=ncursesw

make -j16
make install

これで、 /vim/vim に Vim の実行ファイルが配置される。

静的リンクの確認

root@88773c67d701:~/work/vim# file /vim/bin/vim
/vim/bin/vim: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
root@88773c67d701:~/work/vim# ldd /vim/bin/vim
        not a dynamic executable

OK.

パッケージング

vim 実行時に検索される VIM ディレクトリは、--prefix で指定したパスを基に検索されるため、このままだとポータブルにならない。 (今回の場合だと /vim/share/vim となる)

そのため、 VIM 環境変数を上書きするシェルスクリプトを用意し、それ経由で実行してもらう事とする。

cat << "EOF" > /vim/vim
#!/bin/sh
CURRENT_DIR="$(dirname "$(realpath "$0")")"
export VIM="${CURRENT_DIR}/share/vim/"
exec "${CURRENT_DIR}/bin/vim" "$@"
EOF
chmod a+x /vim/vim
tar zcfv ./vim.tar.gz /vim

docker cp でホストに持ってきて、適当な場所に配置し、 vim/vim を実行すれば Vim が起動する。 ※vim/bin/vim ではなく

Windows で Node.js をビルドする

Windows サンドボックスの起動

メイン PC に色々入れたくないので、今回は Windows サンドボックスで試す。

NodeBuildEnv.wsb を作成し、ダブルクリックで起動する。

NodeBuildEnv.wsb

<Configuration>
  <MemoryInMB>16384</MemoryInMB>
  <ClipboardRedirection>Enable</ClipboardRedirection>
  <VGpu>Disable</VGpu>
  <MappedFolders>
    <MappedFolder>
      <HostFolder>C:\Users\Public\Downloads</HostFolder>
      <SandboxFolder>C:\Users\WDAGUtilityAccount\Downloads</SandboxFolder>
      <ReadOnly>false</ReadOnly>
    </MappedFolder>
  </MappedFolders>
</Configuration>

Node.js のビルド環境構築

PowerShell を開発者権限で起動し、以下コマンドを実行。

Set-ExecutionPolicy Unrestricted -Force
iex ((New-Object System.Net.WebClient).DownloadString('https://boxstarter.org/bootstrapper.ps1'))
get-boxstarter -Force
Install-BoxstarterPackage https://raw.githubusercontent.com/nodejs/node/HEAD/tools/bootstrap/windows_boxstarter -DisableReboots
refreshenv

これだけでは、 Visual Studio のコンポーネント(ワークロード)のインストールまではしてくれないため、以下コマンドでビルドに必要なコンポーネントをインストールする。

& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\setup.exe' modify `
    --installPath 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools' `
    --add Microsoft.VisualStudio.Component.Windows11SDK.22621 `
    --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 `
    --quiet

これで MSVC C++ Build ToolsWindows 11 SDK のインストールがされる。

Node.js のビルド

コードのクローン

git clone --depth=1 https://github.com/nodejs/node.git
cd node

ビルド

Windows 用にビルド用 bat ファイルがあるのでそれを叩く。

.\vcbuild.bat

ビルドに成功すると ./out/Release にビルド結果が出力されるので、 wsb ファイルで指定したディレクトリにコピーすれば受け渡せる。

Copy-Item -Recurse .\out\Release C:\Users\WDAGUtilityAccount\Downloads\

参考資料