Detect & post-build for Intel C++ Compiler
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%\Intel\Compiler\C++\9.1。
裝了 ICC 後,compiler/linker 會被換成在 %ICPP_COMPILER%\..\..\ISELECT\bin\ 目錄下的 xicl6.exe 跟 xilink6.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%\..\..\ISELECT\bin\pickcmd.exe,會修改 registry,依據給定的參數,設定要使用的編譯器。也就是說,這個 utility 就是 MSVC IDE 裡的 Tools » Intel® C++ Compiler Selection Tool 的 command line 版本。
故 ICC 實際上是依據 registry 來決定要使用哪個 compiler/linker 的。所以觀察 registry 可以發現 HKEY_CURRENT_USER\Software\Intel\Intel Tools\Select Compiler\IDE\6 下面有這幾組 key:
HKEY_CURRENT_USER\Software\Intel\Intel Tools\Select Compiler\IDE\6
Path64 REG_SZ
MSVC_binary_dir REG_SZ C:\Program Files\Microsoft Visual Studio\VC98\Bin
Compiler REG_SZ 91032
Use_Intel_Cxx REG_DWORD 0x1
Languages REG_DWORD 0x1
Compiler_List REG_SZ 91032
HKEY_CURRENT_USER\Software\Intel\Intel Tools\Select Compiler\IDE\6\90032
bin REG_SZ C:\Program Files\Intel\Compiler\C++\9.0\IA32\bin
CHelpFile REG_SZ C:\Program Files\Intel\Compiler\C++\9.0\Docs\Main_cls.chm
Compiler_Interface REG_DWORD 0x0
Include REG_SZ C:\Program Files\Intel\Compiler\C++\9.0\IA32\include
Languages REG_DWORD 0x1
Lib REG_SZ C:\Program Files\Intel\Compiler\C++\9.0\IA32\lib
Name REG_SZ 9.0
HKEY_CURRENT_USER\Software\Intel\Intel Tools\Select Compiler\IDE\6\91032
bin REG_SZ C:\Program Files\Intel\Compiler\C++\9.1\IA32\bin
CHelpFile REG_SZ C:\Program Files\Intel\Compiler\C++\9.1\Docs\Main_cls.chm
Compiler_Interface REG_DWORD 0x0
Include REG_SZ C:\Program Files\Intel\Compiler\C++\9.1\IA32\include
Languages REG_DWORD 0x1
Lib REG_SZ C:\Program Files\Intel\Compiler\C++\9.1\IA32\lib
Name REG_SZ 9.1
HKEY_CURRENT_USER\Software\Intel\Intel Tools\Select Compiler\IDE\6\91064
bin REG_SZ C:\Program Files\Intel\Compiler\C++\9.1\Itanium\bin
Name REG_SZ 9.1
Lib REG_SZ C:\Program Files\Intel\Compiler\C++\9.1\Itanium\lib
Languages REG_DWORD 0x1
Include REG_SZ C:\Program Files\Intel\Compiler\C++\9.1\Itanium\include
Compiler_Interface REG_DWORD 0x0
CHelpFile REG_SZ C:\Program Files\Intel\Compiler\C++\9.1\Docs\Main_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 "HKCU\Software\Intel\Intel Tools\Select Compiler\IDE\6" \
/v Use_Intel_Cxx
! REG.EXE VERSION 3.0
HKEY_CURRENT_USER\Software\Intel\Intel Tools\Select Compiler\IDE\6
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選項時,會改向使用者詢問,將得到的字串,設給foo,而x後面的字串,則當作 prompt string 使用。因此,我們先執行bar,將結果存在暫存檔bar.tmp裡,然後用 redirect 將bar.tmp的內容,導給SET /p foo=,如此就可以將bar的執行結果,存在foo變數裡。 - 如 UNIX 的
grep工具一般,在一堆內容裡,找到符合某 pattern 的那些行 -
因為使用
reg.exe查詢 registry key 的時候,會額外印出許多不相干的內容,所以我們第一步就是要把顯示值的那一行抓出來。Windows 的 cmd.exe 有個指令叫
FIND,可以做到如grep的效果,只是沒有grep那麼強大。假設我們要抓Use_Intel_Cxx的值,我們可以這麼下指令:SHELL> SET ISELECT6=HKCU\Software\Intel\Intel Tools\Select Compiler\IDE\6 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或awk等強大的文字處理工具,而cmd.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=HKCU\Software\Intel\Intel Tools\Select Compiler\IDE\6 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 :POST_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 :POST_BUILD_ICC_END GOTO POST_BUILD_COMMON_BEGIN :POST_BUILD_MSVC_BEGIN ECHO Perform MSVC post-build steps: :POST_BUILD_MSVC_END GOTO POST_BUILD_COMMON_BEGIN :POST_BUILD_COMMON_BEGIN ECHO Perform common post-build steps: XCOPY lib\libFoo\include\foo_core.h %PREFIX%\include /Y /F /D :POST_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,而在 Debug mode 裡需要的是 libmmdd.dll,所以這個 script 吃一個參數,可以是 debug 或 release (大小寫不拘),以決定要 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
Random Posts
- None Found
Similar Posts
- None Found
3 Comments
难道你不是中国人?
wengyc,
您應該是要回《文章被抄.啼笑皆非》這篇文章吧,跑到這邊來,文不對題喔。
總之,回答您:我當然不是中(華人民共和)國人,我也不願意與中(華人民共和)國人成為同胞,那太丟臉了。
Jeff Hung
jeffhung
初看你的blog,觉得你的编程技术很不错,但看到你对wengyc的回复后
“wengyc,
您應該是要回《文章被抄.啼笑皆非》這篇文章吧,跑到這邊來,文不對題喔。
總之,回答您:我當然不是中(華人民共和)國人,我也不願意與中(華人民共和)國人成為同胞,那太丟臉了。
Jeff Hung”
我为你感到羞耻,我不想谈政治,也不想知道你是哪国人,更不想知道你是哪国的同胞(也许你是天上掉下来的),但是,你不应该因为几个人而去打击所有的中国人,知道吗?
3 Backlinks
參考資料: Command Line Registry Edits Using Unix shell's backquote functionality in DOS/Windows batch files Find from Rob van der Woude's Scripting Pages 內含於 Windows Support Tools 裡。 ↩ Windows 的 cmd.exe 有兩種模式:基本模式與擴充模式。擴充模式的內建指令功能較強,可以在 cmd 啟動時加上 /E 選項開啟,或是直接在 registry
的中文翻譯,很多地方都不太傳神。 ↩ [IMG ][IMG ]
[IMG ]剛剛發現,我的這篇《Detect & post-build for Intel C++ Compiler》,被一個中國人全文抄錄到他的 blog 裡。由於整個排版,仍然是原來的樣子,甚至連裡面的 footnotes,連結也都是連回到我的 blog,因此可以合理推斷,是被直接「複製/貼上」全文抄錄的。
Post a Comment