2016年4月26日:「シェルでやる方法」を追記。
Windowsの開発環境には、環境変数を設定するバッチファイルが提供されていて、そのバッチファイルを実行すると PATH
とかの環境変数が設定されるというパターンがたまにある。(スタートメニューに「環境変数を設定済みのシェルを起動する」ショートカットを登録するパターンの方がもっと多い気がするが)
MSYS2からこういう開発環境(というか、Visual C++)を叩きたい。もちろん、cmd.exe
を使って
$ cmd //c "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.bat"
みたいなことをすれば vcvars64.bat
(Visual C++用に環境変数を設定するバッチファイル)を実行すること自体はできるが、そこで設定された環境変数は呼び出し元のシェルに反映されない。
設定後の環境変数の内容を出力するには、 vcvars64.bat
を実行後にMSYS2の env
コマンドを実行すれば良い。
$ echo "\"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.bat\" && env" | xargs cmd //c
わざわざ echo + xargs を使っているのは、シェルの引数の変換規則(エスケープとか)がよく分からなくなったからだ。
env
の出力結果を sed
とかで加工すれば bash
から source
するのに適した形式になりそうだが、あまりシェルでいろいろやろうとすると訳が分からなくなりそうなので、スクリプト言語(Lua)を使うことにする。Luaは
$ pacman -S mingw-w64-x86_64-lua
でインストールできる。
で、書いたLuaスクリプトが以下だ。
#!cmd /c C:\\msys64\\mingw64\\bin\\lua.exe
-- 文字列をシェル用にエスケープする
local function escape(s)
return (string.gsub(s, "[\\|&;()<> \t]", "\\%1"))
end
-- Windows での os.tmpname() はアレっぽい
local TMPDIR = os.getenv("TMP")
local tmpname = string.format("%s\\vc14-shell-%s.sh", TMPDIR, os.tmpname():match("([.%w_-]+)$"))
local f = io.open(tmpname, "w")
local p = assert(io.popen [[cmd /c "( "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.bat" && C:\msys64\usr\bin\env )"]])
for l in p:lines() do
local name, value = l:match("^([%w_]+)=(.*)$")
if name and name ~= "PWD" then
f:write(string.format("export %s=%s\n", name, escape(value)))
end
end
p:close()
f:write("export MSYSTEM=VC14\n")
f:close()
io.write(string.format("source %s\n", escape(tmpname)))
これを vcvars64-msvc14.lua
みたいなファイル名で保存して、
$ $(./vcvars64-msvc14.lua)
と実行すれば、実行中のシェルで Visual C++ のコマンド(cl
等)が叩けるようになる。
shebang を #!cmd /c ...
みたいな形式にしているのは、 #!/mingw64/bin/lua
と書くと $(./vcvars64-msvc14.lua)
と実行した時になぜか output is not a tty
とかいうエラーが出たからだ(cmd /c
で実行すれば出ない)。
cl.exe
でファイルをコンパイルしてみた例:
$ cat test.cpp
#include <iostream>
#include <string>
class Hoge
{
public:
virtual ~Hoge() {}
explicit operator int() { return score(); }
virtual int score() = 0;
};
class Piyo : public Hoge
{
public:
virtual ~Piyo() {}
virtual int score() override { return 334; }
explicit operator std::string() {
using namespace std::literals; /* C++14 */
return "Hello world!"s;
}
};
int main(int argc, char *argv[]) {
Piyo piyo;
// std::string message = piyo; -> an error
std::cout << std::string(piyo) << std::endl;
std::cout << static_cast<int>(piyo) << std::endl;
std::cout << "__cplusplus=" << __cplusplus << std::endl;
return 0;
}
$ cl //EHsc //nologo test.cpp
test.cpp
$ ./test.exe
Hello world!
334
__cplusplus=199711
シェルでやる方法(2016年4月26日追記)
Lua を使わないで頑張ってシェルで完結させた方が何かと都合がいいことを悟ったので、頑張ってシェルで描き直してみた。
あと、当初の処理だと MANPATH
, INFOPATH
, PKG_CONFIG_PATH
が Windows 仕様になってしまうので、それらは上書きしないようにした。
.bash_profile
あたりに
function vcvars64.bat() { source <(cmd /c "( \"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.bat\" && C:\msys64\usr\bin\env )" | grep -P "^(?!PWD=)(?!(MAN|INFO|PKG_CONFIG_)PATH=)\\w+=" | sed -e "s/[\\|&;()<> \t]/\\\&/g" | sed -re "s/^(.+)$/export \1/g") }
と書いて $ vcvars64.bat
を叩けば cl
等が使えるようになる。3行で済むので、他のバッチファイルに対応するシェル関数も容易に用意できる。多分かなり bash に依存している。
あと、 .bash_profile
かどこかに
cmd /c "chcp 65001 > NUL"
と書いておけば、 MSBuild
や csc
の出力が UTF-8 になって文字化けしなくなる。
> あと、 .bash_profile かどこかに
> cmd /c “chcp 65001 > NUL”
> と書いておけば、 MSBuild や csc の出力が UTF-8 になって文字化けしなくなる。
上記の点につきまして、筆者様の投稿日以後に状況が変化していると思われます。
筆者様においては既知の事項かと思われますが、
当方が確認したところ以下の通りでしたので共有させていただきます。
1. ~/.bash_profile に cmd /c “chcp 65001 > NUL” を書き加える
2. C:\msys64\msys2_shell.cmd -mingw64 を実行する
3. cmd.exe が立ち上がる (ここでは mintty で bash が立ち上がることが期待値と存じます)