在 Win 10 上编译 Libevent 需要准备如下工具:
这里使用的工具版本如下:
这些工具都可以在网上进行自由下载,下面从简单入手,对这些工具的安装和使用进行逐一讲解。这里先事先说明一点,这些程序最好不要安装在带空格的目录中,否则后期编译会报错 ,当然也会有解决办法。
Visual Studio Community 2015 的安装比较简单,可用直接从 Microsoft 官网 下载安装包,一路点击 “下一步” 进行安装即可。另外,用户需要先注册一个账号才能下载安装包。截止到目前为止,Microsoft 推出的最新版本为 Visual Studio 2022 。
直接从 zlib 官网 下载最新的源码,目前最新版本为 zlib-1.2.11 。下载完成后按照以下步骤可完成安装:
nmake /f win32\Makefile.msc
编译完成后需要自行拷贝头文件、库文件和可执行程序到安装目录中。为了省事,完全可以写一个批处理脚本文件 build_zlib_vs2015_64.bat ,文件内容如下:
set VS="C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
set INSTALL="build_release"
@rem 调用 VS 环境
call %VS%
@rem 进入 zlib 目录中进行编译
cd zlib-1.2.11
nmake /f win32\Makefile.msc clean
nmake /f win32\Makefile.msc
@rem 拷贝编译后的文件
md %INSTALL%\bin
md %INSTALL%\lib
md %INSTALL%\include
copy /Y *.dll %INSTALL%\bin
copy /Y *.exe %INSTALL%\bin
copy /Y *.lib %INSTALL%\lib
copy /Y *.h %INSTALL%\include
@rem 显示编译结束
pause
然后直接双击运行,就将 zlib 编译完成,并自动将头文件、库文件和可执行程序拷贝到安装目录 INATALL 中。
注意,在VS中调用静态库 zlib.lib 时会提示警告:warning LNK4099: PDB ‘zlib.pdb’ … ,这是因为程序默认调试 zlib 开源库,需要 pdb 文件。不需要理会,直接忽视该警告即可,或者直接使用动态库 zdll.lib / zlib1.dll。
要安装 OpenSSL ,需要先安装 Perl 和 nasm,Perl 用于生成 Makefile 文件,nasm 用于编译 OpenSSL 中的汇编代码。
安装 Perl 编译器
直接去 Perl 官网 下载编译器,在 Window 平台上有 ActiveStatePerl 和 Strawberry Perl 两个版本的编译器。ActiveState Perl 和 Strawberry Perl 最大的区别是后者中多包含一些 CPAN 里的模块, 所以Strawberry Perl 下载的安装文件要大一些,有 80MB 左右, 而 ActiveState Perl 只有 20M 左右。
想一键安装的可用选择 msi 安装包,不想安装的可用选择 zip 或 Portable 版本,也就是俗称的绿色版,只需要手工配置环境变量即可使用,很方便。我选择的正是 Strawberry Perl Portable 版本:strawberry-perl-5.24.1.1-64bit-portable.zip ,解压后配置环境变量就可以直接使用。
安装 nasm 编译器
NASM全称The Netwide Assembler,是一款基于 x86 和 x64 平台的汇编语言编译程序,其设计初衷是为了实现编译器程序跨平台和模块化的特性。NASM支持大量的文件格式,包括 Linux,*BSD,a.out,ELF,COFF,Mach−O,Microsoft 16−bit OBJ,Win32 以及 Win64,同时也支持简单的二进制文件生成。它的语法被设计的简单易懂,相较 Intel 的语法更为简单,支持目前已知的所有 x86 架构之上的扩展语法,同时也拥有对宏命令的良好支持。
可以在 nasm 官网下载最新 win64 安装包,我这里使用的是 nasm-2.15.05-win64.zip ,解压后设置环境变量即可使用。
nasm 官网 https://www.nasm.us/
nasm 简介 http://www.bytekits.com/nasm/intro.html
设置 Perl 和 nams 环境变量
Win 10 可以在 系统属性 —— 高级 —— 环境变量 —— 系统变量 中增加 Path 中的路径,如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8xPRymNO-1665832234264)(./image/perl_nasm_path.png)]
安装 OpenSSL
解压 openssl-1.1.1.tar.gz
调出 VS 2015 中的 VS 2015 x64 本机工具命令提示符
在命令提示符中进入 openssl-1.1.1 目录,执行 perl Configure VC-WIN64A --prefix="D:\Program Files\openssl"
,编译 64 位 OpenSSL ,并将其安装到 D:\Program Files\openssl ,因为目录中有空格符,需要用双引号括起来。运行过程中会提示已创建 makefile 文件:
Configuring OpenSSL version 1.1.1 (0x1010100fL) for VC-WIN64I
Using os-specific seed configuration
Creating configdata.pm
Creating makefile
**********************************************************************
*** ***
*** If you want to report a building issue, please include the ***
*** output from this command: ***
*** ***
*** perl configdata.pm --dump ***
*** ***
**********************************************************************
Windows 版本选择一般有三种架构:VC-WIN32 | VC-WIN64A | VC-WIN64I
运行 nmake
运行 nmake install
,这一步需要管理员权限,因为需要从系统盘中拷贝文件。
也可以像 zlib 一样,为 OpenSSL 写一个编译脚本 build_openssl_vs2015_64.bat ,内容如下:
@echo "begin complete openssl"
set VS="C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
set INSTALL="D:\Program Files\openssl"
@rem 调用 VS 环境
call %VS%
@rem 进入 D 盘
D:
@rem 进入 openssl 目录中进行编译
cd "D:\Program Files\openssl-1.1.1"
perl Configure VC-WIN64A --prefix=%INSTALL%
nmake clean
nmake
nmake install
@echo "end complete openssl"
pause
即可将 OpenSSL 编译安装到目录 D:\Program Files\openssl 中,注意,因为要从系统盘中拷贝文件,所以需要管理员的权限运行。注意,第 9 行不能省,因为 CMD 中管理员默认处于系统盘(C: 盘)中,若省略,则无法进入 D: 目录。若是安装包都放于 C 盘,则可以省略此行。
先来看看,在 Windows 上不做任何修改,直接解压编译 libevent-2.1.12-stable 会出现什么问题。
解压 libevent-2.1.12-stable.tar.gz
调出 VS 2015 中的 VS 2015 x64 本机工具命令提示符
在命令提示符中进入 libevent-2.1.12-stable 目录,执行 nmake /f Makefile.nmake
,此时会报错 “UINT32_MAX”: 未声明的标识符
event.c
d:\program files\libevent-2.1.12-stable\minheap-internal.h(73): error C2065: “UINT32_MAX”: 未声明的标识符
我在网上搜了一下,很多人都说直接在 mm-internal.h 文件里引入头文件 #include
,但我觉得修改源码不是本意,后来发现可以在 WIN32-Code\nmake\event2\event-config.h 中修改,直接放开 EVENT__HAVE_STDINT_H
的宏定义
/* Define to 1 if you have the header file. */
/* #define EVENT__HAVE_STDINT_H 1 */
#define EVENT__HAVE_STDINT_H 1
继续执行 nmake /f Makefile.nmake
,此时又会报错 无法解析的外部符号 if_nametoindex
libevent.lib(evutil.obj) : error LNK2019: 无法解析的外部符号 if_nametoindex,该符号在函数 evutil_inet_pton_scope 中被引用
regress.exe : fatal error LNK1120: 1 个无法解析的外部命令
这是因为编译静态库,缺少库文件 Iphlpapi.lib
的引用,这是 Windows 一个网络名称和索引的转换库,具体可参考 Microsoft 官方说明 。此时只需要在 test/Makefile.nmake 中加入这个库即可
LIBS=..\libevent.lib ws2_32.lib shell32.lib advapi32.lib Iphlpapi.lib
至此问题全部解决,再次编译则全部通过。这种编译虽然可以使用,但并不包含 OpenSSL 和 zlib ,所以还需要将这两部分加入到 Libevent 的编译中。
在编译时指定 OpenSSL 的目录
nmake /f Makefile.nmake clean # 先清除掉以前的编译文件
nmake /f Makefile.nmake OPENSSL_DIR="d:\Progra~1\openssl"
因为 Libevent 中引用的 OpenSSL 库比较老,新版的 OpenSSL 已经更改了生成的库文件名,所以还需要修改 test/Makefile.nmake 中引用的库文件名称
SSL_LIBS=..\libevent_openssl.lib $(OPENSSL_DIR)\lib\libeay32.lib $(OPENSSL_DIR)\lib\ssleay32.lib gdi32.lib User32.lib
# 改为
SSL_LIBS=..\libevent_openssl.lib $(OPENSSL_DIR)\lib\libssl.lib $(OPENSSL_DIR)\lib\libcrypto.lib gdi32.lib User32.lib
另外,需要注意,我的 OpenSSL 位于 D:\Program Files\openssl ,目录中有个空格,即便用双引号括起来也没有用,不过可以用 缩写法 。
CMD 中可以采用 8 个字符缩写,即写头六个字母(略去空白),另加波浪号和数字。若首字母不足六个,则略去空白,采用第二个字母。比如一下三个目录
的缩写分别为:C:\Progra~1; C:\Progra~2; C:\Progra~3 。
编译完成后,到 test 目录中执行 regress.exe 测试程序:
D:\Program Files\libevent-2.1.12-stable\test>regress.exe
main/methods: [forking] [warn] event.c: no event mechanism available
OK
main/version: OK
main/base_features: [forking] OK
main/base_environ: [forking] SKIPPED
main/event_base_new: [forking] OK
main/free_active_base: [forking] OK
......
bufferevent/bufferevent_trigger_defer_postpone: [forking] OK
bufferevent/bufferevent_zlib: SKIPPED
bufferevent/bufferevent_connect_fail_eventcb_defer: [forking] OK
......
ssl/bufferevent_socketpair: [forking] OK
ssl/bufferevent_socketpair_write_after_connect: [forking] OK
ssl/bufferevent_filter: [forking] OK
ssl/bufferevent_filter_write_after_connect: [forking] OK
ssl/bufferevent_renegotiate_socketpair: [forking] OK
ssl/bufferevent_renegotiate_filter: [forking] OK
ssl/bufferevent_socketpair_startopen: [forking] OK
ssl/bufferevent_filter_startopen: [forking] OK
ssl/bufferevent_socketpair_dirty_shutdown: [forking] OK
ssl/bufferevent_filter_dirty_shutdown: [forking] OK
ssl/bufferevent_renegotiate_socketpair_dirty_shutdown: [forking] OK
ssl/bufferevent_renegotiate_filter_dirty_shutdown: [forking] OK
ssl/bufferevent_socketpair_startopen_dirty_shutdown: [forking] OK
ssl/bufferevent_filter_startopen_dirty_shutdown: [forking] OK
ssl/bufferevent_socketpair_fd: [forking] OK
ssl/bufferevent_socketpair_freed: [forking] OK
ssl/bufferevent_socketpair_freed_fd: [forking] OK
ssl/bufferevent_filter_freed_fd: [forking] OK
ssl/bufferevent_socketpair_timeout: [forking] OK
ssl/bufferevent_socketpair_timeout_freed_fd: [forking] OK
ssl/bufferevent_connect: [forking] OK
ssl/bufferevent_connect_sleep: [forking] OK
ssl/bufferevent_wm: [forking] OK
ssl/bufferevent_wm_filter: [forking] OK
ssl/bufferevent_wm_defer: [forking] OK
ssl/bufferevent_wm_filter_defer: [forking] OK
可以看到,OpenSSL 的测试程序全部 OK,但是 zlib 测试程序 bufferevent_zlib 却被跳过。
要使用 zlib ,需要修改 test/Makefile.nmake 和 WIN32-Code\nmake\event2\event-config.h ,步骤如下
# 1. 引用 zlib 头文件
CFLAGS=/I.. /I../../zlib/include
# 2. 增加 zlib 的测试文件,在 REGRESS_OBJS 追加 regress_zlib.obj
REGRESS_OBJS=... regress_zlib.obj
# 3. 引用 zlib 库文件,这里应用动态库
LIBS=..\libevent.lib ...... ../../zlib/lib/zdll.lib
# 4. 在 WIN32-Code\nmake\event2\event-config.h 中添加 zlib 宏定义
/* Define if the system has zlib */
/* #undef EVENT__HAVE_LIBZ */
#define EVENT__HAVE_LIBZ
# 5. 重新编译
nmake /f Makefile.nmake clean
nmake /f Makefile.nmake OPENSSL_DIR="d:\Progra~1\openssl"
# 6. 运行 test 中的 regress.exe 程序,可以成功调用 zlib
......
bufferevent/bufferevent_zlib: [forking] OK
......
如果在测试中出现 找不到 libssl-1_1-x64.dll 错误
是因为没有找到 OpenSSL 的动态库,只需要将 libssl-1_1-x64.dll 和 libcrypto-1_1-x64.dll 拷贝到 test 目录中即可。
同样的,也可以为 Libevent 写一个编译脚本 build_libevent_vs2015_64 .bat ,内容如下
@echo "begin complete libevent"
set VS="C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
set INSTALL="D:\Program Files\libevent"
@rem 调用 VS 环境
call %VS%
@rem 进入 D 盘
D:
@rem 进入 libevent 目录中进行编译
cd "D:\Program Files\libevent-2.1.12-stable"
nmake /f Makefile.nmake clean
nmake /f Makefile.nmake OPENSSL_DIR="d:\Progra~1\openssl"
@rem 拷贝编译后的文件
md %INSTALL%\bin
md %INSTALL%\lib
md %INSTALL%\include
copy /Y *.dll %INSTALL%\bin
copy /Y *.exe %INSTALL%\bin
copy /Y *.lib %INSTALL%\lib
xcopy /S/Y include %INSTALL%\include
xcopy /S/Y WIN32-Code\nmake\event2 %INSTALL%\include\event2
@echo "end complete libevent"
pause
以上就是通过 nmake 编译 Libevent 的全部过程,可以看到,还是比较反锁的,而且还只能生成静态库。好在还可以使用 cmake 来编译 Libevent ,既可以生成静态库,还可以生成动态库。
cmake 安装 Libevent 还是比较简单的,只需要指定 OpenSSL 和 zlib 的目录即可,但比较违和的地方是头文件和库文件路径要分开指定,我也不知道为啥。另外,test 中的 regress 测试程序需要依赖 Python2 才能使用(RPC 部分的代码需要 Python2 生成),我电脑上没有 python2 的环境,也只好略过。
解压 libevent-2.1.12-stable.tar.gz
调出 VS 2015 中的 VS 2015 x64 本机工具命令提示符 (不能直接cmd,会找不到某些环境变量)
在命令提示符中进入 libevent-2.1.12-stable 目录,执行如下命令
# 1. 建立 release 目录,放编译产生的文件,避免污染源码
mkdir release
cd release
# 2. 指定 cmake 配置,包含编译类型(静态库),安装目录,OpenSSL 和 zlib 的目录
cmake.exe .. -G "Visual Studio 14 2015 Win64" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="D:\Progra~1\libevent_s" -DOPENSSL_ROOT_DIR="d:\Progra~1\openssl" -DOPENSSL_CRYPTO_LIBRARY="d:\Progra~1\openssl\lib" -DOPENSSL_INCLUDE_DIR="d:\Progra~1\openssl\include" -DZLIB_LIBRARY="d:\Progra~1\zlib\lib\zdll.lib" -DZLIB_INCLUDE_DIR=="d:\Progra~1\zlib\include" -DEVENT__LIBRARY_TYPE=STATIC
# 3. 编译为 release 版本
cmake.exe --build . --config Release
# 4. 安装到指定目录中 DCMAKE_INSTALL_PREFIX="D:\Progra~1\libevent_s"
cmake.exe --install .
# 5. 如果编译动态库,只需要修改第 2 步的 cmake 配置即可
cmake.exe .. -G "Visual Studio 14 2015 Win64" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="D:\Progra~1\libevent" -DOPENSSL_ROOT_DIR="d:\Progra~1\openssl" -DOPENSSL_CRYPTO_LIBRARY="d:\Progra~1\openssl\lib" -DOPENSSL_INCLUDE_DIR="d:\Progra~1\openssl\include" -DZLIB_LIBRARY="d:\Progra~1\zlib\lib\zdll.lib" -DZLIB_INCLUDE_DIR=="d:\Progra~1\zlib\include"
# 6. 再次执行 3. 4 两部进行动态库的编译和安装
cmake.exe --build . --config Release
cmake.exe --install .
cmake 安装的最大好处就是不需要像 nmake 那样修改源码,cmake 会自动检测机器中的程序配置,并设置号编译信息,节省了工作,推荐使用 cmake 编译。
如果提示找不到 OpenSSL,那是因为 OpenSSL 的目录不在 PATH 环境变量中,直接添加进去,重新 cmake 一次即可。
后来换了win11的系统,不知为啥 d:\Progra~1 变成一个不可识别的路径,它在 win10 上表示第一个已 Progra 开头的路径。后来在 win11 上换成了 D:\ProgramFiles 就没有问题了,即
cmake.exe .. -G "Visual Studio 14 2015 Win64" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="D:\ProgramFiles\libevent" -DOPENSSL_ROOT_DIR="d:\ProgramFiles\openssl" -DOPENSSL_CRYPTO_LIBRARY="d:\ProgramFiles\openssl\lib" -DOPENSSL_INCLUDE_DIR="d:\ProgramFiles\openssl\include" -DZLIB_LIBRARY="d:\ProgramFiles\zlib\lib\zdll.lib" -DZLIB_INCLUDE_DIR=="d:\ProgramFiles\zlib\include"
所以说,在 windows 上使用 nmake 或 cmake 安装程序时,最好不要有带空格的目录,否则会报一些莫名其妙的问题。
接下来写一个简单的测试程序,测试 Libevent 安装是否成功,代码如下:
#include
#include "event.h"
int main(int argc, char* argv[])
{
#ifdef _WIN32
// 初始化 socket 库
WSADATA wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);
#endif
std::cout << "test libevent!" << std::endl;
// 创建 libevent 上下文
event_base *base = event_base_new();
if (base) {
std::cout << "event_base_new success!" << std::endl;
}
return 0;
}
代码很简单,就是创建了一个 Libevent 上下文。特别说明一下第 6 ~ 10 行,这几行的功能是初始化 Windows 下的 socket 库,或者是初始化 Win32 程序,需要单独调用,也不知道是什么原因,感觉怪怪的。
Libevent 并没有专门针对 socket 去编程,而是所有的文件 IO 都可以被处理,包括普通文件的 IO ,管道 IO 等。
最后,需要在 VS 2015 的项目属性中设置头文件和库文件的路径,以及库文件名称。前面两个路径根据实际安装目录进行选择,库文件除了需要 event.lib 外,还需要 ws2_32.lib ,它是 Win32 中的 socket 库。
若是使用静态库,则还会有一个问题,编译时提示:
event.lib(evutil.obj) : error LNK2001: 无法解析的外部符号 if_nametoindex
1>D:\vs2015\libevent_demo\x64\Release\demo_server_test.exe : fatal error LNK1120: 1 个无法解析的外部命令
需要再添加 Iphlpapi.lib ,但编译时还会提示如下警告:
1>LINK : warning LNK4098: 默认库“LIBCMT”与其他库的使用冲突;请使用 /NODEFAULTLIB:library
这里不做深究,但个人推荐,还是使用动态库比较好,至少没有这么多乱七八糟的问题。
个人感觉,Libevent 在 Linux 上的编译比 Windows 上要简单很多,貌似开源软件都是这样。
Linux 上的编译环境:
这里将所有的软件都安装在 H O M E / . l o c a l / ∗ ∗ 目录中,所以需要先确保 ∗ ∗ HOME/.local/** 目录中,所以需要先确保 ** HOME/.local/∗∗目录中,所以需要先确保∗∗HOME/.local/bin 位于 PATH 环境变量中,否则 Libevent 会找不到 OpenSSL 和 zlib,当然也可以通过编译选项指定它们的目录。安装方法如下:
# 1. 安装 zlib
$ tar zxvf zlib-1.2.11.tar.gz
$ cd zlib-1.2.11
$ ./configure --prefix=$HOME/.local/
$ make
$ make install
# 2. 安装 openssl
$ tar zxvf openssl-1.1.1.tar.gz
$ cd openssl-1.1.1
$ ./config --prefix=$HOME/.local/
$ make
$ make install
# 3. 安装 libevent
$ tar zxvf libevent-2.1.12-stable.tar.gz
$ cd libevent-2.1.12-stable
$ ./configure --prefix=$HOME/.local/
......
# 可以看到,zlib 和 openssl 都被成功检索到
checking zlib.h usability... yes
checking zlib.h presence... yes
checking for zlib.h... yes
checking for library containing inflateEnd... -lz
checking for special C compiler options needed for large files... no
checking for _FILE_OFFSET_BITS value needed for large files... no
checking for pkg-config... /usr/bin/pkg-config
checking if pkg-config is at least version 0.15.0... yes
checking openssl/ssl.h usability... yes
checking openssl/ssl.h presence... yes
......
$ make
$ make install
# 4. 测试 libevent test 程序
cd test/
./regress
# 需要将 $HOME/.local/lib 添加到 LD_LIBRARY_PATH 中,否则找不到库文件
......
bufferevent/bufferevent_zlib: [forking] OK
......
ssl/bufferevent_socketpair: [forking] OK
ssl/bufferevent_socketpair_write_after_connect: [forking] OK
ssl/bufferevent_filter: [forking] OK
ssl/bufferevent_filter_write_after_connect: [forking] OK
ssl/bufferevent_renegotiate_socketpair: [forking] OK
......
可以看到,zlib 和 openssl 都被测试通过。
在安装 libevent 时若提示找不到 openssl,可以使用 CPPFLAGS 和 LDFLAGS 指定安装目录,尽量不要使用 CFLAGS,会提示两条 WARNING(我是安装在用户目录,若是安装在系统目录,应该无此问题)。示例如下:
# 使用 CFLAGS 会提示两条 WARNING,可以使用 CPPFLAGS
./configure CFLAGS=-I$HOME/.local/include LDFLAGS=-L$HOME/.local/lib --prefix=$HOME/.local
checking zlib.h usability... yes
checking zlib.h presence... no
configure: WARNING: zlib.h: accepted by the compiler, rejected by the preprocessor!
configure: WARNING: zlib.h: proceeding with the compiler's result
checking for zlib.h... yes
checking for library containing inflateEnd... -lz
checking for special C compiler options needed for large files... no
checking for _FILE_OFFSET_BITS value needed for large files... no
checking for pkg-config... /usr/bin/pkg-config
checking if pkg-config is at least version 0.15.0... yes
checking for library containing SSL_new... -lssl
checking openssl/ssl.h usability... yes
checking openssl/ssl.h presence... no
configure: WARNING: openssl/ssl.h: accepted by the compiler, rejected by the preprocessor!
configure: WARNING: openssl/ssl.h: proceeding with the compiler's result
# 查看帮助文档,CPPFLAGS 包含了 C/C++ 的头文件目录
./configure CPPFLAGS=-I$HOME/.local/include LDFLAGS=-L$HOME/.local/lib --prefix=$HOME/.local
CentOS 7 Libevent 测试程序与 Windows 上一样,只是需要编写 Makefile 进行编译:
# libevent_demo makefile
CPP = g++
DEBUG = -g
CPPFLAGS = -Wall $(DEBUG) -I$(HOME)/.local/include
LIBS = -L$(HOME)/.local/lib -levent
TARGET := $(subst .cpp,,$(wildcard *.cpp))
all: $(subst .cpp,,$(wildcard *.cpp))
% : %.cpp
$(CPP) -o $@ $^ $(CPPFLAGS) $(LIBS)
.PHONY : clean
clean:
rm -rf $(TARGET)
这是一个比较独立的 Makefile ,可以将目录中的每个 cpp 文件编译为独立的可执行程序。
另外说明一点,Linux 默认对库的连接是使用动态库,在找不到动态库的情况下才选择静态库,若当前目录有两个库 libevent.so 和 libevent.a ,则肯定是连接 libevent.so ,使用方式为:
g++ test.cpp -L. -levent
如果要指定连接静态库 libevent.a ,可使用如下方式:
g++ test.cpp -L. -static -levent