Closed3

初めてのApple M2(x86_64とarm64)

seckseck

2016年モデルのmacbook proから2022年モデルのmacbook air(M2)に切り替えたが、
なんか色々仕様が変わっている(もしくは自分が意識できていなかっただけ?)っぽいのでまとめておく。

問題が発生した発端はPython-MIPを動かそうとしたときの話。
なんかエラーが起きた。

import mip
model = mip.Model(name="sample_problem", solver_name=mip.CBC)

# エラーメッセージ抜粋
# An error occurred while loading the CBC library:	 cannot load library '/Users/secky/Library/Mobile Documents/com~apple~CloudDocs/engineering/2025/env/lib/python3.11/site-packages/mip/libraries/cbc-c-darwin-x86-64.dylib': dlopen(/Users/secky/Library/Mobile Documents/com~apple~CloudDocs/engineering/2025/env/lib/python3.11/site-packages/mip/libraries/cbc-c-darwin-x86-64.dylib, 0x0002): tried: '/Users/secky/Library/Mobile Documents/com~apple~CloudDocs/engineering/2025/env/lib/python3.11/site-packages/mip/libraries/cbc-c-darwin-x86-64.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64e' or 'arm64')), '/System/Volumes/Preboot/Cryptexes/OS/Users/secky/Library/Mobile Documents/com~apple~CloudDocs/engineering/2025/env/lib/python3.11/site-packages/mip/libraries/cbc-c-darwin-x86-64.dylib' (no such file), '/Users/secky/Library/Mobile Documents/com~apple~CloudDocs/engineering/2025/env/lib/python3.11/site-packages/mip/libraries/cbc-c-darwin-x86-64.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64e' or 'arm64')).  Additionally, ctypes.util.find_library() did not manage to locate a library called '/Users/secky/Library/Mobile Documents/com~apple~CloudDocs/engineering/2025/env/lib/python3.11/site-packages/mip/libraries/cbc-c-darwin-x86-64.dylib'
# NameError: name 'cbclib' is not defined

どうやらCBCソルバーがx86_64のバイナリだから、デフォルトのarm64では動かないらしい。
https://qiita.com/SaitoTsutomu/items/fbc33299e1906a238f53

どうやら、うまくx86_64を動かす仕組みがあり、
こちらのサイトにとても丁寧にまとまっていた。うれしい。
https://zenn.dev/suzuki_hoge/books/2021-07-m1-mac-4ede8ceb81e13aef10cf
https://zenn.dev/suzuki_hoge/books/2021-07-m1-mac-4ede8ceb81e13aef10cf/viewer/1-cpu-and-is
https://zenn.dev/suzuki_hoge/books/2021-07-m1-mac-4ede8ceb81e13aef10cf/viewer/6-shell-policy

とりあえず一旦ここまでで、補足を少しづつ加えていきたい。

seckseck

どうやら、x86_64とarm64のアーキテクチャごとに環境を独立させて用意する必要があるらしい。
少しまとめた。

コマンド解説

  • softwareupdate --install-rosetta
    • Rosetta 2をインストールする
  • uname -m
    • 現状のアーキテクチャどちらか確認する。
  • arch -x86_64 /bin/zsh あるいは arch -arm64 /bin/zsh
    • アーキテクチャを指定した状態でコマンドを実行する。

それぞれの環境にbrewを入れるとき

  • x86_64かarm64かで、brewのインストール先のディレクトリが異なる。
    • brew --prefixで確認可能
  • brewをx86_64とarm64の2つ入れる場合、単にbrewを実行したときどちらのbrewか注意。
    • パス設定の管理をきちんとするか、フルパスでbrewを指定する。

いくつか生成AIにコマンドについて聞いたので、その結果をそのまま掲載しておく。

unameの生成AI解説

uname コマンドは、Unix Name の略で、システムに関する様々な情報を表示するためのコマンドです。 オプションによって表示される情報が異なります。代表的なオプションとM1 Macでの出力例を挙げます。

  • uname (オプションなし): システムのカーネル名を表示します。 M1 Macでは Darwin となります。これはmacOS (およびiOS, iPadOS, watchOS, tvOS) のカーネルです。

  • uname -a (all): ほぼ全ての情報を表示します。

    • Darwin <ホスト名> <カーネルバージョン> <ビルド日時> <ハードウェア名> <プロセッサ名>
    • 例: Darwin My-MacBook-Pro.local 22.6.0 Darwin Kernel Version 22.6.0: Mon Aug 7 20:25:09 PDT 2023; root:xnu-8796.141.3~2/RELEASE_ARM64_T8112 arm64
  • uname -m (machine hardware name): マシンのハードウェア名 (CPUアーキテクチャ) を表示します。 M1 Macでは arm64 です。

  • uname -p (processor): プロセッサの種類を表示します。 M1 Macでは arm です。

  • uname -r (kernel release): カーネルのリリースバージョンを表示します。 例: 22.6.0

  • uname -s (kernel name): カーネル名を表示します。 Darwin となります。

  • uname -n (network node hostname): ネットワークノードのホスト名を表示します。 例: My-MacBook-Pro.local

  • uname -v (kernel version): カーネルのバージョンを表示します。 例: Darwin Kernel Version 22.6.0: Mon Aug 7 20:25:09 PDT 2023; root:xnu-8796.141.3~2/RELEASE_ARM64_T8112

これらのオプションを組み合わせて使うこともできます。例えば、uname -sm とすると、カーネル名とハードウェア名が続けて表示されます。

uname コマンドは、シェルスクリプトなどでシステムの種類を判別する際に役立ちます。例えば、特定のアーキテクチャ用の処理を実行する場合などに使用されます。

lipoの生成AI解説

lipo コマンドは、ユニバーサルバイナリ (複数のアーキテクチャに対応したバイナリ) を操作するためのコマンドラインツールです。macOS、iOS、watchOS、tvOS などの Apple プラットフォームで開発を行う際に使用されます。

ユニバーサルバイナリは、異なるCPUアーキテクチャ(例:Intel x86_64、Apple Silicon arm64)を持つ複数のマシンで同じバイナリを実行できるように設計されています。 lipo を使用すると、ユニバーサルバイナリから特定のアーキテクチャのスライスを抽出したり、複数のアーキテクチャのスライスを結合してユニバーサルバイナリを作成したり、含まれるアーキテクチャの種類を確認したりできます。

主な機能と使用方法:

  • アーキテクチャの確認:

    lipo -info バイナリファイル名
    

    例:lipo -info /bin/ls
    出力例:Architectures in the fat file: /bin/ls are: x86_64 arm64e

  • 特定のアーキテクチャのスライスを抽出:

    lipo -thin アーキテクチャ名 バイナリファイル名 -output 出力ファイル名
    

    例:lipo -thin arm64e /bin/ls -output ls_arm64e (/bin/ls から arm64e アーキテクチャのスライスを抽出して ls_arm64e というファイルに保存)

  • 複数のアーキテクチャのスライスからユニバーサルバイナリを作成:

    lipo -create バイナリファイル1 バイナリファイル2 ... -output 出力ファイル名
    

    例:lipo -create ls_x86_64 ls_arm64e -output ls_universal (ls_x86_64 と ls_arm64e からユニバーサルバイナリを作成して ls_universal というファイルに保存)

  • ファットファイル (ユニバーサルバイナリ) の置き換え:

    lipo -replace アーキテクチャ名 新しいバイナリファイル名 -output 出力ファイル名
    

    例:lipo -replace arm64e new_ls_arm64e -output ls_universal (ls_universal 内の arm64e アーキテクチャのスライスを new_ls_arm64e で置き換え)

使用例:

例えば、Apple Silicon Mac で Intel バイナリを実行するために Rosetta 2 を使用する場合、lipo を使用してユニバーサルバイナリから x86_64 スライスを抽出し、それを Rosetta 2 で実行することができます。 (ただし、通常はこのような操作は必要ありません。Rosetta 2 は自動的に適切なアーキテクチャを選択します。)

lipo コマンドは、開発者が異なるアーキテクチャに対応したアプリケーションをビルドし、配布する際に非常に役立ちます。

注意点:

  • lipo はファットバイナリ (ユニバーサルバイナリ) 専用のコマンドです。単一アーキテクチャのバイナリに対しては使用できません。
  • 操作対象のファイルは、実行権限を持っている必要があります。

より詳細な情報は、ターミナルで man lipo と入力してマニュアルページを参照してください。

単一アーキテクチャのバイナリかどうかを確認(生成AI解説)

単一アーキテクチャのバイナリかどうかを確認するには、file コマンドと lipo コマンドを組み合わせて使用します。

  1. file コマンドでファイルの種類を確認:

    file バイナリファイル名
    

    もしバイナリが単一アーキテクチャの場合、出力にはアーキテクチャの情報が含まれます。例えば、

    • Intel Mac でコンパイルされたバイナリ: Mach-O 64-bit executable x86_64
    • Apple Silicon Mac でコンパイルされたバイナリ: Mach-O 64-bit executable arm64
    • Intel Mac でコンパイルされた 32-bit バイナリ: Mach-O 32-bit executable i386

    もしバイナリがユニバーサルバイナリ(fat binary)の場合、出力には "Mach-O universal binary" という記述が含まれます。例えば、

    • Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64:Mach-O 64-bit executable arm64]
  2. lipo コマンドでアーキテクチャを確認 (念のため):

    file コマンドでユニバーサルバイナリでないと判断された場合でも、念のため lipo コマンドで確認できます。

    lipo -info バイナリファイル名
    
    • 単一アーキテクチャの場合: fatal error: /path/to/binary is not a fat file のようなエラーメッセージが表示されます。
    • ユニバーサルバイナリの場合: 含まれるアーキテクチャの一覧が表示されます (例: Architectures in the fat file: /bin/ls are: x86_64 arm64e)

まとめ:

  • file コマンドで "Mach-O universal binary" が含まれていない場合は、単一アーキテクチャのバイナリです。
  • lipo -info でエラーが出る場合も、単一アーキテクチャのバイナリです。

これらのコマンドを組み合わせて使用することで、バイナリが単一アーキテクチャかユニバーサルバイナリかを確実に判断できます。

seckseck

結局Python-MIPは以下のやり方で動かせた。

  1. x86_64上のbrewでpythonを準備
  2. このpython(x86_64なので/usr/local/bin/に入っている)を実行する。このとき、archコマンドでわざわざ指定する必要はなく、そのまま実行すれば良い。
    • pythonのインタープリター上で、以下を実行すれば実行中のアーキテクチャがx86_64に自動的に変換されているのがわかる。
    • ただその分、実行の一番最初に時間がかかっている(気がする)。
import os
print(os.uname())
# posix.uname_result(sysname='Darwin', nodename='MacBookAir.local', release='24.4.0', version='Darwin Kernel Version 24.4.0: Wed Mar 19 21:18:03 PDT 2025; root:xnu-11417.101.15~1/RELEASE_ARM64_T8112', machine='x86_64')
このスクラップは2025/04/08にクローズされました