使用 nmake 编译 Lua 及扩展库

起因

本来一直没想过还会在 windows 下开发的,但是一想到我的处理 Excel 的转表工具可能需要提供给策划本地使用,我就想方设法了,安装 mingw 的话应该更好解决,但是不了解发布版需不需要安装 mingw,而且刚好电脑已经安装好了 vs。所以就有了这篇文章。其实不用 nmake ,完全用 cl 和 link 命令也是可以的。只不过 nmake 和 make 一样,更适合管理。

编译 Lua

为了能编译并使用扩展,需要在生成 .dll 的时候生成 .lib 文件。生成 DLL 使用 /DLL 参数。但是生成的 lib 文件有问题,编译扩展库的时候会出现 unresolved xxx。搜到这个 https://ci.appveyor.com/project/xpol/lua-rapidjson/build/0.1.0.4/job/93u2pxa808er1huc 。最有用了,虽然是用 CMake 生成的 Make 日志,但是都把编译命令输出来了。需要添加 -DLUA_BUILD_AS_DLL ,至于为什么的话可以去 src/luaconf.h 中搜就知道了。

下面的 makefile 就是使用 nmake 执行的。生成 Lua.exe 比较简单。生成 Lua.lib 主要是要加上面提到的预定义。

STD_INC=/I "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include" \
/I "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include" \
/I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.10150.0\ucrt" \
/I "C:\Program Files (x86)\Windows Kits\8.1\Include\um" \
/I "C:\Program Files (x86)\Windows Kits\8.1\Include\shared" \
/I "C:\Program Files (x86)\Windows Kits\8.1\Include\winrt"

STD_LIB_PATH=/LIBPATH:"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib" \
/LIBPATH:"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\lib" \
/LIBPATH:"C:\Program Files (x86)\Windows Kits\10\lib\10.0.10150.0\ucrt\x86" \
/LIBPATH:"C:\Program Files (x86)\Windows Kits\8.1\lib\winv6.3\um\x86" \
/LIBPATH:"C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6\Lib\um\x86"

SRC_DIR=src
OBJ_DIR=objs

LUA_INC=/I $(SRC_DIR)

INCLUDE_DIR=$(STD_INC) $(LUA_INC)
LIB_PATH=$(STD_LIB_PATH)

OBJS=$(OBJ_DIR)\lapi.obj \
$(OBJ_DIR)\lauxlib.obj \
$(OBJ_DIR)\lbaselib.obj \
$(OBJ_DIR)\lbitlib.obj \
$(OBJ_DIR)\lcode.obj \
$(OBJ_DIR)\lcorolib.obj \
$(OBJ_DIR)\lctype.obj \
$(OBJ_DIR)\ldblib.obj \
$(OBJ_DIR)\ldebug.obj \
$(OBJ_DIR)\ldo.obj \
$(OBJ_DIR)\ldump.obj \
$(OBJ_DIR)\lfunc.obj \
$(OBJ_DIR)\lgc.obj \
$(OBJ_DIR)\linit.obj \
$(OBJ_DIR)\liolib.obj \
$(OBJ_DIR)\llex.obj \
$(OBJ_DIR)\lmathlib.obj \
$(OBJ_DIR)\lmem.obj \
$(OBJ_DIR)\loadlib.obj \
$(OBJ_DIR)\lobject.obj \
$(OBJ_DIR)\lopcodes.obj \
$(OBJ_DIR)\loslib.obj \
$(OBJ_DIR)\lparser.obj \
$(OBJ_DIR)\lstate.obj \
$(OBJ_DIR)\lstring.obj \
$(OBJ_DIR)\lstrlib.obj \
$(OBJ_DIR)\ltable.obj \
$(OBJ_DIR)\ltablib.obj \
$(OBJ_DIR)\ltm.obj \
$(OBJ_DIR)\lundump.obj \
$(OBJ_DIR)\lutf8lib.obj \
$(OBJ_DIR)\lvm.obj \
$(OBJ_DIR)\lzio.obj

all: Lua.dll Lua.exe Luac.exe

#compile
{$(SRC_DIR)}.c{$(OBJ_DIR)}.obj::
    @if not exist $(OBJ_DIR) mkdir $(OBJ_DIR)
    $(CC) /nologo $(INCLUDE_DIR) /Fo$B /c -DLUA_BUILD_AS_DLL $<
    @move *.obj $(OBJ_DIR)

# link
Lua.exe: Lua.lib $(OBJ_DIR)\lua.obj
    LINK /nologo /OUT:$@ $(LIB_PATH) $?

Luac.exe: $(OBJS) $(OBJ_DIR)\luac.obj
    LINK /nologo /OUT:$@ $(LIB_PATH) $?

Lua.dll: $(OBJS)
    LINK /nologo /DLL /OUT:$@ $(LIB_PATH) $?

clean:
    rd /S /Q $(OBJ_DIR)
    del *.exe
    del *.dll
    del *.exp
    del *.lib

对应的 Lua 源码目录结构是这样的

E:\workspace\lua\
                |
                .---src\
                |
                .---objs\
                |
                .---Makefile

编译 Lua 的扩展库

我主要是为了编译 xlsx 库,所以就以这个为例子。
主要就是为了生成 zip.sobase64.so。 生成 base64.so 比较简单就一个文件,生成一个obj,
然后再 link 成 dll。主要关键点在引用 Lua.lib 和 两个预定义 -DLUA_LIB -DLUA_BUILD_AS_DLL
Lua.lib 是上面编译 Lua 的时候生成的,要使用绝对路径。两个预定义的话还是要依据 luaconf.h
也是根据而来。改了这么多还不够,还要改 lbase64.cluazip.cxxx_open 函数的定义,
需要在前面加上 LUA_API/MD 需要改为 /MT,要不然移植到没有安装 vc++ 环境的机器上就跑不起来。历经N次尝试和谷歌搜索,就这样结束了。luazip.c 里面有行代码不兼容VC编译器:char tmp[szfn];,这是C99的用法,需要自行修改下。

STD_INC=/I "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include" \
/I "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include" \
/I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.10150.0\ucrt" \
/I "C:\Program Files (x86)\Windows Kits\8.1\Include\um" \
/I "C:\Program Files (x86)\Windows Kits\8.1\Include\shared" \
/I "C:\Program Files (x86)\Windows Kits\8.1\Include\winrt"

STD_LIB_PATH=/LIBPATH:"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib" \
/LIBPATH:"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\lib" \
/LIBPATH:"C:\Program Files (x86)\Windows Kits\10\lib\10.0.10150.0\ucrt\x86" \
/LIBPATH:"C:\Program Files (x86)\Windows Kits\8.1\lib\winv6.3\um\x86" \
/LIBPATH:"C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6\Lib\um\x86"

CFLAGS=/nologo /MT /O2 /c

LUA_INC=/I "E:\workspace\lua\src"
LUA_LIB= E:\workspace\lua\Lua.lib
LUA_LIB_PATH=/LIBPATH:"E:\workspace\lua"

ZLIB_SRC=zlib
MINIZIP_SRC=minizip

INCLUDE_DIR=$(STD_INC) $(LUA_INC) /I $(ZLIB_SRC) /I $(MINIZIP_SRC)
LIB_PATH=$(STD_LIB_PATH) $(LUA_LIB_PATH)

OBJ_DIR=objs

ZLIB_OBJS=$(OBJ_DIR)\adler32.obj \
$(OBJ_DIR)\compress.obj \
$(OBJ_DIR)\crc32.obj \
$(OBJ_DIR)\deflate.obj \
$(OBJ_DIR)\gzclose.obj \
$(OBJ_DIR)\gzlib.obj \
$(OBJ_DIR)\gzread.obj \
$(OBJ_DIR)\gzwrite.obj \
$(OBJ_DIR)\infback.obj \
$(OBJ_DIR)\inffast.obj \
$(OBJ_DIR)\inflate.obj \
$(OBJ_DIR)\inftrees.obj \
$(OBJ_DIR)\trees.obj \
$(OBJ_DIR)\uncompr.obj \
$(OBJ_DIR)\zutil.obj

MINIZIP_OBJS=$(OBJ_DIR)\ioapi.obj \
$(OBJ_DIR)\iowin32.obj \
$(OBJ_DIR)\miniunz.obj \
$(OBJ_DIR)\mztools.obj \
$(OBJ_DIR)\unzip.obj \
$(OBJ_DIR)\zip.obj

all: base64.dll zip.dll

#compile
.c{$(OBJ_DIR)}.obj::
    @if not exist $(OBJ_DIR) mkdir $(OBJ_DIR)
    $(CC) $(CFLAGS) $(INCLUDE_DIR) /Fo$B -DLUA_LIB -DLUA_BUILD_AS_DLL $<
    @move *.obj $(OBJ_DIR)

{$(MINIZIP_SRC)}.c{$(OBJ_DIR)}.obj::
    @if not exist $(OBJ_DIR) mkdir $(OBJ_DIR)
    $(CC) $(CFLAGS) $(INCLUDE_DIR) /Fo$B -DLUA_LIB -DLUA_BUILD_AS_DLL $<
    @move *.obj $(OBJ_DIR)

{$(ZLIB_SRC)}.c{$(OBJ_DIR)}.obj::
    @if not exist $(OBJ_DIR) mkdir $(OBJ_DIR)
    $(CC) $(CFLAGS) $(INCLUDE_DIR) /Fo$B -DLUA_LIB -DLUA_BUILD_AS_DLL $<
    @move *.obj $(OBJ_DIR)


# link
base64.dll: $(OBJ_DIR)\lbase64.obj
    LINK /nologo /DLL /OUT:$@ $(LIB_PATH) $(LUA_LIB) $?

zip.dll: $(OBJ_DIR)\luazip.obj $(ZLIB_OBJS) $(MINIZIP_OBJS)
    LINK /nologo /DLL /OUT:$@ $(LIB_PATH) $(LUA_LIB) $?

clean:
    rd /S /Q $(OBJ_DIR)
    del *.exe
    del *.dll
    del *.exp
    del *.lib

目录结构

luaclib\
       |
       .---minizip\
       |
       .---zlib\
       |
       .---objs\
       |
       .---lbase64.c
       |
       .---luazip.c
       |
       .---Makefile

编译 luasocket

makefile 见 https://github.com/hanxi/luasocket-nmake

跟上面的不同的是 lua.exe 和 luac.exe 都为静态编译的方式

点击进入评论 ...