Nginx 在 Windows 平台上编译

时间:2021-07-30 23:00:30

在Windows平台上编译Nginx遇到一些问题, 最终都逐个解决了, 记录一下过程.

打开网址

http://nginx.org/en/download.html

其中 nginx/Windows-1.10.1 是直接下载编译好的Windows版本的二进制程序
而要下载源码需要使用 Mercurial 去 clone, 源代码地址是
http://hg.nginx.org/nginx

Mercurial 是一个源代码管理工具, 与 SVN 类似
他有Windows的版本, 在Windows环境下也能使用, 但是官网貌似打不开
https://www.mercurial-scm.org/
但可以在别的地方下载到它 如:
http://www.onlinedown.net/soft/87736.htm
版本不一定最新, 但是能用
安装过程很简单, 为了方便起见, 可以允许将它的安装目录添加到Windows的环境变量PATH中

打开命令行, cd 到某个目录(用来存放和编译Nginx源码的位置), 执行

hg clone http://hg.nginx.org/nginx

开始下载源码, 看网速可能需要好几分钟
Nginx 在 Windows 平台上编译

下面需要下载Nginx依赖的三个库, 分别是 PCRE, zlib 和 OpenSSL
Nginx 1.10.1 对应的第三方库的版本分别是

pcre-8.39.tar.gz
zlib-1.2.8.tar.gz
openssl-1.0.2h.tar.gz

基本上都能从sourceforge上获取, 或直接用搜索引擎去找download

然后需要在Nginx源码根目录下创建 “objs” 和 “objs/lib” 两级目录, 将以上三个库解压到 objs/lib 下
注: 这三个第三方库实际上也是源码形式, 并没有lib或dll, 都需要一起编译

然后需要执行配置, 命令是

auto/configure --with-cc=cl --builddir=objs --prefix= \
--conf-path=conf/nginx.conf --pid-path=logs/nginx.pid \
--http-log-path=logs/access.log --error-log-path=logs/error.log \
--sbin-path=nginx.exe --http-client-body-temp-path=temp/client_body_temp \
--http-proxy-temp-path=temp/proxy_temp \
--http-fastcgi-temp-path=temp/fastcgi_temp \
--with-cc-opt=-DFD_SETSIZE=1024 --with-pcre=objs/lib/pcre-8.39 \
--with-zlib=objs/lib/zlib-1.2.8 --with-openssl=objs/lib/openssl-1.0.2h \
--with-select_module --with-http_ssl_module --with-ipv6

但这个命令是无法直接在 Windows 命令行中执行的, 需要一个Linux环境, 方法便是使用 MinGW, 里面有一个MSYS 的工具, 类似 Linux 中的 bash
打开网站
http://www.mingw.org/wiki/MSYS
下载 MinGW 的 Installer, 注意: 这只是一个安装器
最终下载下来的文件是 mingw-get-setup.exe, 很小不到 1MB, 双击安装
需要注意的是, 安装路径可以修改, 但路径中不能有空格(中文应该也不行, 没试过)
Nginx 在 Windows 平台上编译
真正的安装过程也不快,需要在线下载很多东西
安装完成, 点击 Continue 会直接打开一个管理界面
Nginx 在 Windows 平台上编译
接下来勾选上 Basic Setup 里的 msys-base, 然后 Apply Changes, 开始安装msys
这个过程也不快, 需要等几分钟

打开msys的安装目录, 启动msys.bat, 类似Linux环境, 默认目录为home/username, 貌似不能访问到 “/” 根目录之外的地方(没有研究), 简单的方式是将Nginx的源代码目录整个拷贝到用户目录下
然后执行 auto/Configure …… 创建Makefile文件
Nginx 在 Windows 平台上编译
Makefile 文件会生成到 nginx\objs 目录下
然后就不需要MSYS了

在正式编译前可以安装一下sed这个工具, 因为编译完后, 脚本会调用它生成一个nginx.8的文件(具体什么用没搞清楚), 需要将sed.exe的目录加到环境变量PATH中

还需要安装Perl, 可以从
http://www.activestate.com/activeperl
下载, 并允许将安装路径加入到环境变量PATH中

下面启动 Visual Studio 工具命令提示, 我用的版本是 Visual Studio 2015
cd 到 Nginx 源代码目录, 执行 nmake -f objs/Makefile 开始编译

但会发现编译错误

        ml /nologo /Cp /coff /c /Cx /Zi /Fotmp32\sha1-586.obj tmp32\sha1-586.asm
 Assembling: tmp32\sha1-586.asm
tmp32\sha1-586.asm(1432) : error A2070:invalid instruction operands
tmp32\sha1-586.asm(1576) : error A2070:invalid instruction operands
NMAKE : fatal error U1077: “"D:\Program Files (x86)\Microsoft Visual Studio 14.
0\VC\BIN\ml.EXE"”: 返回代码“0x1”
Stop.
NMAKE : fatal error U1077: “"D:\Program Files (x86)\Microsoft Visual Studio 14.
0\VC\BIN\nmake.exe"”: 返回代码“0x2”
Stop.
NMAKE : fatal error U1077: “"D:\Program Files (x86)\Microsoft Visual Studio 14.
0\VC\BIN\nmake.exe"”: 返回代码“0x2”
Stop.

其实是在编译OpenSSL时报错, 具体报错的语句是

tmp32\sha1-586.asm line:1432    movd    xmm1,XMMWORD PTR 16[edi]
tmp32\sha1-586.asm line:1576    movd    XMMWORD PTR 16[edi],xmm1

整个文件就两个地方用到了 movd 指令, 而且都报错了
movd 是一条SSE指令, 作用是将源存储器32位内容送入目的寄存器的低32位,高96位清零
而 XMMWORD PTR 是指向 128 位的数据,
参考 MSDN
https://msdn.microsoft.com/zh-cn/library/cw0399sf(v=vs.140).aspx
Nginx 在 Windows 平台上编译
这里要编译通过要么将 XMMWORD 改为 DWORD, 或将指令改为movdqa
尝试改完后发现又报出了其他的很多指令语法错误
Nginx 在 Windows 平台上编译
看来有可能不是能简单修改代码的问题
查看了一下Openssl的开发者网站, 有对应问题的描述
http://mailing.openssl.dev.narkive.com/f9PT0DaI/openssl-dev-openssl-org-3650-sha1-586-asm-broken-in-1-0-2-stable-for-windows-builds

要解决这个问题可以在Windows平台上使用nasm, 而不使用微软的masm
下载 nasm for Windows
http://www.nasm.us/pub/nasm/releasebuilds/2.12.02rc9/win64/nasm-2.12.02rc9-installer-x64.exe

安装完成后需要 手动将安装目录加到环境变量PATH中, 并需要重启一个VS命令行窗口(需要重新加载环境变量)

然后需要修改 nginx\auto\lib\openssl\makefile.msvc 文件将

ms\do_ms 

改为

ms\do_nasm

此时最好将上次编译的目录临时目录 “nginx\objs\lib\openssl-1.0.2h\tmp32” 和 输出目录 “nginx\objs\lib\openssl-1.0.2h\out32” 清空一下
符: 关于 OpenSSL 的编译可以 阅读 nginx\objs\lib\openssl-1.0.2h\INSTALL.W32 这个说明文件
注: Nginx 需要编译的是 OpenSSL 的静态库, 所以 nginx\auto\lib\openssl\makefile.msvc 中使用的是

$(MAKE) -f ms\nt.mak 

而不是

$(MAKE) -f ms\ntdll.mak

最后回到 Nginx 源码的根目录, 再次执行 nmake -f objs/Makefile
编译成功!

补充1: 关于执行 auto/Configure … 有报错提示的问题
提示为:

auto/cc/msvc: line 117: [: : integer expression expected

原因是: 在脚本文件 nginx\auto\cc\msvc 中变量 NGX_MSVC_VER 的值计算有误
具体代码是

NGX_MSVC_VER=`$NGX_WINE $CC 2>&1 | grep 'Compiler Version' 2>&1 \
 | sed -e 's/^.* Version \(.*\)/\1/'`
echo " + cl version: $NGX_MSVC_VER"

貌似是根据 $CC 也就是 cl.exe 命令执行后的输出 用正则匹配出编译器的版本号, 另外在后面

# MSVC 2005 supports C99 variadic macros
if [ "$ngx_msvc_ver" -ge 14 ]; then
    have=NGX_HAVE_C99_VARIADIC_MACROS . auto/have
fi

用到了变量NGX_MSVC_VER, 用于比较这个值是否大于等于14, 而提示报错的也就是这一句话
由于对Configure的写法不是很了解(初步猜想是因为我用得是中文版本的原因), 可以简单的根据自己的Visual Studio的版本, 直接设定这个值, 参考

# MSVC 6.0 SP2 cl 12.00
# MSVC Toolkit 2003 (7.1) cl 13.10
# MSVC 2005 Express Edition SP1 (8.0) cl 14.00
# MSVC 2008 Express Edition (9.0) cl 15.00
# MSVC 2010 (10.0) cl 16.00
# MSVC 2015 (14.0) cl 19.00

我编译时用的是 Visual Studio 2015, 于是将代码改为

NGX_MSVC_VER=`$NGX_WINE $CC 2>&1 | grep 'Compiler Version' 2>&1 \
 | sed -e 's/^.* Version \(.*\)/\1/'`
NGX_MSVC_VER=19.00
echo " + cl version: $NGX_MSVC_VER"

然后重新在msys中执行 auto/Configure …
成功执行没有报错