2023年3月にノートパソコンを買い替えたので、競技プログラミング(競プロ)の環境構築をし直すことになりました。言語はC++です。前回の環境構築で非常に苦労したので、今回は環境構築をしながら後から読んでも再現しやすいように手順を記録していきます。
2023/3/24 追記
以下の手順では、デバッグ時に標準ライブラリのコンテナの成分が確認できるようになっていませんでした。この問題について、より詳しくはセクション8に掲載しました。解決し次第記事を修正します。
環境
MacBook Pro (2023年モデル; M2 Pro)
macOS Ventura 13.2.1
目標
- VSCode の導入
- デバッガーの導入
- AC Library の導入
- AtCoder でのサンプル正答チェック・自動提出ツールの導入
0. 準備
0.1 Homebrew のインストール
パッケージマネージャーHomebrewの公式ページからインストールコマンドをコピペできます。
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
0.1.1 PATH を通す
Warning: /opt/homebrew/bin is not in your PATH.
Instructions on how to configure your shell for Homebrew
can be found in the 'Next steps' section below.
と出たので指示
==> Next steps:
- Run these two commands in your terminal to add Homebrew to your PATH:
に従いターミナルで以下の2行を実行しました(すぐ下で述べる様に、この手順は不要かも)。
(echo; echo 'eval "$(/opt/homebrew/bin/brew shellenv)"') >> /Users/(username)/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
但し(username)
は各自のユーザーネームに置き換える(以下同様)。しかし後の手順で不具合が生じたため、こちらのサイトに従い ファイル.zshrc
に
export PATH=/opt/homebrew/bin:$PATH
と書き込みます。これで PATH が通ったはずです。
参考: mac でターミナルが zsh の場合のパスの通し方に関する一般的な解説のページ
参考: mac での隠しファイルの表示・非表示の切り替え は Command + Shift + .
でできます
0.1.2 Intel チップ用の設定と Apple シリコン用の設定の主な違い
以前の mac ではターミナルからインストールするような諸々のソフトウェアは /usr/local/bin
以下にインストールされる事が多かったと思われますが、Apple シリコンで上記のように Homebrew を使うと /opt/homebrew/bin/
以下にインストールされるようです。そのため、古い解説ページなどに従って設定しても上手くいかない時は、/usr/local/
を /opt/homebrew/
に置き換えると上手くいくことがあるようです。
0.2 Clang か GCC か
結論から書くと、この記事では GCC を使います。
C++ にはいくつかの亜種があり、mac には元から Clang がインストールされています。そのため、Clang で困らなければ Clang を使えばよいように思います。競プロでは標準ライブラリのファイルを一括で読み込む <bits/stdc++.h>
が便利ですが、多くの環境構築記事で指摘されている通り Clang ではデフォルトでは使えません。これが GCC (the GNU Compiler Collection) を代わりに使う主な理由です。
環境構築の詳しい解説ページ Ray's note によると、デバッグ関係は GCC でやろうとすると泥沼にはまったとのことで、(自分の環境で Clang でも <bits/stdc++.h>
を使えるように工夫をした上で) Clang でデバッグし、(提出先でも <bits/stdc++.h>
が使えるように) GCC で提出するという方式が取られていました。
私も以前これに倣って環境構築をし、今回もこれに倣って環境構築をしようと当初は思っていたのですが、上記ページとは対照的に Clang のデバッグや AC-Library の取り扱いがうまくいかなくなってしまい、逆に GCC でのデバッグはなんとか動いたので、GCC 一本で環境構築することにしました。(一部の便利なコンパイルオプション -fsanitize
が使えていませんが…。)
1. GCC のインストール
インストール自体は簡単で、ターミナルで
brew install gcc
とするだけ。
最新版にアップデートしておきましょう。
brew upgrade
brew update と brew upgrade の違い
1.1 シンボリックリンクの設定
Clang の代わりにデフォルトで GCC を使うには、Clang があるディレクトリよりも優先度の高いディレクトリに GCC のシンボリックリンクを置く必要があります。(参考)
元々の Clang の場所を調べます。
which gcc
と入力すると /user/bin/gcc
と表示されました。
PATH の優先順位を調べます。
echo $PATH
と入力すると /opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin
と表示されました。/user/bin
は後ろから4番目なので、それより前にある場所にシンボリックリンクを作ります。gcc がインストールされた場所が /opt/homebrew/
以下なので、場所を特定したら次のようにターミナルに入力します。バージョンの数値などは適宜自分の環境に合わせます。
ln -s /opt/homebrew/Cellar/gcc/12.2.0/bin/g++-12 /opt/homebrew/bin/g++
ln -s /opt/homebrew/Cellar/gcc/12.2.0/bin/gcc-12 /opt/homebrew/bin/gcc
その前に試行錯誤して(Homebrew の PATH を入れ損なっていたせいです)以下のような入力なども試しています。
ln -s /opt/homebrew/bin/g++-12 /opt/homebrew/bin
ln -s /opt/homebrew/bin/g++-12 /usr/local/bin/g++
成功すると、
which gcc
の入力に対して /opt/homebrew/bin/gcc
と出ました。gcc
の部分を g++
にしたものも同様です。
蛇足: Clang で <bits/stdc++.h>
を使えるようにする
Clang は結局使わないことにしましたが、記録として残しておきます。
2. Visual Studio Code: 拡張機能と設定
2.1 VSCode のインストール
Visual Studio Code (VSCode) をインストールします。Intel chip, Apple Silicon, Universal の3つのダウンロードファイルが用意されており、Universal でも良いですが余計なファイルが入っているので Apple Silicon を選びます。
VSCode 公式の macOS での設定のページを参考に設定をします。コマンドパレット(Command + Shift + p
)から shell command
と入力してInstall 'code' command in PATH
を選びます(VSCode の言語設定が英語の場合)。これでターミナルから code .
と入力することで、入力したディレクトリを基準にして VSCode を開けるようになりました。
詳しい説明は公式のドキュメントが充実しています。
2.2 VSCode 拡張機能のインストール
- C/C++
- Code Runner
- CodeLLDB
それから必要に応じて C/C++ Extension Pack なども VSCode 内の拡張機能メニューから検索して追加します。
このページのセクション4.1を参考に、Code Runner の Extension Settings で Code-Runner: Run In Terminal をチェックします。
2.3 各種 JSON ファイルの設定
C++プログラムのビルド・デバッグ等に関する設定をします。VSCode の設定画面を開いて設定するか、対応する JavaScript Object Notation (JSON) ファイルを直接編集します。
インクルードパスなど C++ の基本的な設定
{
"configurations": [
{
"name": "Mac",
"includePath": [
"~/programming/cpp/ac-library/atcoder/**",
"${workspaceFolder}/ac-library/atcoder/**",
"${workspaceFolder}/**",
"/opt/homebrew/include/**"
],
"defines": [],
"macFrameworkPath": [
"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks"
],
"compilerPath": "/opt/homebrew/bin/g++",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "macos-gcc-arm64"
}
],
"version": 4
}
C++ を実行するときのコマンド及び、ここでもインクルードパスを指定
{
"files.associations": {
// skipped
},
"C_Cpp.errorSquiggles": "disabled",
// skipped
"C_Cpp.default.cppStandard": "c++17",
"C_Cpp.default.cStandard": "c17",
"code-runner.executorMap": {
// skipped
"cpp": "cd $dir && g++ -std=c++17 $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
// skipped
},
"C_Cpp.default.includePath": [
"${workspaceFolder}/**",
"/opt/homebrew/include/**",
"/usr/local/include/**",
],
"cmake.configureOnOpen": false,
}
// skipped
は略した部分です。
次のファイル tasks.json
では、
- デバッグ時などに使う通常のタスク
- サンプルチェック用のタスク
- コード提出用のタスク
の3つのタスクを設定します。
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "g++ build active file",
"command": "/opt/homebrew/bin/g++",
"args": [
"-std=c++17",
"-g",
"-Wall",
"-Wextra",
"-Wshadow",
//"-fsanitize=address,undefined",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}",
"-I",
"./ac-library/atcoder/"
],
"options": {
"cwd": "${fileDirname}/",
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "CheckTestCase",
"type": "shell",
"command": "cd ${fileDirname} && g++ -std=gnu++17 ${fileBasename} -o ${fileBasenameNoExtension} && oj test -c \"${fileDirname}/${fileBasenameNoExtension}\" -d ${fileDirname}/tests/",
"presentation": {
"reveal": "always",
"focus": true,
"panel": "shared",
}
},
{
"label": "SubmitCode",
"type": "shell",
"command": "cd ${fileDirname} && acc submit ${fileBasename} -- -l 4003",
"presentation": {
"reveal": "always",
"focus": true,
"panel": "shared",
},
},
]
}
//"-fsanitize=address,undefined",
の //
を消すと
ld: library not found for -lasan
collect2: error: ld returned 1 exit status
というエラーが出てしまいます。解消方法が分かる方は教えて頂けると幸いです。また、tasks.json
で下二つ(”label": "CheckTestCase" と "label": "SubmitCode")はサンプルのテストとコードの提出の為のタスクなので、"command"
の部分を使用するツールに合わせて書き換える必要があります。
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(LLDB) Debug",
"type": "lldb",
"request": "launch",
"program": "${fileDirname}/${fileBasenameNoExtension}",
"args": [],
"cwd": "${fileDirname}/",
"preLaunchTask": "g++ build active file",
}
]
}
これら4つの JSON ファイルをワークスペース直下の .vscode
フォルダに入れます。
(VSCode の設定はユーザー設定、ワークスペース設定、フォルダ設定と分かれていて、同じ設定を使い回すならユーザー設定で行う方が良くて、そうでないならワークスペースやフォルダの設定にすると良いでしょう。ワークスペースについては VSCode のワークスペース説明ページ を参照。)
これでデバッグができるようになります。デバッガーの使い方はVSCodeのこのページの後半で軽く説明されています。
以下は、次のセクションで導入する自動提出ツールを VSCode 上のキーボードショートカットから使う為の設定です。command
, args
の名前は上記 tasks.json
内の項目と対応しています。
[
{
"key": "ctrl+t",
"command": "workbench.action.tasks.runTask",
"when": "editorTextFocus",
"args": "CheckTestCase"
},
{
"key": "ctrl+s",
"command": "workbench.action.tasks.runTask",
"when": "editorTextFocus",
"args": "SubmitCode"
}
]
コマンドパレット Command + Shift + p
から Open Keyboard Shortcuts を選択して JSON ファイルに上記のように書き込みます。これで Ctrl + t
でサンプルに正答できているかどうかのテストができ、Ctrl + s
でコードの提出ができます。
3. AC Library の導入
AC Library は、日本最大の競プロサイト AtCoder で使える C++ のライブラリです。他のプログラミング言語にも有志で翻訳されています。
上記(セクション2)のように VSCode でも include path を指定し、AC Library 公式サイト の指示に従うと AC Library を(VSCode 上でも)エラー無く使うことができるようになりました。AC Library はgithub 上の AC Library のページ から、又は AtCoder 上の記事 から入手できます。
export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/opt/homebrew/include/
私の環境では workspace フォルダ直下に ac-library
フォルダを置き opt/homebrew/include/
下に atcoder
フォルダへのシンボリックリンクを置きました。
ln -s (path to the atcoder folder) /opt/homebrew/include/atcoder
(path to the atcoder folder) はご自身の環境に合わせてください。
4. 自動提出ツールの導入
検索してみると幾つかサンプルの正答チェックや自動提出のツールがあります。
以前 atcoder-cli を使っていたので、今回もインストールしました。(途中警告が出たので atcoder-tools をインストールしようとしたけれどインストールできなかったので atcoder-cli のインストールをやりきりました。)
atcoder-cli インストールガイド が公式のインストールガイドで、上記でも度々参照したサイトでも解説されています。
インストールしていくと多数の警告が出ましたが、多くは無視してもなんとかなりました。nvm をインストールする所で .zshrc
に書き込む必要がありました。
nvm のページ に従い、 ~/.zshrc
ファイルに以下のように書き込みます。
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
nvm.sh
が入った場所を探してファイルを移動させたようなさせなかったような…。
source ~/.zshrc
で書き込んだ設定を反映させます。
また、プリインストールされていた(それか Xcode と共にインストールされた?) python3 を使ったのですが、インストールした諸々が PATH の通っていない所に保存されたという趣旨の警告が出たので PATH を通す必要がありました。
再掲: mac でターミナルが zsh の場合のパスの通し方に関する一般的な解説のページ
VSCode でデフォルトで使われるターミナルを zsh にすることで、zsh の設定を通して使えるようにしたツールを VSCode 内のターミナルからも使えるようにすることができます。(コマンドが見つからないというエラーに悩んでいたら、ChatGPT が教えてくれました!)
VSCode の環境設定から terminal.integrated.shellArgs と検索して、Osx の項目でデフォルトのターミナルとして zsh を選択します。対応する JSON ファイルで
"terminal.integrated.defaultProfile.osx": "zsh",
と設定します。一応、これを実行する前によく分からないままこのページの手順を試していますが、必要無いかもしれません。
これでほぼ使えるようになりましたが、Ctrl + s
で提出しようとするとテストケースがダウンロードされているにも関わらず、参照しているディレクトリが異なるせいか警告が出ます。
[WARNING] the problem "https://atcoder.jp/contests/abc293/tasks/abc293_f" is specified to submit, but no samples were downloaded in this directory. this may be mis-operation
Are you sure? Please type "abcf"
他の方も同じ状況に直面したようです。(参考1)(参考2)
コード提出時に毎回数秒ロスしますが、許容範囲内ということにします…。
5. Chrome 拡張機能・ユーザースクリプト
AtCoder で便利な Google Chrome の拡張機能が幾つかあります。Chrome にログインすれば新しい環境でも以前と同じものを使えます。例えば私は以下の拡張機能を使っています。
-
Comfortable AtCoder
問題をドロップダウンリストで切り替える機能などがあります。 -
AtCoder Color Mark
ユーザー名の後ろにレートを示すマークが付きます。
Tampermonkey という拡張機能を使うと、Chrome の拡張機能にはなっていないようなユーザースクリプトというものを使うこともできます。これは新しい環境でインストールし直す必要があるようです。
-
AtCoder Easy Test v2
コード提出欄でそのままサンプルのテストなどができます。自動提出ツールに不具合がある場合などに非常に重宝します。 -
Tabbed AtCoder Editorial
異なる問題の解説のページを行き来しやすくします。 - cf-fast-submit 世界最大級の競プロのサイトCodeforces で問題ページから提出できるようになります。
その他、AtCoder で便利な拡張機能等へのリンク集です。
6. インストールしたもののバージョンまとめ
主なもの
ソフトウェア | バージョン |
---|---|
Homebrew | 4.0.6 |
GCC | 12.2.0 |
VSCode | 1.76.2 |
AC Library | 1.4 |
自動提出ツール関連
ソフトウェア | バージョン |
---|---|
nvm | 0.39.3 |
npm | 9.6.2 |
Node | 19.8.1 |
atcoder-cli | 2.2.0 |
online-judge-tools | 11.5.1 |
online-judge-api-client | 10.10.1 |
最後の項目は online-judge-tools と同時にインストールされたようです。
自動提出ツールのインストールに使った Python のバージョンは 3.9.6 です。
Chrome 拡張機能等のバージョンは省略。
7. 最後に: 環境構築の味方
検索しても分からず、身の回りに質問できる人がいない場合は、Twitter などで質問するというのも有効です。
しかし今回、最近話題の ChatGPT に質問することで一つエラーが解消したのが印象的でした。ただ、-fsanitizer
のエラーについても質問してみたら満足のいく回答が得られなかったので、今後のこういったツールの発展に期待します。
8. 追記: デバッガーでSTLコンテナの成分を覗けない問題
C++標準ライブラリ(STL)のコンテナ、例えば vector
の中身を確認しながらステップバイステップでプログラムを実行できるのがデバッガーの便利な所ですが、上記の手順ではコンテナの成分を確認できるようになっておりませんでした。
解決策をご存知の方は是非ご教示ください。記事を修正いたします。
以下、推測される原因や解決方法の方向性について記します。
基本的には、GCC は GDB でデバッグして、Clang は lldb でデバッグするようです。
この記事の現状のやり方では、GCC と lldb を使っているので相性が悪いのかもしれません。
[解決の方策 1]
GCC を使う方針は変えず、デバッガーを lldb から GDB に変える。
ただ、GDB はまだ Apple シリコンに対応していないようです。Graphical User Interface を用いないでターミナルで動くようなソフトも Rosetta で動かせるのでしょうか? MacPorts の GDB を使うという提案を見つけました。
先人達は GDB でのデバッグ環境構築で泥沼にはまったと記しているので、GDB を mac で使うのは難しいのかもしれません。
「デバッグ環境をGCCで環境構築しようとするとGDB周りで泥沼にはまった」
「gdbも試そうとしたのですが挫折しました…」
[解決の方策 2]
デバッガーに lldb を使う方針は変えず、Clang でエラーが出ないようにする。特に bits/stdc++.h
を使えるようにする。
現状、以下の Clang のタスクを
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "clang++ build active file",
"command": "clang++",
"args": [
"-std=c++17",
"-stdlib=libc++",
"-x",
"c++",
"-Wall",
"-Wextra",
"-Wshadow",
"-g",
"-fsanitize=undefined,address",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}",
"--debug",
"-I",
"./ac-library/atcoder/"
],
"group": "build"
}
]
}
preLaunchTask
として launch.json
から呼び出してから lldb しようとすると大量のエラーに見舞われます。エラーメッセージの例は
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__locale:1681:71: error: implicit instantiation of undefined template 'std::string'
_LIBCPP_INLINE_VISIBILITY string_type falsename() const {return do_falsename();}
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__locale:1727:12: error: implicit instantiation of undefined template 'std::string'
string _grouping;
などです。CPLUS_INCLUDE_PATH や tasks.json のインクルードパスに /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/ を指定してもエラーがたくさん出ることは変わりませんでした。例えば以下のエラーが出ました。
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/stdlib.h:148:34: error: unknown type name 'ldiv_t'
inline _LIBCPP_INLINE_VISIBILITY ldiv_t div(long __x, long __y) _NOEXCEPT {
参考