Visual C++ 実行に必要な環境変数をいつでも使えるように予め設定しておく方法 (vcvarsall.bat で設定される変数をユーザー環境変数に自動反映するツールを作りました!【完璧バージョン】)


お便利ツールを作ってみました!使用した(ら)感想を是非お聞かせください

※ この記事で紹介しているスクリプトを文字コードUTF-8で保存してしまうと実行がエラーになりますので、必ず Shift-JISで保存してご利用ください。(@emadurandal さんのコメントでご指摘いただきました)

このツール(バッチファイル)を作った動機は Cygwin 内で Visual C++ のコンパイラ(CL.exe) や NMAKE.exe や MSBUILD.exe を簡単に使いたいということでした。Cygwin.bat を呼び出す前に vcvarsall.bat を呼び出すようなバッチファイルを自作してそれをダブルクリックして Cygwin を起動するというワザもなくはないんですが、MSYS2 や 通常のコマンドプロンプトで使う場合にも CL.exe NMAKE.exe MSBUILD.exe 等が使えれば便利ですのでツール作成に挑戦してみました。

  • 環境変数のアンインストールに対応しました。
  • Windowsのメニューを一切操作せずに32bit版または64bit版の環境変数をインストールできます。
  • Windowsのメニューを一切操作せずに32bit版←→64bit版の環境変数切り替えができます。
  • INCLUDE LIB LIBPATH PATH にユーザーが独自に設定してある項目(パス)は保持されます。

早速ツールを使ってみる

前提事項

Visual Studio Community 2017 (C++) がインストール済の環境を想定しています。

インストール項目のチェック

Visual Studio Community 2017 をインストールする際に、以下の項目に追加でチェックを入れておく必要があります。

  • 「ワークロード」の「C++ によるデスクトップ開発」
  • 「個別のコンポーネント」の「MFCとALTのサポート (x86とx64)」
  • 「個別のコンポーネント」の「WIndows 8.1 SDK」
  • 「個別のコンポーネント」の「Windows Universal CRT SDK」
  • 「個別のコンポーネント」の「標準ライブラリモジュール」

バッチファイルの実行

添付したバッチファイルを実行(エクスプローラーからダブルクリックしてください)するとユーザー環境変数に、INCLUDE LIB LIBPATH PATH が自動的に設定されます(一度実行しておけば設定が残りますので、ログアウトやOSの再起動しても再実行する必要がありません。このバッチファイルを実行した後に、Cygwin や MSYS2、またはコマンドプロンプト等を起動すればvcvarsall.batの実行なしで VC++のコマンド(CL NMAKE MSBUILD 等)が使えるようになります)。

「希望する操作を数字で選択してください ⇒ 0=環境変数を消去  32=32bit環境を設定  64=64bit環境を設定  空文字=何もしない」
「数字(0 or 32 or 64)を入力: 」
と表示されますので、キーボードから 0 32 64 入力せずにENTER のいずれかを選択します。

  • すでに 32bit環境を設定 を実行した後であっても(消去処理が自動的に行われますので) そのまま(上書きで) 64bit環境を設定 を実行してかまいません。64bit環境から32bit環境に切り替える場合にも、わざわざ 環境変数を消去 を呼び出す必要はありません(上書きで実行してください)。

vc2017-env.bat
@if(0)==(0) echo off
setlocal enabledelayedexpansion
set VCVARSALL=C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat

set INCLUDE=
set LIB=
set LIBPATH=
set PATH=%SystemRoot%\System32;%SystemRoot%\System32\Wbem
call "%VCVARSALL%" x86
set INCLUDE32=%INCLUDE%
set LIB32=%LIB%
set LIBPATH32=%LIBPATH%
echo [INCLUDE32]
for %%A in ("%INCLUDE32:;=";"%") do (if not "%%~A"=="" echo %%~A)
echo [LIB32]
for %%A in ("%LIB32:;=";"%") do (if not "%%~A"=="" echo %%~A)
echo [LIBPATH32]
for %%A in ("%LIBPATH32:;=";"%") do (if not "%%~A"=="" echo %%~A)
echo [VCPATH32]
set VCPATH32=
for %%A in ("%PATH:;=";"%") do (
  if /i "%%~A" neq "%SystemRoot%\System32" (
    if /i "%%~A" neq "%SystemRoot%\System32\Wbem" (
      echo %%~A
      if "!VCPATH32!" equ "" (set VCPATH32=%%~A) else (set VCPATH32=!VCPATH32!;%%~A)
    )
  )
)
::echo %VCPATH32%

set INCLUDE=
set LIB=
set LIBPATH=
set PATH=%SystemRoot%\System32;%SystemRoot%\System32\Wbem
call "%VCVARSALL%" amd64
set INCLUDE64=%INCLUDE%
set LIB64=%LIB%
set LIBPATH64=%LIBPATH%
echo [INCLUDE64]
for %%A in ("%INCLUDE64:;=";"%") do (if not "%%~A"=="" echo %%~A)
echo [LIB64]
for %%A in ("%LIB64:;=";"%") do (if not "%%~A"=="" echo %%~A)
echo [LIBPATH64]
for %%A in ("%LIBPATH64:;=";"%") do (if not "%%~A"=="" echo %%~A)
echo [VCPATH64]
set VCPATH64=
for %%A in ("%PATH:;=";"%") do (
  if /i "%%~A" neq "%SystemRoot%\System32" (
    if /i "%%~A" neq "%SystemRoot%\System32\Wbem" (
      echo %%~A
      if "!VCPATH64!" equ "" (set VCPATH64=%%~A) else (set VCPATH64=!VCPATH64!;%%~A)
    )
  )
)
::echo %VCPATH64%

set INCLUDE=
set LIB=
set LIBPATH=
set PATH=%SystemRoot%\System32;%SystemRoot%\System32\Wbem
cscript.exe //nologo //E:JScript "%~f0"
endlocal
exit /b
goto :EOF
@end

if (!Array.prototype.contains) {
  Array.prototype.contains = function(x) {
    for (var i=0; i<this.length; i++) {
      if (this[i]==x) return true;
    }
    return false;
  };
}

if (!Array.prototype.remove) {
  Array.prototype.remove = function(x) {
    var result = [];
    for (var i=0; i<this.length; i++) {
      if (this[i]==x) continue;
      result.push(this[i]);
    }
    return result;
  };
}

if (!Array.prototype.removeAll) {
  Array.prototype.removeAll = function(a) {
    var result = [];
    for (var i=0; i<this.length; i++) {
      if (a.contains(this[i])) continue;
      result.push(this[i]);
    }
    return result;
  };
}

var opts = {};
opts.penv         = new ActiveXObject("WScript.Shell").Environment("PROCESS");
opts.uenv         = new ActiveXObject("WScript.Shell").Environment("USER");
opts.include32    = opts.penv.item("INCLUDE32").split(";").remove("");
opts.include64    = opts.penv.item("INCLUDE64").split(";").remove("");
opts.lib32        = opts.penv.item("LIB32").split(";").remove("");
opts.lib64        = opts.penv.item("LIB64").split(";").remove("");
opts.libpath32    = opts.penv.item("LIBPATH32").split(";").remove("");
opts.libpath64    = opts.penv.item("LIBPATH64").split(";").remove("");
opts.path32       = opts.penv.item("VCPATH32").split(";").remove("");
opts.path64       = opts.penv.item("VCPATH64").split(";").remove("");

var selectOk = false;
var num;
while(!selectOk) {
  WScript.StdOut.WriteLine("希望する操作を数字で選択してください ⇒ 0=環境変数を消去  32=32bit環境を設定  64=64bit環境を設定  空文字=何もしない");
  WScript.StdOut.Write("数字(0 or 32 or 64)を入力: ");
  var line = WScript.StdIn.ReadLine();
  num = line.replace(/(^\s+)|(\s+$)/g, ""); // トリム
  switch(num) {
    case '0': case '32': case '64': case '':
      selectOk = true;
      break;
  }
}
switch(num) {
  case '0':
    clearEnv(opts);
    msgbox("環境変数を消去しました");
    break;
  case '32':
    clearEnv(opts);
    put32Env(opts);
    msgbox("32bit環境を設定しました");
    break;
  case '64':
    clearEnv(opts);
    put64Env(opts);
    msgbox("64bit環境を設定しました");
    break;
  default:
    msgbox("何もしませんでした");
    break;
}
WScript.Quit();
function msgbox(msg) {
  var shell = new ActiveXObject("WScript.Shell");
  shell.Popup(msg, 0, "Windows Script Host", 0);
}
function initEnv(opts) {
  opts.include = opts.uenv.item("INCLUDE").split(";").remove("");
  opts.lib     = opts.uenv.item("LIB").split(";").remove("");
  opts.libpath = opts.uenv.item("LIBPATH").split(";").remove("");
  opts.path    = opts.uenv.item("PATH").split(";").remove("");
}
function clearEnv(opts) {
  initEnv(opts);
  opts.uenv.item("INCLUDE") = opts.include.removeAll(opts.include32).removeAll(opts.include64).join(";");
  if (opts.uenv.item("INCLUDE") == "") opts.uenv.remove("INCLUDE");
  opts.uenv.item("LIB") = opts.lib.removeAll(opts.lib32).removeAll(opts.lib64).join(";");
  if (opts.uenv.item("LIB") == "") opts.uenv.remove("LIB");
  opts.uenv.item("LIBPATH") = opts.libpath.removeAll(opts.libpath32).removeAll(opts.libpath64).join(";");
  if (opts.uenv.item("LIBPATH") == "") opts.uenv.remove("LIBPATH");
  opts.uenv.item("PATH") = opts.path.removeAll(opts.path32).removeAll(opts.path64).join(";");
  if (opts.uenv.item("PATH") == "") opts.uenv.remove("PATH");
}
function put32Env(opts) {
  initEnv(opts);
  opts.uenv.item("INCLUDE") = opts.include.concat(opts.include32).join(";");
  opts.uenv.item("LIB") = opts.lib.concat(opts.lib32).join(";");
  opts.uenv.item("LIBPATH") = opts.libpath.concat(opts.libpath32).join(";");
  opts.uenv.item("PATH") = opts.path.concat(opts.path32).join(";");
}
function put64Env(opts) {
  initEnv(opts);
  opts.uenv.item("INCLUDE") = opts.include.concat(opts.include64).join(";");
  opts.uenv.item("LIB") = opts.lib.concat(opts.lib64).join(";");
  opts.uenv.item("LIBPATH") = opts.libpath.concat(opts.libpath64).join(";");
  opts.uenv.item("PATH") = opts.path.concat(opts.path64).join(";");
}

CygwinでMSBUILDを使う場合の注意事項

Visual Studio 2015 で使う場合

スクリプトの3行目を以下のように変更します。

  • 変更前: set VCVARSALL=C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat

  • 変更後: set VCVARSALL=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat

としてください。

以上です。

Cygwin Terminal や MSYS2 のターミナルで Visual C++ のコマンドが使えるようになったら、シフトJISのコマンド出力が文字化けするのでお時間があれば以下の記事もご覧になってください:

Cygwin/Msys2 で Windows標準の ping や nslookup を使う (nkf を使わないでシフトJISの出力をUTF-8に変換する方法)