MacのRosetta2のサポートが終了するみたいなので、Intelのアプリを検索するスクリプトをAIに作ってもらった。
26/09/16 - はじめに / 記事のスクリプトに関する注意事項
ご迷惑おかけして申し訳ありません。初めにお断りしなければならない事を申し上げておきます。
本記事はRosetta2のサポート終了に伴うアプリケーションの本格的な解析や移行作業を目的とするものではありません。あくまでも大雑把な探索を目的とするものとします。そのような本格的なツールがあれば、是非掲載させて頂きたいと思いますので、コメントを頂ければと思います。
app内部は複雑である可能性があります。
掲載されているスクリプトは、Rosettaなしで動作する事を保証するものではありません。
ChatGPTによると、様々なappが存在するため、完全な解析には不十分であるという事です。
ChatGPTより補足。(26/09/16)
「.app の中にある実行バイナリを全部探す」だけならまだできますが、「このアプリはRosettaなしで確実に動くか?」を判定するとなると別問題です。
この記事で紹介するスクリプトは、.app/Contents/MacOS/ に含まれる実行ファイルのarchitectureを調べるものです。アプリ内部のFramework、Plug-in、XPCサービスなど、すべての実行コードを網羅的に検査するものではありません。したがって、ここでの「Universal」はアプリ全体の互換性を保証するものではありません。
26/09/17 - ネットからの情報
自分一人では責任が重いので、重い腰を上げて探してみました。
こちらのサイトで詳しい説明がされているようです。
「Universal」となっていても安心できないのが現実のようです。
有料のアプリになりますが、ストアには「Rosetta Check」というツールもあるようです。
こういったツールによる解析を利用した方が良さそうです。
26/06/29, 26/09/16 - 修正コードと経緯
(26/06/29)
@kkdd様からご指摘があったので、再度ChatGPT(無料版)に質問したところ修正コードをいただきましたので、ChatGPTによる引用含め、コードを提示させて頂きます。
また、何か問題がありましたら一報下さい。
改善案などありましたら、是非リンクを貼らさせて頂きたいと思います。
検索対象は/Applications配下のサブディレクトリを含むようになっているはずです。
(26/09/16)
@kkdd様より、シェルスクリプトを頂きましたので、
ご不満があれば、こちらを参考にして頂きたいと思います。
引き続き、何か改善案があれば、コメントを頂けましたら反映させて頂きたいと思います。
私の記事のスクリプトは、/Applications内部を大雑把に探索するものになっていますが、厳密な精度を保障するものではありません。
(筆者がMacを2台しか持ったことがないため、Appleシリコンかintelかの違いくらいしか分かっていないので、マカーの皆様からは不満に思われるかもしれません)
ChatGPTの「完全版」を言葉通りに表現すると、完璧な解析ツールであるという誤解を生むので以降version形式で記載することにしました。
※アプリが全て「判定不能」になる、などの現象が起きる場合
lipoコマンドの実行でエラーが出る可能性があるので、その際にはarchで変更してから実行してみてください。(26/09/16)
シェルスクリプト版(v3):
arch -arm64 zsh
Python版(v1,v2):
subprocessのコメントアウト部分のarchコマンドで変更してから実行してみてください。
pythonの実行環境とターミナルの実行環境の違いを確認するには、
import platform
print(f"Python architecture: {platform.machine()}")
とarchコマンドの環境の違いを確認してみてください。
ChatGPTより
Apple Silicon版macOSでは、lipoなどの開発者向けコマンドをRosetta経由のx86_64環境から実行すると、システム側のarm64/arm64eライブラリとの互換性によって失敗する場合があります。そのため、architectureを調べる際はlipo自体をarm64で実行するのが安全です。
ChatGPT版v3(シェルスクリプト)
(作成の経緯:v2以前のPythonよりもシェルスクリプトの方が環境依存のトラブルが発生しにくいと判断したため、v2からシェルスクリプトに変換するようにChatGPTに指示して作成しました)
#!/bin/bash
echo "/Applications 以下の全アプリを解析しています..."
OUTPUT="$HOME/Desktop/Applicationsフォルダ内アプリリスト.txt"
# 一時ファイル
TMP_DIR=$(mktemp -d)
trap 'rm -rf "$TMP_DIR"' EXIT
INTEL="$TMP_DIR/intel.txt"
UNIVERSAL="$TMP_DIR/universal.txt"
ARM64="$TMP_DIR/arm64.txt"
OTHER="$TMP_DIR/other.txt"
: > "$INTEL"
: > "$UNIVERSAL"
: > "$ARM64"
: > "$OTHER"
match_count=0
# /Applications 以下の .app をすべて取得
while IFS= read -r app; do
[ -z "$app" ] && continue
app_name=$(basename "$app" .app)
plist_path="$app/Contents/Info.plist"
# Info.plist がなければスキップ
if [ ! -f "$plist_path" ]; then
continue
fi
match_count=$((match_count + 1))
# CFBundleExecutable を取得
executable=$(
/usr/libexec/PlistBuddy \
-c "Print :CFBundleExecutable" \
"$plist_path" 2>/dev/null
)
if [ -z "$executable" ]; then
{
echo "■ $app_name"
echo " - 判定: 判定不能 (CFBundleExecutableなし)"
echo " - アーキテクチャ: -"
echo " - パス: $app"
echo
} >> "$OTHER"
continue
fi
binary="$app/Contents/MacOS/$executable"
# 実行ファイルがなければ判定不能
if [ ! -f "$binary" ]; then
{
echo "■ $app_name"
echo " - 判定: 判定不能 (実行ファイルなし)"
echo " - アーキテクチャ: -"
echo " - パス: $app"
echo
} >> "$OTHER"
continue
fi
# アーキテクチャ取得
archs=$(lipo -archs "$binary" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$archs" ]; then
{
echo "■ $app_name"
echo " - 判定: 判定不能 (lipo失敗)"
echo " - アーキテクチャ: -"
echo " - パス: $app"
echo
} >> "$OTHER"
continue
fi
# 判定
if echo "$archs" | grep -q "x86_64" &&
(echo "$archs" | grep -q "arm64" || echo "$archs" | grep -q "arm64e"); then
category="Universal (🍏そのまま動く・両対応)"
kind="Universal"
output_file="$UNIVERSAL"
elif echo "$archs" | grep -q "x86_64"; then
category="Intel専用 (⚠️将来動かなくなる可能性)"
kind="Intel専用"
output_file="$INTEL"
elif echo "$archs" | grep -q "arm64" ||
echo "$archs" | grep -q "arm64e"; then
category="Apple Silicon専用 (🍏そのまま動く)"
kind="Apple Silicon専用"
output_file="$ARM64"
else
category="判定不能・その他 (データなし)"
kind="判定不能"
output_file="$OTHER"
fi
{
echo "■ $app_name"
echo " - 判定: $kind"
echo " - アーキテクチャ: $archs"
echo " - パス: $app"
echo
} >> "$output_file"
done < <(find /Applications -type d -name "*.app" 2>/dev/null | sort)
# 出力ファイル作成
{
echo "=== /Applications 内アプリ一覧 (${match_count}個) ==="
echo "Apple公式のCFBundleExecutableを取得し、lipo -archsで判定"
echo
echo "▼ Intel専用 (⚠️将来動かなくなる可能性) : $(grep -c '^■ ' "$INTEL")個"
echo "--------------------------------------------------"
cat "$INTEL"
echo
echo "▼ Universal (🍏そのまま動く・両対応) : $(grep -c '^■ ' "$UNIVERSAL")個"
echo "--------------------------------------------------"
cat "$UNIVERSAL"
echo
echo "▼ Apple Silicon専用 (🍏そのまま動く) : $(grep -c '^■ ' "$ARM64")個"
echo "--------------------------------------------------"
cat "$ARM64"
echo
echo "▼ 判定不能・その他 (データなし) : $(grep -c '^■ ' "$OTHER")個"
echo "--------------------------------------------------"
cat "$OTHER"
echo
} > "$OUTPUT"
echo "解析が完了しました。"
echo "保存先: $OUTPUT"
なお、元のPython版と同様に .app の中の CFBundleExecutable を取得 → 実行ファイルに対して lipo -archs という方式です。
一点だけ改善していて、Python版の match_count と同じく、Info.plist が存在する .app をカウント対象にしています。
ChatGPT版v2(Pythonスクリプト)
(作成の経緯:ChatGPT版v1には不完全な部分があると自ら提案したので、それを掲載)
はい。精度を最優先するなら、Appleのアプリ構造に沿って「Info.plist → CFBundleExecutable → lipo -archs」の順で調べるのが最も確実です。
この方法なら、
アプリ本体だけを判定
HelperやUpdaterに影響されない
Universal判定も100%正確
Apple純正コマンドのみ使用
となります。
以下が完全版です。
import os
import plistlib
import subprocess
print("/Applications 以下の全アプリを解析しています...")
OUTPUT = os.path.expanduser("~/Desktop/Applicationsフォルダ内アプリリスト.txt")
categories = {
"Intel専用 (⚠️将来動かなくなる可能性)": [],
"Universal (🍏そのまま動く・両対応)": [],
"Apple Silicon専用 (🍏そのまま動く)": [],
"判定不能・その他 (データなし)": []
}
match_count = 0
# findでApplications以下すべての.appを取得
result = subprocess.run(
["find", "/Applications", "-type", "d", "-name", "*.app"],
capture_output=True,
text=True
)
apps = [x for x in result.stdout.splitlines() if x.strip()]
for app in sorted(apps):
app_name = os.path.basename(app).replace(".app", "")
plist_path = os.path.join(app, "Contents", "Info.plist")
if not os.path.exists(plist_path):
continue
try:
with open(plist_path, "rb") as fp:
plist = plistlib.load(fp)
executable = plist.get("CFBundleExecutable")
if not executable:
raise Exception("CFBundleExecutableなし")
binary = os.path.join(app, "Contents", "MacOS", executable)
if not os.path.isfile(binary):
raise Exception("実行ファイルなし")
r = subprocess.run(
["lipo", "-archs", binary],
# ["arch", "-arch", "arm64", "lipo", "-archs", binary],
capture_output=True,
text=True
)
if r.returncode != 0:
raise Exception("lipo失敗")
archs = r.stdout.strip().split()
if "x86_64" in archs and ("arm64" in archs or "arm64e" in archs):
category = "Universal (🍏そのまま動く・両対応)"
kind = "Universal"
elif "x86_64" in archs:
category = "Intel専用 (⚠️将来動かなくなる可能性)"
kind = "Intel専用"
elif "arm64" in archs or "arm64e" in archs:
category = "Apple Silicon専用 (🍏そのまま動く)"
kind = "Apple Silicon専用"
else:
category = "判定不能・その他 (データなし)"
kind = "判定不能"
except Exception as e:
archs = []
category = "判定不能・その他 (データなし)"
kind = f"判定不能 ({e})"
match_count += 1
categories[category].append(
f"""■ {app_name}
- 判定: {kind}
- アーキテクチャ: {" ".join(archs) if archs else "-"}
- パス: {app}
"""
)
with open(OUTPUT, "w", encoding="utf-8") as f:
f.write(f"=== /Applications 内アプリ一覧 ({match_count}個) ===\n")
f.write("Apple公式のCFBundleExecutableを取得し、lipo -archsで判定\n\n")
for section, apps in categories.items():
f.write(f"▼ {section} : {len(apps)}個\n")
f.write("-" * 50 + "\n")
f.writelines(apps)
f.write("\n")
print("解析が完了しました。")
print(f"保存先: {OUTPUT}")
以下、ChatGPT(26/06/29)より
この版が「完全版」と言える理由
このスクリプトは、一般的な .app の構造を前提にした最も信頼性の高い方法を採用しています。
find で /Applications 配下のすべての .app を再帰的に検索します。
各アプリの Contents/Info.plist を読み込みます。
CFBundleExecutable に記載されたメイン実行ファイルだけを対象にします。
lipo -archs で CPU アーキテクチャを取得します。
x86_64、arm64、arm64e の組み合わせから Intel専用・Apple Silicon専用・Universal を判定します。
この方法なら、補助ツールやヘルパー実行ファイルに左右されず、アプリ本体の対応状況を正確に判定できます。
さらに精度を上げることも可能で、Finderで表示されるアプリ名(CFBundleDisplayName や CFBundleName)を取得して表示する、あるいはIntel専用アプリだけを別ファイルに一覧出力するといった機能を追加すると、移行準備にも役立つスクリプトになります
ChatGPT版v1(Pythonスクリプト)
(作成の経緯:GeminiのPythonコードには問題がありましたので、v0を修正しました)
import os
import subprocess
import glob
print("/Applications 以下の全アプリをチェックしています...")
output_path = os.path.expanduser("~/Desktop/Applicationsフォルダ内アプリリスト.txt")
categorized_apps = {
"Intel専用 (⚠️将来動かなくなる可能性)": [],
"Universal (🍏そのまま動く・両対応)": [],
"Apple Silicon専用 (🍏そのまま動く)": [],
"判定不能・その他 (データなし)": []
}
# findで.appを取得
result = subprocess.run(
["find", "/Applications", "-type", "d", "-name", "*.app"],
capture_output=True,
text=True
)
apps = result.stdout.strip().split("\n")
match_count = 0
for app_path in apps:
if not app_path:
continue
app_name = os.path.basename(app_path).replace(".app", "")
# Contents/MacOS 以下の全ファイル
execs = glob.glob(os.path.join(app_path, "Contents", "MacOS", "*"))
if not execs:
continue
archs = set()
for exe in execs:
if os.path.isdir(exe):
continue
try:
r = subprocess.run(
["lipo", "-archs", exe],
# ["arch", "-arch", "arm64", "lipo", "-archs", exe],
capture_output=True,
text=True
)
if r.returncode == 0:
archs.update(r.stdout.strip().split())
except Exception:
pass
if not archs:
key = "判定不能・その他 (データなし)"
kind = "判定不能"
elif "x86_64" in archs and ("arm64" in archs or "arm64e" in archs):
key = "Universal (🍏そのまま動く・両対応)"
kind = "Universal"
elif "x86_64" in archs:
key = "Intel専用 (⚠️将来動かなくなる可能性)"
kind = "Intel専用"
elif "arm64" in archs or "arm64e" in archs:
key = "Apple Silicon専用 (🍏そのまま動く)"
kind = "Apple Silicon専用"
else:
key = "判定不能・その他 (データなし)"
kind = "判定不能"
match_count += 1
categorized_apps[key].append(
f"■ {app_name}\n"
f" - 判定: {kind}\n"
f" - アーキテクチャ: {' '.join(sorted(archs)) if archs else '-'}\n"
f" - パス: {app_path}\n\n"
)
with open(output_path, "w", encoding="utf-8") as f:
f.write(f"=== /Applications 内アプリ一覧({match_count}個)===\n\n")
for section, apps in categorized_apps.items():
f.write(f"▼ {section} : {len(apps)}個\n")
f.write("---------------------------------\n")
f.write("".join(apps))
f.write("\n")
print("完了しました。デスクトップを確認してください。")
以下、ChatGPT(26/06/29)より
はい。この指摘はもっともです。
現在のPythonスクリプトには次の問題があります。
Contents/MacOS の最初のファイルだけを調べている
実行ファイルが複数あるアプリでは誤判定する
fileコマンドは文字列解析になるため、lipo -archsの方が確実
lipo -archsはAppleが推奨する方法で、Mach-Oバイナリに含まれるCPUアーキテクチャだけを返してくれます。
例えば
lipo -archs "/Applications/Safari.app/Contents/MacOS/Safari"
なら
arm64e
あるいは
x86_64 arm64
のような結果になります。
また、ご提示の
find "/Applications" -type d -name '*.app'
でアプリを探す方法も、os.walkよりシンプルで確実です。
また、
さらに精度を上げるなら
この修正版でも「Contents/MacOS内のすべての実行ファイル」を調べるので、元のスクリプトよりかなり正確です。
ただし、macOSアプリには補助ツールやヘルパー実行ファイルが含まれることがあり、それらのアーキテクチャがメイン実行ファイルと異なる場合があります。そのため、最も正確なのは Info.plist の CFBundleExecutable を読み取り、そのアプリのメイン実行ファイルだけを lipo -archs で調べる方法です。Appleのアプリ構造にも沿った方法で、ヘルパー実行ファイルによる誤判定を避けられます。
もし精度を最優先するなら、その方式にした完全版をおすすめします。
旧コード・Gemini・AIモードv0(Pythonスクリプト)
記事の簡略化のため、問題のあるコードと判断し、削除しました。