Apple Silicon版 Mac(M1)で開発するときのメモ

どこにでもありそうな内容だが、Apple Silicon版 Mac(M1)で開発する時によく使うコマンドをまとめた。

## 1. ビルド編
### CMakeのビルド設定

[CMAKE_OSX_ARCHITECTURES](https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_ARCHITECTURES.html#variable:CMAKE_OSX_ARCHITECTURES)を設定しないとデフォルトでは M1 macのarm64環境でビルドしたらApple Silicon版(arm64)ができる。Intel MacならIntel Mac(Rosetta2)版(x86_64)ができる。[CMake 3.19.4](https://gitlab.kitware.com/cmake/cmake/-/tree/v3.19.4)以降でUniversal Binaryに対応している。 


```zsh:Build_Universal_Binary
cmake -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" -DCMAKE_BUILD_TYPE=Release ..
cmake --build .
```

[CMake v3.19.2](https://gitlab.kitware.com/cmake/cmake/-/tree/v3.19.2)からは[CMAKE_APPLE_SILICON_PROCESSOR](https://cmake.org/cmake/help/latest/envvar/CMAKE_APPLE_SILICON_PROCESSOR.html#envvar:CMAKE_APPLE_SILICON_PROCESSOR)でビルドする環境を指定することができる。

```zsh:Build_AppleSiliconApp
cmake -DCMAKE_APPLE_SILICON_PROCESSOR="arm64" -DCMAKE_BUILD_TYPE=Release ..
cmake --build .
```

`cmake --version` で3.19.4より古かったら更新したほうがよい( `brew upgrade cmake` )。

### CMakeLists.txt内での処理の切り替え
[CMAKE_SYSTEM_PROCESSOR](https://cmake.org/cmake/help/latest/variable/CMAKE_SYSTEM_PROCESSOR.html?highlight=cmake_system_processor)はコンパイル対象のプロセッサー、[CMAKE_HOST_SYSTEM_PROCESSOR](https://cmake.org/cmake/help/latest/variable/CMAKE_HOST_SYSTEM_PROCESSOR.html#variable:CMAKE_HOST_SYSTEM_PROCESSOR)は実行しているシステムのプロセッサーを返す。

```cmake
if(APPLE)
    message(STATUS "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}")
    message(STATUS "CMAKE_HOST_SYSTEM_PROCESSOR: ${CMAKE_HOST_SYSTEM_PROCESSOR}")
    message(STATUS "CMAKE_OSX_ARCHITECTURES: ${CMAKE_OSX_ARCHITECTURES}")
    message(STATUS "CMAKE_INSTALL_RPATH: ${CMAKE_INSTALL_RPATH}")
    foreach(ARCH IN LISTS CMAKE_OSX_ARCHITECTURES)
        if(${ARCH} MATCHES "arm64")
            # arm64設定
            message(STATUS "CMAKE_OSX_ARCHITECTURES: arm64")
        elseif(${ARCH} MATCHES "x86_64")
            # x86_64設定
            message(STATUS "CMAKE_OSX_ARCHITECTURES: x86_64")
        else()
             # 未知のプロセッサ or 未設定
            message(STATUS "CMAKE_OSX_ARCHITECTURES: ${CMAKE_OSX_ARCHITECTURES}")
        endif()
    endforeach()
endif()
```

### node-gypのx86_64版ビルド設定
そのままM1 macでビルドするとarm64版ができる。x86_64版は--arch=x86_64を指定する。
この方法だとUniversal binaryは作れなそう(2021/10/14現在)。

```
"node-gyp clean configure build --verbose --arch=x86_64"
```

### node-gypのUniversal binaryビルド
まだ試せていないのでメモのみ(2021/10/14現在)。[node-gyp-build](https://github.com/prebuild/node-gyp-build)の[ここ](https://github.com/prebuild/node-gyp-build/commit/d9c2118210206dad678fc651da477f366f3097da)で変更が入っているのでver.4.3.0 (Sep 12, 2021)以降を使うとできそう。オプションで--arch=arm64+x86_64を指定してビルドする？


## 2. コマンド編
M1 Macを使っている時によく使うコマンド。

### uname -m: 使っているTerminalがx86_64かarm64かを確認する。

```zsh
% uname -m
arm64
```

### arch -x86_64: arm64 terminalでx86_64コマンドを呼び出す。
"uname"コマンドをx86_64で呼び出した例。

```zsh
% arch -x86_64 uname -m
x86_64
```

#### x86_64版Terminalへの切り替え
使っているターミナルをx86_64版に切り替えてからビルドする時などに使う。

```zsh
% arch -x86_64 /bin/zsh
```

### lipo -archs: 実行ファイルの対応アーキテクチャを確認する。

```zsh
% lipo -archs /System/Applications/Notes.app/Contents/MacOS/Notes 
x86_64 arm64e
```
コマンドのパスを探してアーキテクチャを確認するには`which ***`で囲んで戻りを使う

```zsh
% lipo -archs `which node`
arm64
```


### otool -L: リンクしているライブラリをリスト表示
M1と関係ないけど。Windowsの[Dependencies](https://github.com/lucasg/Dependencies)みたいなもの。リンクしているライブラリを調べるのに使う。

```zsh
% otool -L /System/Applications/Notes.app/Contents/MacOS/Notes
/System/Applications/Notes.app/Contents/MacOS/Notes:
	/System/Library/Frameworks/PencilKit.framework/Versions/A/PencilKit (compatibility version 1.0.0, current version 1.0.0)
	/System/Library/PrivateFrameworks/NotesShared.framework/Versions/A/NotesShared (compatibility version 1.0.0, current version 1872.0.0)
	/System/Library/PrivateFrameworks/NotesUI.framework/Versions/A/NotesUI (compatibility version 1.0.0, current version 1872.0.0)
	...
```

### install_name_tool : リンクしているdylibを変更
otool -L と一緒によく使うコマンドでinstall_name_toolがある。リンクしているdylibのパスを @rpath に後から変更したい時やdylibのパスを修正するときなどに利用する。"a.out"という実行ファイルのlibc++.1.dylibのリンクパスを `/usr/lib/libc++.1.dylib` に変更する例。

```
% install_name_tool -change /usr/local/opt/llvm/lib/libc++.1.dylib /usr/lib/libc++.1.dylib a.out
```

@rpathを追加した場合、`% otool -l b.out` して`cmd LC_RPATH`という出力が出て来なかったら @rpathが設定されていないのでadd_rpathする。

```
% install_name_tool -add_rpath "@executable_path" b.out
```

### おまけ（/usr/libにシステムのdylibがない件）

Apple Siliconと共に登場したBig Surから“dynamic linker cache”という仕組みが導入され、/usr/libにシステムのdylibが置かれなくなった。
> New in macOS Big Sur 11.0.1, the system ships with a built-in dynamic linker cache of all system-provided libraries. As part of this change, copies of dynamic libraries are no longer present on the filesystem. Code that attempts to check for dynamic library presence by looking for a file at a path or enumerating a directory will fail. Instead, check for library presence by attempting to dlopen() the path, which will correctly check for the library in the cache. (62986286)

https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11_0_1-release-notes



## 3. シェルスクリプト例

### 動作環境(x86_64/arm64)を見て処理を切り替える
Apple SiliconかIntel Macかを見分けるビルドスクリプトを作る時に利用したものから抜粋。

```zsh:checkMacOS.sh
if uname -m | grep --quiet "arm64" 2>&1 > /dev/null ; then
    echo 'is Apple Silicon'
else
    echo 'is Intel/Rosetta2'
fi
```

M1 Macのターミナルから実行すると、

```zsh
% zsh ./checkMacOS.sh 
is Apple Silicon
```

M1 MacのRosette2上のVSCodeターミナルから実行すると、

```zsh
% zsh ./checkMacOS.sh
is Intel/Rosetta2
```

となる。