node-canvasのビルド時に遭遇したエラー

# はじめに
こんにちは、株式会社コズム開発部の穴澤です。

今回は、Reactのライブラリである、React-Konvaを使用した際にnode-canvasを利用する必要があり、そのビルド時にいくつかエラーに遭遇したのでそのエラー内容と対処方法をまとめました。

## TL;DR

:::note
対象者：
　　ARM64アーキテクチャのPCで開発している方
ユースケース：
　　node-canvasを利用するライブラリを使用するシーン
TODO：
　　CLI上で以下のコマンドを実行

```bash
brew install pkg-config cairo pango libpng jpeg giflib librsvg pixman
```
:::

## node-canvasとは

node-canvasは2Dグラフィックスライブラリである[cairo](https://www.cairographics.org/)を用いてNode.jsに実装されているCanvasです。

## 使用したライブラリ

[canvas@npm v2.11.2](https://www.npmjs.com/package/canvas)

# 問題

canvasというnode moduleが見つからないため、インストールしたところビルドに失敗しました。

```bash
yarn add canvas
 
➤ YN0000: · Yarn 4.1.1
➤ YN0000: ┌ Link step
➤ YN0008: │ canvas@npm:2.11.2 must be rebuilt because its dependency tree changed
➤ YN0009: │ canvas@npm:2.11.2 couldn't be built successfully (exit code 1, logs can be found here: /private/var/folders/y0/6l7j3fpj1916jkpgl82nhm7w0000gn/T/xfs-452bc208/build.log)
➤ YN0000: └ Completed in 10s 23ms
➤ YN0000: · Failed with errors in 10s 864ms
```

# 解決手順

## 1. エラーログを見てみる

<details> <summary> エラーログ </summary>
    
    ```bash
    # This file contains the result of Yarn building a package (canvas@npm:2.11.2)
    # Script name: install
    
    node-pre-gyp info it worked if it ends with ok
    node-pre-gyp info using node-pre-gyp@1.0.11
    node-pre-gyp info using node@21.7.2 | darwin | arm64
    (node:3201) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
    (Use `node --trace-deprecation ...` to show where the warning was created)
    node-pre-gyp http GET https://github.com/Automattic/node-canvas/releases/download/v2.11.2/canvas-v2.11.2-node-v120-darwin-unknown-arm64.tar.gz
    node-pre-gyp ERR! install response status 404 Not Found on https://github.com/Automattic/node-canvas/releases/download/v2.11.2/canvas-v2.11.2-node-v120-darwin-unknown-arm64.tar.gz 
    node-pre-gyp WARN Pre-built binaries not installable for canvas@2.11.2 and node@21.7.2 (node-v120 ABI, unknown) (falling back to source compile with node-gyp) 
    node-pre-gyp WARN Hit error response status 404 Not Found on https://github.com/Automattic/node-canvas/releases/download/v2.11.2/canvas-v2.11.2-node-v120-darwin-unknown-arm64.tar.gz 
    gyp info it worked if it ends with ok
    gyp info using node-gyp@10.1.0
    gyp info using node@21.7.2 | darwin | arm64
    gyp info ok 
    gyp info it worked if it ends with ok
    gyp info using node-gyp@10.1.0
    gyp info using node@21.7.2 | darwin | arm64
    gyp info find Python using Python version 3.9.6 found at "/Applications/Xcode.app/Contents/Developer/usr/bin/python3"
    
    gyp info spawn /Applications/Xcode.app/Contents/Developer/usr/bin/python3
    gyp info spawn args [
    gyp info spawn args '/プロジェクトのパス/node_modules/node-gyp/gyp/gyp_main.py',
    gyp info spawn args 'binding.gyp',
    gyp info spawn args '-f',
    gyp info spawn args 'make',
    gyp info spawn args '-I',
    gyp info spawn args '/プロジェクトのパス/node_modules/canvas/build/config.gypi',
    gyp info spawn args '-I',
    gyp info spawn args '/プロジェクトのパス/node_modules/node-gyp/addon.gypi',
    gyp info spawn args '-I',
    gyp info spawn args '/Users/ユーザー名/Library/Caches/node-gyp/21.7.2/include/node/common.gypi',
    gyp info spawn args '-Dlibrary=shared_library',
    gyp info spawn args '-Dvisibility=default',
    gyp info spawn args '-Dnode_root_dir=/Users/ユーザー名/Library/Caches/node-gyp/21.7.2',
    gyp info spawn args '-Dnode_gyp_dir=/プロジェクトのパス/node_modules/node-gyp',
    gyp info spawn args '-Dnode_lib_file=/Users/ユーザー名/Library/Caches/node-gyp/21.7.2/<(target_arch)/node.lib',
    gyp info spawn args '-Dmodule_root_dir=/プロジェクトのパス/node_modules/canvas',
    gyp info spawn args '-Dnode_engine=v8',
    gyp info spawn args '--depth=.',
    gyp info spawn args '--no-parallel',
    gyp info spawn args '--generator-output',
    gyp info spawn args 'build',
    gyp info spawn args '-Goutput_dir=.'
    gyp info spawn args ]
    /bin/sh: pkg-config: command not found
    gyp: Call to 'pkg-config pixman-1 --libs' returned exit status 127 while in binding.gyp. while trying to load binding.gyp
    gyp ERR! configure error 
    gyp ERR! stack Error: `gyp` failed with exit code: 1
    gyp ERR! stack at ChildProcess.<anonymous> (/プロジェクトのパス/node_modules/node-gyp/lib/configure.js:297:18)
    gyp ERR! stack at ChildProcess.emit (node:events:519:28)
    gyp ERR! stack at ChildProcess._handle.onexit (node:internal/child_process:294:12)
    gyp ERR! System Darwin 23.4.0
    gyp ERR! command "/opt/homebrew/Cellar/node/21.7.2/bin/node" "/プロジェクトのパス/node_modules/node-gyp/bin/node-gyp.js" "configure" "--fallback-to-build" "--update-binary" "--module=/プロジェクトのパス/node_modules/canvas/build/Release/canvas.node" "--module_name=canvas" "--module_path=/プロジェクトのパス/node_modules/canvas/build/Release" "--napi_version=9" "--node_abi_napi=napi" "--napi_build_version=0" "--node_napi_label=node-v120"
    gyp ERR! cwd /プロジェクトのパス/node_modules/canvas
    gyp ERR! node -v v21.7.2
    gyp ERR! node-gyp -v v10.1.0
    gyp ERR! not ok 
    node-pre-gyp ERR! build error 
    node-pre-gyp ERR! stack Error: Failed to execute '/opt/homebrew/Cellar/node/21.7.2/bin/node /プロジェクトのパス/node_modules/node-gyp/bin/node-gyp.js configure --fallback-to-build --update-binary --module=/プロジェクトのパス/node_modules/canvas/build/Release/canvas.node --module_name=canvas --module_path=/プロジェクトのパス/node_modules/canvas/build/Release --napi_version=9 --node_abi_napi=napi --napi_build_version=0 --node_napi_label=node-v120' (1)
    node-pre-gyp ERR! stack     at ChildProcess.<anonymous> (/プロジェクトのパス/node_modules/@mapbox/node-pre-gyp/lib/util/compile.js:89:23)
    node-pre-gyp ERR! stack     at ChildProcess.emit (node:events:519:28)
    node-pre-gyp ERR! stack     at maybeClose (node:internal/child_process:1105:16)
    node-pre-gyp ERR! stack     at ChildProcess._handle.onexit (node:internal/child_process:305:5)
    node-pre-gyp ERR! System Darwin 23.4.0
    node-pre-gyp ERR! command "/opt/homebrew/Cellar/node/21.7.2/bin/node" "/プロジェクトのパス/node_modules/@mapbox/node-pre-gyp/bin/node-pre-gyp" "install" "--fallback-to-build" "--update-binary"
    node-pre-gyp ERR! cwd /プロジェクトのパス/node_modules/canvas
    node-pre-gyp ERR! node -v v21.7.2
    node-pre-gyp ERR! node-pre-gyp -v v1.0.11
    node-pre-gyp ERR! not ok 
    Failed to execute '/opt/homebrew/Cellar/node/21.7.2/bin/node /プロジェクトのパス/node_modules/node-gyp/bin/node-gyp.js configure --fallback-to-build --update-binary --module=/プロジェクトのパス/node_modules/canvas/build/Release/canvas.node --module_name=canvas --module_path=/プロジェクトのパス/node_modules/canvas/build/Release --napi_version=9 --node_abi_napi=napi --napi_build_version=0 --node_napi_label=node-v120' (1)
    ```
</details>

pixman-1が見つからないため呼び出しに失敗していることがわかりました。

pixmanと、念の為pkg-configもインストールします。

```bash
brew install pixman pkg-config
```

しかし、再度canvasをインストールしたものの、またしてもビルドに失敗しました。

## 2. 再度エラーログを見てみる

<details> <summary> エラーログ </summary>
    
    ```bash
    # This file contains the result of Yarn building a package (canvas@npm:2.11.2)
    # Script name: install
    
    node-pre-gyp info it worked if it ends with ok
    node-pre-gyp info using node-pre-gyp@1.0.11
    node-pre-gyp info using node@21.7.2 | darwin | arm64
    (node:4844) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
    (Use `node --trace-deprecation ...` to show where the warning was created)
    node-pre-gyp http GET https://github.com/Automattic/node-canvas/releases/download/v2.11.2/canvas-v2.11.2-node-v120-darwin-unknown-arm64.tar.gz
    node-pre-gyp ERR! install response status 404 Not Found on https://github.com/Automattic/node-canvas/releases/download/v2.11.2/canvas-v2.11.2-node-v120-darwin-unknown-arm64.tar.gz 
    node-pre-gyp WARN Pre-built binaries not installable for canvas@2.11.2 and node@21.7.2 (node-v120 ABI, unknown) (falling back to source compile with node-gyp) 
    node-pre-gyp WARN Hit error response status 404 Not Found on https://github.com/Automattic/node-canvas/releases/download/v2.11.2/canvas-v2.11.2-node-v120-darwin-unknown-arm64.tar.gz 
    gyp info it worked if it ends with ok
    gyp info using node-gyp@10.1.0
    gyp info using node@21.7.2 | darwin | arm64
    gyp info ok 
    gyp info it worked if it ends with ok
    gyp info using node-gyp@10.1.0
    gyp info using node@21.7.2 | darwin | arm64
    gyp info find Python using Python version 3.9.6 found at "/Applications/Xcode.app/Contents/Developer/usr/bin/python3"
    
    gyp info spawn /Applications/Xcode.app/Contents/Developer/usr/bin/python3
    gyp info spawn args [
    gyp info spawn args '/プロジェクトのパス/node_modules/node-gyp/gyp/gyp_main.py',
    gyp info spawn args 'binding.gyp',
    gyp info spawn args '-f',
    gyp info spawn args 'make',
    gyp info spawn args '-I',
    gyp info spawn args '/プロジェクトのパス/node_modules/canvas/build/config.gypi',
    gyp info spawn args '-I',
    gyp info spawn args '/プロジェクトのパス/node_modules/node-gyp/addon.gypi',
    gyp info spawn args '-I',
    gyp info spawn args '/Users/ユーザー名/Library/Caches/node-gyp/21.7.2/include/node/common.gypi',
    gyp info spawn args '-Dlibrary=shared_library',
    gyp info spawn args '-Dvisibility=default',
    gyp info spawn args '-Dnode_root_dir=/Users/ユーザー名/Library/Caches/node-gyp/21.7.2',
    gyp info spawn args '-Dnode_gyp_dir=/プロジェクトのパス/node_modules/node-gyp',
    gyp info spawn args '-Dnode_lib_file=/Users/ユーザー名/Library/Caches/node-gyp/21.7.2/<(target_arch)/node.lib',
    gyp info spawn args '-Dmodule_root_dir=/プロジェクトのパス/node_modules/canvas',
    gyp info spawn args '-Dnode_engine=v8',
    gyp info spawn args '--depth=.',
    gyp info spawn args '--no-parallel',
    gyp info spawn args '--generator-output',
    gyp info spawn args 'build',
    gyp info spawn args '-Goutput_dir=.'
    gyp info spawn args ]
    Package pangocairo was not found in the pkg-config search path.
    Perhaps you should add the directory containing `pangocairo.pc'
    to the PKG_CONFIG_PATH environment variable
    No package 'pangocairo' found
    gyp: Call to 'pkg-config pangocairo --libs' returned exit status 1 while in binding.gyp. while trying to load binding.gyp
    gyp ERR! configure error 
    gyp ERR! stack Error: `gyp` failed with exit code: 1
    gyp ERR! stack at ChildProcess.<anonymous> (/プロジェクトのパス/node_modules/node-gyp/lib/configure.js:297:18)
    gyp ERR! stack at ChildProcess.emit (node:events:519:28)
    gyp ERR! stack at ChildProcess._handle.onexit (node:internal/child_process:294:12)
    gyp ERR! System Darwin 23.4.0
    gyp ERR! command "/opt/homebrew/Cellar/node/21.7.2/bin/node" "/プロジェクトのパス/node_modules/node-gyp/bin/node-gyp.js" "configure" "--fallback-to-build" "--update-binary" "--module=/プロジェクトのパス/node_modules/canvas/build/Release/canvas.node" "--module_name=canvas" "--module_path=/プロジェクトのパス/node_modules/canvas/build/Release" "--napi_version=9" "--node_abi_napi=napi" "--napi_build_version=0" "--node_napi_label=node-v120"
    gyp ERR! cwd /プロジェクトのパス/node_modules/canvas
    gyp ERR! node -v v21.7.2
    gyp ERR! node-gyp -v v10.1.0
    gyp ERR! not ok 
    node-pre-gyp ERR! build error 
    node-pre-gyp ERR! stack Error: Failed to execute '/opt/homebrew/Cellar/node/21.7.2/bin/node /プロジェクトのパス/node_modules/node-gyp/bin/node-gyp.js configure --fallback-to-build --update-binary --module=/プロジェクトのパス/node_modules/canvas/build/Release/canvas.node --module_name=canvas --module_path=/プロジェクトのパス/node_modules/canvas/build/Release --napi_version=9 --node_abi_napi=napi --napi_build_version=0 --node_napi_label=node-v120' (1)
    node-pre-gyp ERR! stack     at ChildProcess.<anonymous> (/プロジェクトのパス/node_modules/@mapbox/node-pre-gyp/lib/util/compile.js:89:23)
    node-pre-gyp ERR! stack     at ChildProcess.emit (node:events:519:28)
    node-pre-gyp ERR! stack     at maybeClose (node:internal/child_process:1105:16)
    node-pre-gyp ERR! stack     at ChildProcess._handle.onexit (node:internal/child_process:305:5)
    node-pre-gyp ERR! System Darwin 23.4.0
    node-pre-gyp ERR! command "/opt/homebrew/Cellar/node/21.7.2/bin/node" "/プロジェクトのパス/node_modules/@mapbox/node-pre-gyp/bin/node-pre-gyp" "install" "--fallback-to-build" "--update-binary"
    node-pre-gyp ERR! cwd /プロジェクトのパス/node_modules/canvas
    node-pre-gyp ERR! node -v v21.7.2
    node-pre-gyp ERR! node-pre-gyp -v v1.0.11
    node-pre-gyp ERR! not ok 
    Failed to execute '/opt/homebrew/Cellar/node/21.7.2/bin/node /プロジェクトのパス/node_modules/node-gyp/bin/node-gyp.js configure --fallback-to-build --update-binary --module=/プロジェクトのパス/node_modules/canvas/build/Release/canvas.node --module_name=canvas --module_path=/プロジェクトのパス/node_modules/canvas/build/Release --napi_version=9 --node_abi_napi=napi --napi_build_version=0 --node_napi_label=node-v120' (1)
    ```
</details>

pangocairoが見つからないことがわかりました。

pango と cairo をインストールします。

```bash
brew install pango cairo
```

## 3. canvasを再度インストール

```bash
yarn add canvas
 
➤ YN0000: · Yarn 4.1.1
➤ YN0000: ┌ Link step
➤ YN0007: │ canvas@npm:2.11.2 must be built because it never has been before or the last one failed
➤ YN0000: └ Completed in 20s 558ms
➤ YN0000: · Done with warnings in 21s 430ms yarn add canvas
```

インストールに成功しました。

# おわりに

今回は何も見ずエラーログから解決したのですが、[node-canvasの公式GitHubのWiki](https://github.com/Automattic/node-canvas/wiki/Installation%3A-Mac-OS-X)を閲覧したところOS X (macOS)の場合は

```bash
brew install pkg-config cairo pango libpng jpeg giflib librsvg pixman
```

でインストールすることが推奨されていました。ただし、`jpeg`、`giflib`、`libsvg`のインストールはそれぞれJPEG、GIF、SVGに対応させたい場合のオプショナルであるようです。

結果として、今回の対処は正しくはあったのですが、公式のGithubを閲覧すれば一発だったということもあり、今後は新しいモジュール、ライブラリ等を使用する際には公式のGithub、Docsを閲覧すべきという教訓が得られました。

# ちなみに・・・

なぜこれらのパッケージを追加でインストールする必要があるかというと、どうやら私の使用しているPCのプロセッサアーキテクチャによる影響があるようです。

私は普段開発でApple社のM2 MacBook Proを使用しているのですが、このPCに搭載されているCPUであるApple M2 ProのプロセッサアーキテクチャはARM64であり、ARM64アーキテクチャ上でnode-canvasを利用するためにはこれらのパッケージを利用してシステム上でコンパイルする必要があるという理由のようです。（[参考](https://qiita.com/shinobear01/items/157552be4039a71d47a8)）