libxml2剖析(2):编译

    1、Linux/Unix下的编译
    内容整理自 http://xmlsoft.org/FAQ.html中一些部分。

    跟大多数UNIX库一样,libxml2的编译遵循以下“标准”:

[plain] view plain copy print ?
  1. gunzip -c xxx.tar.gz | tar xvf -  (或者tar xzvf libxml2-xxxx.tar.gz)  
  2. cd libxml2-xxxx  
  3. ./configure --help  
    查看各选项,然后编译并安装。

[plain] view plain copy print ?
  1. ./configure [possible options]  
  2. make  
  3. make install  
    安装后有可能还要重新运行ldconfig或类似工具以更新被安装的共享库列表。
    编译时Libxml2无需任何其他的库,正常的C ANSI API就足够了。然而在配置时如果libxml2检测到以下库将会使用它们:
    libz: 一个高可移植的、使用广泛的压缩库。
    iconv: 一个强大的字符编码转换库。缺省情况下它已经被包含到当前的glibc库中,因此在Linux上并不需要另外再安装。关于libiconv库,参见http://www.gnu.org/software/libiconv/。
    安装好之后,应用程序在进行编译和链接时需要知道libxml2的头文件和库文件位置。在上面运行./configure之后会生成一个小的Shell脚本xml2-config,它会被安装成为libxml2的一部分。用这个脚本可以获得需要的编译和链接标志。
    xml2-config --cflags: 打印预处理和编译标志,通常是"-I/usr/local/include/libxml2"。
    xml2-config --libs: 打印库链接信息,通常是"-L/usr/local/lib -lxml2 -lm -ldl"。
    在Makefile中通常是直接如下设置:
CFLAGS=`xml2-config --cflags`
LIBS=`xml2-config --libs`
    若要安装到自己指定的目录下,则在配置时使用"--prefix"开关,例如./configure --prefix /home/user/myxml/xmlinst {other configuration options},然后运行make和make install,libxml2库文件、头文件和二进制文件会安装到/home/user/myxml/xmlinst目录下,包括/home/user/myxml/xmlinst/lib,/home/user/myxml/xmlinst/include和/home/user/myxml/xmlinst/bin。为了使用这个“私有”库,你应该先把路径添加到PATH中,export PATH=/home/user/myxml/xmlinst/bin:$PATH。
    现在假设你有一个test.c程序,编译命令为:gcc `xml2-config --cflags --libs` -o test test.c。注意因为/home/user/myxml/xmlinst/bin被添加到了PATH,因此可以找得到xml2-config程序并运行它。
     2、Windows下的编译
    (1)编译libxml2

    若要在Windows下自行编译libxml2,libxml2-2.9.0\win32\Readme.txt中说明怎样通过命令行来编译,win32\VC10\下有Visual Studio 2010工程文件,可通过VS 2010来编译。下面介绍使用命令行的方式编译。
    第一步:配置源代码
    打开Visual Studio命令提示符,切换到libxml2-2.9.0\win32\下,运行cscript configure.js help,我们可以获得详细的配置选项帮助,包括XML处理器和Win32构建两大类选项。我们做以下配置:
    cscript configure.js iso8859x=yes iconv=no compiler=msvc cruntime=/MT debug=no prefix=c:\libxml2
    这个配置的意思是激活libxml2库的ISO8859X支持、不依赖于iconv库;使用MSVC编译器(缺省值)、C运行库链接选项为/MT、编译成Release版本(缺省值)、编译出来的库、头文件和相关工具安装到c:\libxml2下。
    链接选项/MT表示静态链接多线程版的C运行时函数库(libcmt.lib),编译器自动定义_MT宏。这样生成的程序不会依赖于动态库msvcrt.dll。另外/MD是动态链接多线程版本的C运行时库(msvcrt.dll,导入库为msvcrt.lib),编译器自动定义_MT和_DLL两个宏。/ML是静态链接单线程版本的C运行时库(libc.lib)。而/MTd, /MDd, /MLd选项使用对应调试版的C运行时库(libcmtd.lib, msvcrtd.dll, libcd.lib),编译器自动再多定义一个_DEBUG宏。它们包含了调试信息,并采用了一些保护机制以帮助发现错误,加强了对错误的检测,因此在运行性能方面比不上Release版本。
    第二步:编译
    nmake /f Makefile.msvc
    第三步:安装
    nmake /f Makefile.msvc install
    大功告成,在c:\libxml2下得到我们需要的libxml2库及相关测试工具。bin\下有动态库libxml2.dll及相关工具,lib\下有导入库libxml2.lib,及静态库版本libxml2_a.lib,include\下有导出的所有头文件。这个libxml2.dll不依赖于iconv和zlib(缺省配置是不使用zlib的),也不依赖于C运行库msvcrt.dll。
    Libxml2中默认的内码是UTF-8,所有使用libxml2处理的xml文件,必须首先显式或者默认的转换为UTF-8编码才能被处理。libxml2通常使用iconv库来进行各种编码的转换。这对于多国语言的xml解析至关重要,例如处理中文文档。
    Libiconv是GNU的字符编码转换库,使用LGPL许可,我们需要把它编译成动态链接库。当然我们也可以用其他的字符编码转换库,比如著名的ICU库(International Component for Unicode)。ICU是一个成熟的、广泛应用的库,是IBM与开源组织合作研究,基于"IBM公共许可证"的用于支持软件国际化的开源项目。ICU实现了对数字、日期、货币等设置的国际化支持,提供了强大的BIDI算法,对阿拉伯语和希伯来语等BiDi语言提供了完善的支持。ICU分为ICU4J和ICU4C,分别对应Java和C/C++平台。这个库是在一种无限制的开放源码许可证下发布的,这使它可以用在各种商业程序、开源程序中。详细可参考官网http://site.icu-project.org/。
    ICU库比较大,binary就有几十MB以上。一般使用libiconv就足够了,这也是libxml2缺省使用编码转换库。在编译libxml2时缺省是会链接到iconv库的,我们上面选择的是不链接。最好把libxml2和libiconv编译成两个独立的库,这样就不会有依赖。
    如果选择链接iconv,可以先编译好libiconv库(参看下面介绍),把libiconv-2.dll、导出库iconv.lib和iconv.h拷贝到libxml2-2.9.0\win32\下(或者在配置时指定搜索路径以找到这些文件),然后做如下配置:
    cscript configure.js iso8859x=yes compiler=msvc cruntime=/MT debug=no prefix=c:\libxml2
    这里表示编译时链接到iconv库。注意libxml2链接时搜索的库名为iconv.lib,要确保导入库的名称为iconv.lib,不是的话要改成这个名。编译好之后libxml2.dll依赖于libiconv-2.dll,因此两者需要一起发布。
     (2)编译libiconv
    根据官方http://www.gnu.org/software/libiconv/的描述,libiconv和libcharset库以及它们的头文件使用LGPL授权,而iconv程序使用GPL授权,我们只编译libiconv动态库(如果你的应用有闭源需求的话,不能使用GPL的代码)。
    从ftp://ftp.gnu.org/gnu/libiconv/下载最新的libiconv-1.14.tar.gz,新版libiconv已经不提供用Visual Studio的nmake来编译的脚本了,只能用Cygwin、MinGW之类的环境进行编译,参看README.woe32的描述。我们使用MinGW环境来编译,先安装MinGW(http://www.mingw.org/)到C:\MinGW。在libiconv-1.14中,lib\和libcharset\下是libiconv和libcharset库的源码,需要导出的头文件iconv.h在include\下,为iconv.h.in或iconv.h.build.in。它们使用LGPL许可,而其他目录下的源码是GPL许可,我们也无需使用。
    打开MinGW Shell,切换到libiconv-1.14\下。其编译过程与Linux下的编译类似。./configure(收集系统信息,这需要等待比较长的时间),然后make编译。之后可以在libiconv-1.14\include\下看到生成的头文件iconv.h,在libiconv-1.14\lib\.libs\下看到编译好的libiconv-2.dll,但因为使用GNU GCC编译,没有生成.lib导入库。如果需要隐式链接的话,就需要为该dll产生一个导入库。注意导入库是不能跨编译器使用的,在mingw中导入库是.a格式(libiconv.dll.a),而MSVS中则是.lib格式。
    我们可以用Visual Studio的dumpbin.exe和lib.exe工具制作DLL的导入库文件。先用dumpbin.exe输出DEF文件,然后修改DEF文件使之符合语法,再用lib.exe生成.lib文件。
    首先生成dll库的def文件:dumpbin /EXPORTS libiconv-2.dll > iconv.def。输出的iconv.def内容如下:
[plain] view plain copy print ?
  1. Microsoft (R) COFF/PE Dumper Version 10.00.30319.01  
  2. Copyright (C) Microsoft Corporation.  All rights reserved.  
  3.   
  4.   
  5. Dump of file libiconv-2.dll  
  6.   
  7. File Type: DLL  
  8.   
  9.   Section contains the following exports for libiconv-2.dll  
  10.   
  11.     00000000 characteristics  
  12.     50C075A5 time date stamp Thu Dec 06 18:38:29 2012  
  13.         0.00 version  
  14.            1 ordinal base  
  15.           13 number of functions  
  16.           13 number of names  
  17.   
  18.     ordinal hint RVA      name  
  19.   
  20.           1    0 00013000 _libiconv_version  
  21.           2    1 00011478 aliases2_lookup  
  22.           3    2 00011350 aliases_lookup = _locale_charset  
  23.           4    3 000120F0 iconv_canonicalize  
  24.           5    4 00011908 libiconv  
  25.           6    5 00011958 libiconv_close  
  26.           7    6 000114C0 libiconv_open = _libiconv_set_relocation_prefix  
  27.           8    7 00011970 libiconv_open_into  
  28.           9    8 00012580 libiconv_relocate  
  29.          10    9 000124C0 libiconv_set_relocation_prefix  
  30.          11    A 00011EB8 libiconvctl  
  31.          12    B 00011FC8 libiconvlist  
  32.          13    C 00012350 locale_charset  
  33.   
  34.   Summary  
  35.   
  36.         1000 .CRT  
  37.         1000 .bss  
  38.         1000 .data  
  39.         1000 .debug_abbrev  
  40.         1000 .debug_aranges  
  41.         1000 .debug_frame  
  42.        1F000 .debug_info  
  43.         6000 .debug_line  
  44.        18000 .debug_loc  
  45.         1000 .debug_pubnames  
  46.         1000 .debug_pubtypes  
  47.         4000 .debug_ranges  
  48.         1000 .debug_str  
  49.         1000 .edata  
  50.         4000 .eh_frame  
  51.         1000 .idata  
  52.        CD000 .rdata  
  53.         1000 .reloc  
  54.         1000 .rsrc  
  55.        12000 .text  
  56.         1000 .tls  
    ionv.def需要修改成符合模块定义文件的语法。我们只要关心DEF文件定义里的两个段:LIBRARY段和EXPORTS段。LIBRARY段指出DLL的内部名,这里为libiconv-2;EXPORTS段指出导出的函数或数据。上面数据的中间部分列出了所有导出函数和数据,真正需要是orinal列(表示编号)和name列。把iconv.def修改成如下内容:
[plain] view plain copy print ?
  1. LIBRARY      "libiconv-2"  
  2.   
  3. EXPORTS  
  4. _libiconv_version  @1  
  5. aliases2_lookup    @2  
  6. aliases_lookup = _locale_charset  @3  
  7. iconv_canonicalize  @4  
  8. libiconv  @5  
  9. libiconv_close  @6  
  10. libiconv_open = _libiconv_set_relocation_prefix  @7  
  11. libiconv_open_into  @8  
  12. libiconv_relocate  @9  
  13. libiconv_set_relocation_prefix  @10  
  14. libiconvctl  @11  
  15. libiconvlist  @12  
  16. locale_charset  @13  
    最后生成导入库:lib /machine:ix86 /def:iconv.def,生成iconv.lib和iconv.exp文件。发布时包含libiconv-2.dll, iconv.lib和iconv.h即可,并且注意它是使用LGPL许可的。
     (3)编译zlib
    如果你还需要zlib来进行压缩的话(这是可选的),还可以编译出zlib库,这个在我的zlib系列文章中有详细介绍。直接用nmake运行zlib-1.2.7\win32\Makefile.msc即可。如下:
    nmake -f win32/Makefile.msc LOC="-DASMV -DASMINF" OBJA="inffas32.obj match686.obj"
    编译出zlib1.dll及导入库zlib.lib,头文件为zconf.h和zlib.h。
    好了。如果你嫌上述编译过程麻烦,也可以直接从http://www.zlatkovic.com/libxml.en.html处获取libxml2的编译好的Windows版本,这个版本只提供了头文件、库文件和dll,不包含源代码、例子程序和文档。若用于解析xml,通常只需要下载libxml2库、iconv库和zlib库就行了。现在提供的版本为libxml2-2.7.8.win32.zip,iconv-1.9.2.win32.zip和zlib-1.2.5.win32.zip。注意iconv-1.9.2.win32.zip中包含有iconv.dll动态库和iconv.exe程序,而iconv.exe是GPL授权的,要特别注意版权的问题。最好的方式还是像上面一样自行编译libiconv库。
    若要使用Libxml2,可在Visual Studio 2010中新建一个解决方案Libxm2Learning,里面有一个空的Libxml2Learning Win32控制台项目,设置成Release模式。把libxml2.dll及导入库libxml2.lib拷贝到解决方案的Release目录下,所有头文件(C:\libxml2\include目录)拷贝到解决方案根目录下。另外把libiconv-2.dll,iconv.lib和iconv.h也拷过来。新建应用程序项目时要设置项目属性。在"C/C++ --->Additional Include Directories"中添加..\include\libxml2,以便能使用libxml2库的头文件。"Code Generation"中的运行时库最好设置成/MT,"Linker--->Additional Dependencies"中添加..\Release\libxml2.lib和..\Release\iconv.lib以便链接时能找到导入库。


    附:用MSVC编译最新的libiconv
    最新的libiconv已经不提供用Visual C/C++和nmake来编译了。如果一定要用Visual C++来编译libiconv,我们需要对源码的编译过程做一些分析,然后对源代码做一些调整。
    libiconv-1.14中,lib\和libcharset\下是libiconv和libcharset库的源码,需要导出的头文件iconv.h在include\下,为iconv.h.in或iconv.h.build.in。它们使用LGPL许可,而其他目录下的源码是GPL许可,我们也无需使用。iconv.h.build.in中包含一些@开头的符号变量,这是为了提高可移植性而定义的,它们用来收集一些特定于系统的编译构建信息。在用./configure配置源码时,这些变量会被configure扫描到的相关值替换掉,从而生成iconv.h。在Visual C++下编译时没有配置的过程,因此我们需要了解这些变量的含义。
    1)@HAVE_VISIBILITY@: 表示是否有__visibility__属性。这是GCC/G++的__attribute__属性扩展,表示导出符号的可见性,这在创建.so动态库时有用。__attribute__((__visibility__("default")))表示导出的符号可见,而__attribute__ ((visibility("hidden")))可以防止从动态库中导出符号。若用Microsoft Visual C++编译器来编译,显然值为0。
    2)@DLL_VARIABLE@: 表示后面声明的变量是否是从外部DLL中导入过来的。如果是,本变量值设成__declspec (dllimport);如果不是,则设成空。显然这里可以设成空的。参考configure.ac中对本变量的处理。
    在Windows系统中,__declspec(dllimport)用于声明一个导入函数,是说这个函数是从别的DLL(或EXE)导入,我要用。不使用__declspec(dllimport)导入外部函数也能正确编译代码,但使用__declspec(dllimport)使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于DLL中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨DLL边界的函数调用中。但是,必须使用__declspec(dllimport)才能导入DLL中使用的变量。
    __declspec(dllexport)用于声明一个导出函数,是说这个函数要从本DLL导出,要给别人用,一般用在开发DLL时。使用这个声明可以省掉在DEF文件中手工定义导出哪些函数的工作。当然,如果你的DLL里全是C++的类的话,你无法在DEF里指定导出的函数,只能用__declspec(dllexport)导出类。
    3)@EILSEQ@: EILSEQ是errno.h中定义的一个错误码,表示非法字节序列(例如在MBCS字符串)。 一些系统像SunOS 4没有EILSEQ,一些系统像BSD/OS在其他头文件中有EILSEQ,在这些系统上就需要定义我们自己的EILSEQ。Linux, Windows上有这个错误码,因此无需自己再定义,本变量值为空。
    4)@ICONV_CONST@: 表示在"char"前面是否加上"const",例如在Linux系统上./configure后可以看到这个变量替换为空,即使用"char**",而不是"const char**"。Windows上值为"const"。
    5)@USE_MBSTATE_T@: 是否使用宽字符处理函数wchar.h。若要支持多种字符集,本变量值为1。mbstate_t类型用于C的字符编码转换函数和C++的字符编码转换类,C/C++标准没有规定具体的数据类型。mbstate_t类型的对象表示转换函数mbrlen, mbrtowc, mbsrtowcs, wcrtomb或wcsrtombs的一个转换状态(这些函数在wchar.h中)。可用mbsinit()函数测试是否处于读入新字符状态。举个例子,有些字符集包含了一类带有状态编码的字符,如很多Latin语言系带有的重音符号(accent),通常用一个编码表示一种accent,其后面的一个字符表示需要添加accent的字符(因此如果需要输出accent本身,后面还得加另外一个字符,比如空格)。这种字符就是表示进入到某种状态,用mbstate_t类型来描述这种状态。
    6)@BROKEN_WCHAR_H@: wchar.h是否有bug。在带桌面C工具包的HP Tru64 UNIX系统上有一个bug,包含<wchar.h>之前必须先包含<stdio.h>。在BSD/OS 4.0.1上有一个bug,必须在包含<wchar.h>之前必须先包含stddef.h>,<stdio.h>和<time.h>。本变量标记这样的特殊情况。Linux, Windows上本变量值为0。
    7)@HAVE_WCHAR_T@: 是否使用宽字符处理。若上面使用了宽字符处理,本变量值为1。
    详细的构建过程可参考一篇文章:http://www.codeproject.com/Articles/302012/How-to-Build-libiconv-with-Microsoft-Visual-Studio
    在Libxm2Learning解决方案下添加一个Win32 DLL空项目libiconv,设置编译成Release版本。
    第一步:拷贝源文件
    把libiconv-1.14\lib\下的"relocatable.h", "relocatable.c"和"iconv.c"拷贝到libiconv项目目录中。
    把libiconv-1.14\libcharset\lib\localcharset.c拷贝到libiconv项目目录中。
    把libiconv-1.14\libcharset\include\localcharset.h.build.in拷贝到项目中,并重命名为localcharset.h。
    把libiconv-1.14\windows\libiconv.rc拷贝到项目目录。
    在libiconv项目下创建子目录include,用来放各个头文件。
    把libiconv-1.14\include\iconv.h.build.in拷贝到项目include子目录下,并重命名为iconv.h。
    把libiconv-1.14\config.h.in拷贝到项目include子目录下,并重命名为config.h。
    把libiconv-1.14\lib\下的所有头文件(*.h)和模块定义文件(*.def)拷贝到项目include子目录下。
    第二步:设置项目属性
    把项目根目录下的两个源文件、两个文件和一个资源文件导入到libiconv项目视图中。设置项目属性。
    "VC++ Directories"设置:在"Include Directories"中添加include子目录。
    "Preprocessor Definitions"设置:添加宏"BUILDING_LIBICONV"和"BUILDING_LIBCHARSET"。
    "Code Generation"设置:"Runtime Library"最好设置为/MT,以静态链接多线程版的C运行时函数库。
    第四步:调整源代码
    1)iconv.h: 按照上面介绍把各个@...@变量替换成相应值。更改函数导入导出声明,即把以下针对gcc/g++的导出定义

[cpp] view plain copy print ?
  1. #if @HAVE_VISIBILITY@ && BUILDING_LIBICONV   
  2. #define LIBICONV_DLL_EXPORTED __attribute__((__visibility__("default")))   
  3. #else   
  4. #define LIBICONV_DLL_EXPORTED   
  5. #endif  
    修改为针对MSVC的定义:
[cpp] view plain copy print ?
  1. #if BUILDING_LIBICONV   
  2. #define LIBICONV_DLL_EXPORTED __declspec(dllexport)   
  3. #elif USING_STATIC_LIBICONV   
  4. #define LIBICONV_DLL_EXPORTED   
  5. #else   
  6. #define LIBICONV_DLL_EXPORTED __declspec(dllimport)   
  7. #endif  
    2)libiconv.rc: 在前面添加以下定义。
[cpp] view plain copy print ?
  1. #define PACKAGE_VERSION_MAJOR       1   
  2. #define PACKAGE_VERSION_MINOR       14   
  3. #define PACKAGE_VERSION_SUBMINOR    0   
  4. #define PACKAGE_VERSION_STRING      "1.14"  
    3)localcharset.c: 删掉或注释掉以下几行(76-79行)。
[cpp] view plain copy print ?
  1. /* Get LIBDIR.  */  
  2. /* 
  3. #ifndef LIBDIR 
  4. # include "configmake.h" 
  5. #endif 
  6. */  
    4)iconv.c: 把其中的ICONV_CONST宏(247行)改成const。
    5)localcharset.h: 更改其中函数导出导入的声明。把以下针对gcc/g++的导出定义
[cpp] view plain copy print ?
  1. #if @HAVE_VISIBILITY@ && BUILDING_LIBCHARSET   
  2. #define LIBCHARSET_DLL_EXPORTED __attribute__((__visibility__("default")))   
  3. #else   
  4. #define LIBCHARSET_DLL_EXPORTED   
  5. #endif  
    修改为针对MSVC的定义:
[cpp] view plain copy print ?
  1. #ifdef BUILDING_LIBCHARSET   
  2. #define LIBCHARSET_DLL_EXPORTED __declspec(dllexport)   
  3. #elif USING_STATIC_LIBICONV   
  4. #define LIBCHARSET_DLL_EXPORTED   
  5. #else   
  6. #define LIBCHARSET_DLL_EXPORTED __declspec(dllimport)   
  7. #endif  
    6)config.h: 删掉或注释掉以下内容(29-30行),否则会导致Windows上没有EILSEQ定义,从而代码编译通不过。
[cpp] view plain copy print ?
  1. /* Define as good substitute value for EILSEQ. */  
  2. /*#undef EILSEQ*/  
    好了,一切准备就绪,编译完之后就可以在解决方案的Release目录中看到libiconv.dll了,以及导入库libiconv.lib(若让libxml2链接时使用,则需要改名为iconv.lib)。发布时还包含头文件iconv.h。
    打开Visual Studio的命令提示符,运行dumpbin /EXPORTS libiconv.dll,可以看到DLL导出的各个函数名。
[plain] view plain copy print ?
  1. Microsoft (R) COFF/PE Dumper Version 10.00.30319.01  
  2. Copyright (C) Microsoft Corporation.  All rights reserved.  
  3.   
  4.   
  5. Dump of file libiconv.dll  
  6.   
  7. File Type: DLL  
  8.   
  9.   Section contains the following exports for libiconv.dll  
  10.   
  11.     00000000 characteristics  
  12.     50B5EAAA time date stamp Wed Nov 28 18:42:50 2012  
  13.         0.00 version  
  14.            1 ordinal base  
  15.            9 number of functions  
  16.            9 number of names  
  17.   
  18.     ordinal hint RVA      name  
  19.   
  20.           1    0 000E8DC0 _libiconv_version = __libiconv_version  
  21.           2    1 00012810 iconv_canonicalize = _iconv_canonicalize  
  22.           3    2 000120C0 libiconv = _libiconv  
  23.           4    3 00012100 libiconv_close = _libiconv_close  
  24.           5    4 00011D90 libiconv_open = _libiconv_open  
  25.           6    5 00012120 libiconv_open_into = _libiconv_open_into  
  26.           7    6 00012420 libiconvctl = _libiconvctl  
  27.           8    7 00012600 libiconvlist = _libiconvlist  
  28.           9    8 00012970 locale_charset = _locale_charset  
  29.   
  30.   Summary  
  31.   
  32.         3000 .data  
  33.        CE000 .rdata  
  34.         2000 .reloc  
  35.         1000 .rsrc  
  36.        19000 .text    
    原始的libiconv使用LGPL许可,因此这个调整的libiconv也是LGPL许可,在使用时必须仔细注意授权的问题。如果动态链接libiconv.dll,则你的应用程序无需发布源码。如果编译成静态库并使用静态链接,则你的应用程序或者发布源码,或者发布目标文件(*.obj),以便其他人通过静态链接LGPL的libiconv来重新生成应用程序。

你可能感兴趣的:(libxml2剖析(2):编译)