这是一份使用 vs2008 和 CMake 料理 libphonenumber 的食谱。
准备原料
- vs2008
- CMake-3.14.2 下载
- libphonenumber-release-8.10.9 下载
- protobuf-release-2.5.0 下载
- icu4c-4.8.1 下载
- googletest-release-1.6.0 下载
- Java Runtime:jre-8u201-windows-x64.exe 下载
- boost
假设工作目录在 d:\libs,将各个压缩包解压至该目录下。后面编译生成的文件都放在默认位置,需要整理目录的将对应路径替换即可。
加工
gtest
定位到
googletest-release-1.6.0/msvc
文件夹,打开 getst-md.sln 进行编译,-md 就是动态链接的 crt 库,静态链接就用不带 md 的 sln 文件,别点错了。编译后在
googletest-release-1.6.0/msvc/gtest-md/Debug
目录下生成 gtestd.lib 文件。
protobuf
定位到
protobuf-2.5.0/vsprojects
目录,因为和上面 gtest 的项目不同,这里只有一个以 /MD 配置好的解决方案文件,所以上面 gtest 也要选择 /MD 模式的解决方案。打开 protobuf.sln 直接开始编译即可。文件在
protobuf-2.5.0/vsprojects/Debug
中。估计是项目生成依赖有问题,一次性编译所有项目时部分会失败,多编译几次找到依赖后自然就成功了。
Icu
使用预编译的 vs2010 版,配上 2010 的运行库即可直接食用。
Boost
使用预编译的库,解压后摆好。libboost 的几个压缩包解压出来的 lib 可以整理到一起,boost_1_44_0 作为 include 目录单独放在 d:\libs 目录下即可。
D:\libs\boost_1_44_0\ D:\libs\libboost-vc90-mt-gd-1_44\libboost_date_time-vc90-mt-gd-1_44.lib D:\libs\libboost-vc90-mt-gd-1_44\libboost_system-vc90-mt-gd-1_44.lib D:\libs\libboost-vc90-mt-gd-1_44\libboost_thread-vc90-mt-gd-1_44.lib
不同 crt 库链接方式对应的 boost 库:
- MTd = mt-s
- MT = mt-sgd
- MDd = mt-gd
- MD = mt
详细可以看这里。
另外也可在 CMake 中分别指定 Boost_LIBRARY_DIR_RELEASE 和 Boost_LIBRARY_DIR_DEBUG 来代替单一的 BOOST_LIBRARYDIR。
libphonenumber
打开cmake-gui.exe,source code 路径填入
d:/libs/libphonenumber-8.10.9/cpp
build binaries 路径填入
d:/libs/libphonenumber-8.10.9/cpp/build(输出目录,也可以填任意其他路径)
其中 boost 的相关配置项需要手动加上。
BOOST_LIBRARYDIR d:/libs/libboost-vc90-mt-gd-1_44 BOOST_INCLUDEDIR d:/libs/boost_1_44_0
额外添加 Boost_FIND_REQUIRED 可以使 CMake 在查找 boost 库失败时给出详细提示,方便排查。
之后就能顺利的配置、生成 sln 解决方案了。
点击“configure”后选择编译器为 vs2008(vc90),重复“点击 configure” – “填入配置项”直到配置成功,最后结果如下:
BUILD_GEOCODER √ BUILD_STATIC_LIB √ Boost_FIND_REQUIRED √ CMAKE_CONFIGURATION_TYPES Debug;Release;MinSizeRel;RelWithDebInfo CMAKE_INSTALL_PREFIX d:/libs/libphonenumber-8.10.9/cpp/build GTEST_INCLUDE_DIR d:/libs/googletest-release-1.6.0/include GTEST_LIB d:/libs/googletest-release-1.6.0/msvc/gtest-md/Debug/gtestd.lib GTEST_SOURCE_DIR d:/libs/googletest-release-1.6.0/src ICU_I18N_INCLUDE_DIR d:/libs/icu4c-4_8_1_1-Win32-msvc10/include ICU_I18N_LIB d:/libs/icu4c-4_8_1_1-Win32-msvc10/lib/icuin.lib ICU_UC_INCLUDE_DIR d:/libs/icu4c-4_8_1_1-Win32-msvc10/include ICU_UC_LIB d:/libs/icu4c-4_8_1_1-Win32-msvc10/lib/icuuc.lib JAVA_BIN C:/Program Files/Java/jre1.8.0_201/bin/java.exe PROTOBUF_INCLUDE_DIR d:/libs/protobuf-2.5.0/src PROTOBUF_LIB d:/libs/protobuf-2.5.0/vsprojects/Debug/libprotobuf.lib PROTOC_BIN d:/libs/protobuf-2.5.0/vsprojects/Debug/protoc.exe USE_BOOST √(不勾也行,就是没有线程安全性了) USE_ICU_REGEXP √ USE_ALTERNATE_FORMATS √ USE_RE2 x USE_STD_MAP x USE_LITE_METADATA x
编译错误
- 编译 geocoding 项目需要的头文件 dirent.h 可在这里下载一份,然后放到
libphonenumber-8.10.9/tools/cpp/src
目录下。
- geocoding 和 generate_geocoding_data 项目的预处理器定义中加入 COMPILER_MSVC 来避免找不到头文件 stdint.h 的错误
- log10 二义性:改成 log10f。
- 编译时跳 (unsigned)(c + 1) <= 256 断言失败(Debug):在 generate_geocoding_data.cc L.189 和 L.190 处将 std::isspace(XXX) 改为 std::isspace((int)(unsigned char)XXX)。
- 因为 vs2008 不支持 c++11,编译时 phonenumbermatcher.cc 中用到了 std::unique_ptr 会有报错,将 705 行处 std::unique_ptr 改为 boost::scoped_ptr,并在前面加上
#include <boost/scoped_ptr.hpp>。
- 提示 phonenumberutil.cc 中 PhoneNumberUtil::kMinLengthForNsn 等类静态常量重定义:将 57~60 行这四个静态常量改为枚举即可。
- phonenumberutil_test.cc 中使用 EXPECT_NE / EXPECT_EQ 宏对迭代器进行比较产生模板推导错误,原因在于模板推断时将迭代器类型推断为容器类型,之后便开始调用它的 begin / end 方法导致错误。
可以改为直接判断结果是否为 true,如将EXPECT_NE(types.find(PhoneNumberUtil::FIXED_LINE), types.end());
改为
EXPECT_TRUE(types.find(PhoneNumberUtil::FIXED_LINE) != types.end());
或干脆注释掉。 - 还有个 geocoding-shared 的链接错误需要在项目中加入
libphonenumber-8.10.9/cpp/src/phonenumbers
目录和
libphonenumber-8.10.9/cpp/src/phonenumbers/utf
目录下的 c/cc 文件(但要排除 regexp_adapter_re2.cc、metadata.cc、lite-meta.cc,会导致符号重定义),另外还要加上
libphonenumber-8.10.9/cpp/src/phonenumbers/base/strings/string_piece.cc
的引用。
这些都改掉之后就可以顺利编译了。
Ex: 模板推导错误的原因估计在于 vc90 中将构造函数也作为类型参与推导了,结果造成了错误。样例如下,在 vs2008 中两个都会被推导为 1
typedef int IsContainer; typedef char IsNotContainer; template <class C> IsContainer IsContainerTest( int /* dummy */, typename C::iterator* /* it */ = NULL, typename C::const_iterator* /* const_it */ = NULL ) { return 1; } template <class C> IsNotContainer IsContainerTest( long /* dummy */ ) { return '\0'; } class con { public: class const_iterator { public: const_iterator() {} }; class iterator : public const_iterator { public: iterator() {} }; }; int main() { int res1 = IsContainerTest< con::iterator >(0); // vc90 -> 1, vc100 -> 0 int res3 = IsContainerTest< con >(0); // vc90 -> 1, vc100 -> 1 std::cout << res1<< res3 ; return 0; }
尝菜
#include <iostream> #include <string> #include "phonenumbers/phonenumber.h" #include "phonenumbers/phonenumberutil.h" using i18n::phonenumbers::PhoneNumberUtil; using i18n::phonenumbers::PhoneNumber; int _tmain(int argc, _TCHAR* argv[]) { PhoneNumberUtil *phoneUtil = PhoneNumberUtil::GetInstance(); //初始化一个电话号码 PhoneNumber pn; pn.set_country_code(86); pn.set_national_number(13478808311); //判断电话号码是否有效 std::cout << std::boolalpha << phoneUtil->IsValidNumber(pn) << std::endl; //判断电话号码所在的地区 std::string region; phoneUtil->GetRegionCodeForNumber(pn, ®ion); std::cout << region << std::endl; std::string name; phoneUtil->GetNationalSignificantNumber(pn, &name); std::cout << name << std::endl; //获取某个国家的国字区号 PhoneNumber example; phoneUtil->GetInvalidExampleNumber("CN", &example); std::cout << example.country_code() << std::endl; return 0; }
然后加上 include 目录和附加的 lib 目录,以及导入库。
include
d:/libs/icu4c-4_8_1_1-Win32-msvc10/include;d:/libs/libphonenumber-8.10.9/cpp/src;d:/libs/protobuf-2.5.0/src;d:/libs/boost_1_44_0;
libs
d:/libs/protobuf-2.5.0/vsprojects/Debug;d:/libs/icu4c-4_8_1_1-Win32-msvc10/lib;d:/libs/googletest-release-1.6.0/msvc/gtest-md/Debug;D:/libs/boost-1.44-vc90-mt-gd-lib;d:/libs/libphonenumber-8.10.9/cpp/build/Debug;
import
phonenumber.lib gtestd.lib icuin.lib icudt.lib icuuc.lib libprotobuf.lib libboost_thread-vc90-mt-gd-1_44.lib
运行后可以输出:
true CN 13478808311 86