最近总是被一些莫名其妙的小问题困扰,深切的体会到了何为“阎王好斗,小鬼难缠”。
因为研究Codeblocks这个开源IDE,在编译源码时候,发现其中部分插件引用了Boost库,于是乎就想把Boost库编译一下。
我的编译器是TDM-GCC5.1.0,用了有一段时间了,早些时候为了跳出微软的圈儿决定学习和使用wxWidgets,就用了这个编译器。本来想着,下个代码编译一下,然后添加引用,多么简单的一个事儿!
然而我还是在阴沟里翻船了,就这么简单的一个事儿,遇上了麻烦。
下载Boost库、配置GCC环境这些基本操作就不表了,在编译bjam编译工具时候,遇上了麻烦。
按照GettingStart的说明,执行脚本bootstrap.bat gcc,然后坐等工具编译,然后,就弹出了以下两个错误:
E:\Workspace\Librarys\Boost\boost_1_68_0>bootstrap.bat gcc
Building Boost.Build engine
execnt.c: In function 'try_wait_callback':
execnt.c:856:5: warning: implicit declaration of function 'UnregisterWait' [-Wimplicit-function-declaration]
UnregisterWait( slot->wait_handle );
^
execnt.c: In function 'register_wait':
execnt.c:876:9: warning: implicit declaration of function 'RegisterWaitForSingleObject' [-Wimplicit-function-declaration]
RegisterWaitForSingleObject( &cmdtab[ job_id ].wait_handle,
^
C:\Users\Polarix\AppData\Local\Temp\ccI8RYOz.o:execnt.c:(.text+0x139a): undefined reference to `UnregisterWait'
C:\Users\Polarix\AppData\Local\Temp\ccI8RYOz.o:execnt.c:(.text+0x146b): undefined reference to `RegisterWaitForSingleObject'
collect2.exe: error: ld returned 1 exit status
Failed to build Boost.Build engine.
Please consult bootstrap.log for further diagnostics.
E:\Workspace\Librarys\Boost\boost_1_68_0>
错误很常见,分别是调用函数没有显式声明和函数未定义。上网查了一下,UnregisterWait和RegisterWaitForSingleObject函数均为WindowsAPI,所以这两个问题应该是头文件未包含和系统函数库未引用导致。
函数未显式声明可以暂时不理,这种警告一般不会影响程序的运行,至于函数未定义,加上对应的系统库引用不就好了!
至少我是这样想的。
于是我找到了编译脚本,位于\tools\build\src\engine下的build.bat文件,然后又顺藤摸瓜,找到了config_toolset.bat文件,最终找到了这样一段处理。
if NOT "_%BOOST_JAM_TOOLSET%_" == "_gcc_" goto Skip_GCC
set "BOOST_JAM_CC=gcc -DNT"
set "BOOST_JAM_OPT_JAM=-o bootstrap\jam0.exe"
set "BOOST_JAM_OPT_MKJAMBASE=-o bootstrap\mkjambase0.exe"
set "BOOST_JAM_OPT_YYACC=-o bootstrap\yyacc0.exe"
set "_known_=1"
:Skip_GCC
库的引用应该就加在这里了,于是上微软的网站,找到这两个函数,关键信息如下:
Header:winbase.h (include Windows.h)
Library:Kernel32.lib
DLL:Kernel32.dll
小事儿,将库引用加在BOOST_JAM_CC参数后面就行了,像这样:
set "BOOST_JAM_CC=gcc -DNT -lkernel32"
满心欢喜的进入控制台,再次编译……
问题依旧……
这下我就懵了,这个错误就这么回事儿,还能有别的问题报这个错误?
毫无头绪之际,本着之前编程时“无警告编译”的习惯,我决定先看看这两个警告,把他俩先干掉。于是我找到了报错的execnt.c文件,然后在确认了这个文件没有包含winbase.h后,我在头部加入了winbase.h的包含,再编译一下试试。
这时,诡异的现象发生了!控制台上居然又出现了这两句话:
E:\Workspace\Librarys\Boost\boost_1_68_0>bootstrap.bat gcc
Building Boost.Build engine
execnt.c: In function 'try_wait_callback':
execnt.c:856:5: warning: implicit declaration of function 'UnregisterWait' [-Wimplicit-function-declaration]
UnregisterWait( slot->wait_handle );
^
execnt.c: In function 'register_wait':
execnt.c:876:9: warning: implicit declaration of function 'RegisterWaitForSingleObject' [-Wimplicit-function-declaration]
RegisterWaitForSingleObject( &cmdtab[ job_id ].wait_handle,
^
这个不应该啊,头文件包含了,这又是C文件,应该不存在同名函数重载的现象。于是乎我打开了TDM-GCC的include文件夹,决定看一下这两个函数的声明,是这样的:
#if (_WIN32_WINNT >= 0x0500)
WINBASEAPI BOOL WINAPI UnregisterWait(HANDLE);
WINBASEAPI BOOL WINAPI UnregisterWaitEx(HANDLE,HANDLE);
#endif
#if (_WIN32_WINNT >= 0x0500)
WINBASEAPI BOOL WINAPI RegisterWaitForSingleObject(PHANDLE,HANDLE,WAITORTIMERCALLBACK,PVOID,ULONG,ULONG);
WINBASEAPI HANDLE WINAPI RegisterWaitForSingleObjectEx(HANDLE,WAITORTIMERCALLBACK,PVOID,ULONG,ULONG);
#endif
函数声明的类型和调用的地方一致,应该没问题,但是这个警告真的好诡异,于是乎,我突然发现了另一个东西,那就是这两个函数包着的纳个宏定义:
#if (_WIN32_WINNT >= 0x0500)
这个宏定义没有问题,因为在刚才查找资料时,微软的在线文档也明确表示:
Minimum supported client:Windows XP [desktop apps only]
Minimum supported server:Windows Server 2003 [desktop apps only]
但是如果这个宏定义不满足,那么这两个函数的声明就相当于没有,这个和编译产生的警告是符合的,那么系统函数的定义也可能未被包含!
想到这,我觉得我可能找到了问题的突破口,于是我用_WIN32_WINNT这个宏名在编译脚本及参数文件中搜索了一圈,发现只有在指定编译器为微软的VC系列时候,这个宏会被全局定义,在GCC和Mingw时,并没有这个定义。
问题似乎找到了,于是我将config_toolset.bat文件,中BOOST_JAM_CC参数的定义修改为:
set "BOOST_JAM_CC=gcc -DNT -D_WIN32_WINNT=0x0501"
再次编译。
之前的警告和错误都没有了,但是编译时显示:
Failed to build Boost.Build engine.
Please consult bootstrap.log for further diagnostics.
工具还是没有生成,没关系,进入\tools\build\src\engine目录,单独编译工具,这样可以看到更多回显信息。
执行build.bat gcc后,屏幕回显信息如下:
E:\Workspace\Librarys\Boost\boost_1_68_0\tools\build\src\engine>.\bootstrap\jam0 -f build.jam --toolset=gcc "--toolset-root= "
...found 161 targets...
...updating 2 targets...
[COMPILE] bin.ntx86\b2.exe
execnt.c: In function 'try_wait_callback':
execnt.c:856:5: warning: implicit declaration of function 'UnregisterWait' [-Wimplicit-function-declaration]
UnregisterWait( slot->wait_handle );
^
execnt.c: In function 'register_wait':
execnt.c:876:9: warning: implicit declaration of function 'RegisterWaitForSingleObject' [-Wimplicit-function-declaration]
RegisterWaitForSingleObject( &cmdtab[ job_id ].wait_handle,
^
C:\Users\Polarix\AppData\Local\Temp\ccx8A2Ia.o:execnt.c:(.text+0x2f0): undefined reference to `UnregisterWait'
C:\Users\Polarix\AppData\Local\Temp\ccx8A2Ia.o:execnt.c:(.text+0x1845): undefined reference to `RegisterWaitForSingleObject'
collect2.exe: error: ld returned 1 exit status
错误信息只截取了最重要的部分,不是全部,从这里可以看出,依然是之前的问题,只不过发生在另一次编译中,使用的是另一个文件build.jam。打开这个文件,可以找到以下内容。
toolset gcc gcc : "-o " : -D
: -pedantic -fno-strict-aliasing
[ opt --release : [ opt --symbols : -g : -s ] -O3 ]
[ opt --debug : -g -O0 -fno-inline ]
[ opt --profile : -O3 -g -pg ]
-I$(--python-include) -I$(--extra-include) -Wno-long-long
: -L$(--python-lib[1]) -l$(--python-lib[2]) ;
同样的,宏定义加在第二行处,变为:
toolset gcc gcc : "-o " : -D
: -pedantic -fno-strict-aliasing -D_WIN32_WINNT=0x0501
[ opt --release : [ opt --symbols : -g : -s ] -O3 ]
[ opt --debug : -g -O0 -fno-inline ]
[ opt --profile : -O3 -g -pg ]
-I$(--python-include) -I$(--extra-include) -Wno-long-long
: -L$(--python-lib[1]) -l$(--python-lib[2]) ;
保存,再次编译。
E:\Workspace\Librarys\Boost\boost_1_68_0\tools\build\src\engine>build.bat gcc
###
### Using 'gcc' toolset.
###
E:\Workspace\Librarys\Boost\boost_1_68_0\tools\build\src\engine>if exist bootstrap rd /S /Q bootstrap
E:\Workspace\Librarys\Boost\boost_1_68_0\tools\build\src\engine>md bootstrap
E:\Workspace\Librarys\Boost\boost_1_68_0\tools\build\src\engine>gcc -DNT -D_WIN32_WINNT=0x0501 -o bootstrap\jam0.exe command.c compile.c constants.c debug.c execcmd.c execnt.c filent.c frames.c function.c glob.c hash.c hdrmacro.c headers.c jam.c jambase.c jamgram.c lists.c make.c make1.c object.c option.c output.c parse.c pathnt.c pathsys.c regexp.c rules.c scan.c search.c subst.c timestamp.c variable.c modules.c strings.c filesys.c builtins.c md5.c class.c cwd.c w32_getreg.c native.c modules/set.c modules/path.c modules/regex.c modules/property-set.c modules/sequence.c modules/order.c
E:\Workspace\Librarys\Boost\boost_1_68_0\tools\build\src\engine>.\bootstrap\jam0 -f build.jam --toolset=gcc "--toolset-root= " clean
...found 1 target...
...updating 1 target...
...updated 1 target...
E:\Workspace\Librarys\Boost\boost_1_68_0\tools\build\src\engine>.\bootstrap\jam0 -f build.jam --toolset=gcc "--toolset-root= "
...found 161 targets...
...updating 2 targets...
[COMPILE] bin.ntx86\b2.exe
[COPY] bin.ntx86\bjam.exe
...updated 2 targets...
E:\Workspace\Librarys\Boost\boost_1_68_0\tools\build\src\engine>exit /b 0
E:\Workspace\Librarys\Boost\boost_1_68_0\tools\build\src\engine>
0错误,0警告通过。
此时再回到Boost的根目录下,重新执行bootstrap.bat gcc,不再有错误和警告,命令执行完成后,Boost的根目录下多出b2.exe和bjam.exe两个文件,这就是Boost的编译工具。同时,控制台回显:
E:\Workspace\Librarys\Boost\boost_1_68_0>bootstrap.bat gcc
Building Boost.Build engine
Bootstrapping is done. To build, run:
.\b2
To adjust configuration, edit 'project-config.jam'.
Further information:
- Command line help:
.\b2 --help
- Getting started guide:
http://boost.org/more/getting_started/windows.html
- Boost.Build documentation:
http://www.boost.org/build/doc/html/index.html
之后的操作步骤就和网上说的大相径庭,请大家自行搜索参照吧。