Detect & post-build for Intel C++ Compiler
, Programming by
jeffhung @ March 14th, 2007
Intel C++ Compiler (ICC) 所編譯出來的執行檔,其速度總令人驚豔,且可以近乎無痛地與 Microsoft Visual C++ (MSVC) 的開發經驗整合,是升級開發工具的好選擇。然而,在 Windows 上,ICC 需要搭配隨 ICC 附上的 libmmd.dll,才能夠執行,再加上 ICC 和 MSVC 是可以隨時切換的,因此,在 build system 的設計上,必須要有些特別的設計,以便處理這種 IDE 不會處理到的事。
我們用的 MSVC 是 VC6,需要偵測目前使用的編譯器,是 MSVC 還是 ICC,如果是 ICC 的話,還要偵測是哪一個版本,然後,依據偵測到的版本,作對應的 post-build 的動作。例如,若是使用 ICC 的話,就將正確版本的 libmmd.dll,複製到目標目錄下。
以下假設 ICC 是安裝在 ICPP_COMPILER 這個目錄下,如 %ProgramFiles%IntelCompilerC++9.1。
裝了 ICC 後,compiler/linker 會被換成在 %ICPP_COMPILER%....ISELECTbin 目錄下的 跟
xicl6.exexilink6.exe,這只是一個只是一個 front-end,ICC 會依據目前的設定,啟動對應的 compiler/linker 進行編譯或連結。
依據隨附 ICC 安裝的文件裡的「Intel® C++ Compiler Documentation » Building Applications » Building Applications with Microsoft Visual Studio 6.0 » Selecting the Intel® C++ Compiler Using makefile」這一篇文章:
… The Makefile Utility provides users with the ability to switch between the Intel C++ Compiler and the Microsoft Visual C++ Compiler without requiring changes to their makefiles. This utility modifies the registry, so exported makefiles only call
cl.exe(CPP = cl).
這個所謂的 Makefile Utility 指的就是 %ICPP_COMPILER%....ISELECTbinpickcmd.exe,會修改 registry,依據給定的參數,設定要使用的編譯器。也就是說,這個 utility 就是 MSVC IDE 裡的 Tools » Intel® C++ Compiler Selection Tool 的 command line 版本。
故 ICC 實際上是依據 registry 來決定要使用哪個 compiler/linker 的。所以觀察 registry 可以發現 HKEY_CURRENT_USERSoftwareIntelIntel ToolsSelect CompilerIDE6 下面有這幾組 key:
HKEY_CURRENT_USERSoftwareIntelIntel ToolsSelect CompilerIDE6
Path64 REG_SZ
MSVC_binary_dir REG_SZ C:Program FilesMicrosoft Visual StudioVC98Bin
Compiler REG_SZ 91032
Use_Intel_Cxx REG_DWORD 0x1
Languages REG_DWORD 0x1
Compiler_List REG_SZ 91032
HKEY_CURRENT_USERSoftwareIntelIntel ToolsSelect CompilerIDE690032
bin REG_SZ C:Program FilesIntelCompilerC++9.0IA32bin
CHelpFile REG_SZ C:Program FilesIntelCompilerC++9.0DocsMain_cls.chm
Compiler_Interface REG_DWORD 0x0
Include REG_SZ C:Program FilesIntelCompilerC++9.0IA32include
Languages REG_DWORD 0x1
Lib REG_SZ C:Program FilesIntelCompilerC++9.0IA32lib
Name REG_SZ 9.0
HKEY_CURRENT_USERSoftwareIntelIntel ToolsSelect CompilerIDE691032
bin REG_SZ C:Program FilesIntelCompilerC++9.1IA32bin
CHelpFile REG_SZ C:Program FilesIntelCompilerC++9.1DocsMain_cls.chm
Compiler_Interface REG_DWORD 0x0
Include REG_SZ C:Program FilesIntelCompilerC++9.1IA32include
Languages REG_DWORD 0x1
Lib REG_SZ C:Program FilesIntelCompilerC++9.1IA32lib
Name REG_SZ 9.1
HKEY_CURRENT_USERSoftwareIntelIntel ToolsSelect CompilerIDE691064
bin REG_SZ C:Program FilesIntelCompilerC++9.1Itaniumbin
Name REG_SZ 9.1
Lib REG_SZ C:Program FilesIntelCompilerC++9.1Itaniumlib
Languages REG_DWORD 0x1
Include REG_SZ C:Program FilesIntelCompilerC++9.1Itaniuminclude
Compiler_Interface REG_DWORD 0x0
CHelpFile REG_SZ C:Program FilesIntelCompilerC++9.1DocsMain_cls.chm
從這幾組 registry 的結構以及實驗的結果,我們可以看出,ICC 係依據 Use_Intel_Cxx 這一個 key,來決定要不要用 ICC,以及依據 Compiler 這一個 key,決定要用哪一個版本的 ICC。故,如果我們能夠在 command line 下查詢這些 registry key,我們就可以寫出 BATCH 檔,依據設定作對應的 post-build 動作。
我們可以使用 reg.exe 這一支程式[
1],在 command line 查詢 registry key,如下:
SHELL> REG QUERY "HKCUSoftwareIntelIntel ToolsSelect CompilerIDE6"
/v Use_Intel_Cxx
! REG.EXE VERSION 3.0
HKEY_CURRENT_USERSoftwareIntelIntel ToolsSelect CompilerIDE6
Use_Intel_Cxx REG_DWORD 0x1
好,既然 registry 裡的資訊抓得出來了,那剩下的就只剩下 BATCH 檔的程式設計技巧。我們會需要下列技術:
- 如 UNIX shell 的 backquote 一般,取得程式執行的結果,並存入變數
-
BATCH 並沒有如 UNIX shell 的 backquote 的功能,也就是另外執行程式,將程式的輸出,轉變成目前的 script 部份內容,進而存入變數。為了取得 registry 裡的值,放入變數以便作後續的處理,我們必須在 BATCH 裡,模擬出 backquote 的功能。
要達到 backquote 的效果,解法很簡單,但非常 tricky。假設我們要達到
set foo `bar`的效果,我們可以這麼寫:bar > bar.tmp
SET /p foo=<bar.tmp在
SET使用/p選項時,會改向使用者詢問,將得到的字串,設給,而
foox後面的字串,則當作 prompt string 使用。因此,我們先執行,將結果存在暫存檔
barbar.tmp裡,然後用 redirect 將bar.tmp的內容,導給
SET /p foo=,如此就可以將bar的執行結果,存在變數裡。
foo - 如 UNIX 的
grep工具一般,在一堆內容裡,找到符合某 pattern 的那些行 -
因為使用
reg.exe查詢 registry key 的時候,會額外印出許多不相干的內容,所以我們第一步就是要把顯示值的那一行抓出來。Windows 的 cmd.exe 有個指令叫
FIND,可以做到如grep的效果,只是沒有那麼強大。假設我們要抓
grepUse_Intel_Cxx的值,我們可以這麼下指令:SHELL> SET ISELECT6=HKCUSoftwareIntelIntel ToolsSelect CompilerIDE6
SHELL> REG QUERY "%ISELECT6%" /v Use_Intel_Cxx 2>>NUL | FIND "REG_DWORD"
Use_Intel_Cxx REG_DWORD 0x1其中,裡面的
2>>NUL負責把 stderr 的東西丟掉。 - 如 UNIX 的
cut工具一般,依據位置,取出某行文字的部份內容 -
這個要用到
cmd.exe變數展開的延伸功能[
2]。在cmd.exe裡打HELP SET我們可以得到下面的說明[
3]:您也可以為擴充功能指定子字串。
%PATH:~10,5%
這將會擴充 PATH 環境變數,然後只使用擴充結果的第 11 個(位移 10)字元
後的 5 個字元如果長度未指定,將會預設為上次使用的變數值。如果數字(位
移或長度)是負數,使用的數字將會是環境變數的長度加上位移或指定長度。假設含有
Use_Intel_Cxx的值的那一整行,已經在ICC_USE_INTEL_CXX變數裡了,我們就可以用下面的指令,擷取出
0x1的部份:SET ICC_USE_INTEL_CXX=%ICC_USE_INTEL_CXX:~28%
很可惜的,Windows 下預設沒有如
sed、cut或等強大的文字處理工具,而
awkcmd.exe的變數延展功能又太過陽春,不能依靠如 regular pattern 的方式處理,因此這裡的數字必須自行計算並寫死。還好,以目前的應用來說,使用固定的數字不會有任何的問題。
28
有了以上的技術,我們就可以全部組裝起來,達到我們想要的功能。最後完整的程式如下:
@ECHO OFF
SET PREFIX=..FooProj
IF "%1"=="" GOTO USAGE
IF "%1"=="/?" GOTO USAGE
IF "%1"=="/h" GOTO USAGE
IF "%1"=="/help" GOTO USAGE
IF "%1"=="-h" GOTO USAGE
IF "%1"=="--help" GOTO USAGE
SET CONFIGURATION=%1
IF "%CONFIGURATION%"=="debug" GOTO CONFIGURATION_CHECK_OK
IF "%CONFIGURATION%"=="release" GOTO CONFIGURATION_CHECK_OK
:CONFIGURATION_CHECK_FAILED
ECHO ERROR: Bad [configuration].
GOTO USAGE
:CONFIGURATION_CHECK_OK
SET ISELECT6=HKCUSoftwareIntelIntel ToolsSelect CompilerIDE6
SET TMP_FILE=tmp-backquote.txt
REM Determine whether ICC is used
REG QUERY "%ISELECT6%" /v Use_Intel_Cxx 2>>NUL | FIND "REG_DWORD" > %TMP_FILE%
SET /P ICC_USE_INTEL_CXX=<%TMP_FILE%
SET ICC_USE_INTEL_CXX=%ICC_USE_INTEL_CXX:~28%
IF %ICC_USE_INTEL_CXX%==0x1 GOTO POST_BUILD_ICC_BEGIN
IF %ICC_USE_INTEL_CXX%==0x0 GOTO POST_BUILD_MSVC_BEGIN
OST_BUILD_ICC_BEGIN
ECHO Perform ICC post-build steps:
REM Get ICC version code, which is part of next reg key to query
REG QUERY "%ISELECT6%" /v Compiler 2>>NUL | FIND "REG_SZ" > %TMP_FILE%
SET /P ICC_VER_CODE=<%TMP_FILE%
SET ICC_VER_CODE=%ICC_VER_CODE:~20%
REG QUERY "%ISELECT6%%ICC_VER_CODE%" /v bin 2>>NUL | FIND "REG_SZ" > %TMP_FILE%
SET /P ICC_BIN_PATH=<%TMP_FILE%
SET ICC_BIN_PATH=%ICC_BIN_PATH:~15%
IF "%CONFIGURATION%"=="debug" XCOPY "%ICC_BIN_PATH%libmmdd.dll" %PREFIX%bin /Y /F /D
IF "%CONFIGURATION%"=="release" XCOPY "%ICC_BIN_PATH%libmmd.dll" %PREFIX%bin /Y /F /D
OST_BUILD_ICC_END
GOTO POST_BUILD_COMMON_BEGIN
OST_BUILD_MSVC_BEGIN
ECHO Perform MSVC post-build steps:
OST_BUILD_MSVC_END
GOTO POST_BUILD_COMMON_BEGIN
OST_BUILD_COMMON_BEGIN
ECHO Perform common post-build steps:
XCOPY liblibFooincludefoo_core.h %PREFIX%include /Y /F /D
OST_BUILD_COMMON_END
GOTO QUIT
:USAGE
ECHO Usage: %0 [configuration]
ECHO ----
ECHO [configuration] could be Debug or Release.
GOTO QUIT
:QUIT
IF EXIST %TMP_FILE% DEL /Q %TMP_FILE%
EXIT /B
由於在 Release mode 我們需要的是 libmmd.dll,而在 mode 裡需要的是
Debuglibmmdd.dll,所以這個 script 吃一個參數,可以是 或
debugrelease (大小寫不拘),以決定要 XCOPY 哪一個 DLL。
參考資料:
- Command Line Registry Edits
- Using Unix shell’s backquote functionality in DOS/Windows batch files
- Find from Rob van der Woude’s Scripting Pages