从DLL生成LIB
cheungmine
2013-5-14
// (程序清单1)
// WinApp.c
//
#include <windows.h>
#include "C:/DEVPACK/LIBABC/include/ABCapi.h"
# pragma comment(lib, "C:/DEVPACK/LIBABC/lib/libABC.lib");
int main()
{
// 下面可以直接使用 LIBABC 的函数
ABCInit();
ABCAddValues(a, b);
ABCUninit();
exit(0);
}
但是问题是,我们手里只有libABC.dll和头文件。没有libABC.lib。这就需要我们从命令行工具生成出libABC.lib。从DLL生成LIB的过程是:DLL->DEF->LIB。需要生成一个叫做模块定义文件(.def)的中间文件。一个典型的模块定义文件(libABC.def)如下(程序清单2):
LIBRARY libABC
EXPORTS
ABCInit
ABCUninit
ABCAddValues
...
windows的vs系列开发工具提供了这个命令行dumpbin,它的调用方式是打开cmd,然后输入下面的命令:
dumpbin /EXPORTS libABC.dll > libABC.def
上面的命令执行后生成的libABC.def文件包含了对于生成LIB无用的信息,我们必须手工处理掉,然后才能带入下面的命令中生成LIB。假设我们已经把libABC.def处理成(程序清单2)的样子。接下来使用另外一个命令行工具,通过这个def文件生成LIB:
lib /def:libABC.def /machine:i386 /out:libABC.dll
总结下来,整个过程有3步:
1)DLL->def
2)处理def
3)使用处理后的def->LIB
整个过程在第2步不是自动化的,我曾经写过一个脚本完成第2步的自动化。后来发现MinGW提供了这样的工具:pexports。默认的MinGW没有安装pexports,你需要运行下面的命令在MinGW中安装:
$ mingw-get install pexports
于是,产生处理好的def的工作就变得非常简单:
$ pexports libABC.dll > libABC.def
无论是dumpbin还是lib这2个命令都需要VS的运行环境。因此必须先设置环境变量,通过运行一个在%VS??COMNTOOLS%的路径下的批处理文件vsvars32.bat(??表示版本号,可以查看windows环境变量得到这个信息),来完成VS环境变量设置。过程就是打开windows cmd,输入下面的命令:
%VS100COMNTOOLS%vsvars32.bat
最后我把整个处理过程写成了python脚本,这个脚本执行起来特别简单,打开MinGW命令行,进入libABC.dll所在的目录,输入下面的命令:
$ python mklib-win32.py libABC
或者
$ python mklib-win32.py libABC.dll
最后,我把mklib-win32.py贴出来,以飨读者:
#!/usr/bin/python
# filename: mklib-win32.py
# make import x86_32 import-lib from windows win32 dll
# author: cheungmine@gmail.com
# date: 2013-5
# version: 0.1
#
# MinGW:
# $ python build-win32.py target_dll
# $ python build-win32.py libtiff-5.dll
# $ python build-win32.py libtiff-5
# $ python build-win32.py tiff-5
# $ python build-win32.py c:/path/to/libtiff-5
# ERROR: $ python build-win32.py c:\path\to\libtiff-5
#
# file operation:
# import shutil
#
## copy file:
# shutil.copy(myfile, tmpfile)
#
## copy time of file:
# shutil.copy2(myfile, tmpfile)
#
## copy file dir tree, the 3rd parameter means:
## True: symbol link
## False: use phyical copy
# shutil.copytree(root_of_tree, destination_dir, True)
###############################################################################
import os
import platform
import time
import getopt
import optparse
import sys
import string
###############################################################################
# get installed VS???COMNTOOLS environment:
###############################################################################
def get_vspath():
_vspath = os.getenv('VS110COMNTOOLS')
if not _vspath:
_vspath = os.getenv('VS100COMNTOOLS')
if not _vspath:
_vspath = os.getenv('VS90COMNTOOLS')
if not _vspath:
_vspath = os.getenv('VS80COMNTOOLS')
if not _vspath:
print "VS??COMNTOOLS not found"
sys.exit()
else:
print "VS80COMNTOOLS =", _vspath
else:
print "VS90COMNTOOLS =", _vspath
else:
print "VS100COMNTOOLS =", _vspath
else:
print "VS110COMNTOOLS =", _vspath
return _vspath
###############################################################################
# step (1): create a windows module definition: target_lib.def
# MSCMD:
# > dumpbin /EXPORTS target_lib.dll > ~target_lib.def
# or MinGW:
# $ pexports target_lib.dll > target_lib.def
# step (2): use this target_lib.def to create module import file: target_lib.lib
# MSCMD:
# > lib /def:target_lib.def /machine:i386 /out:target_lib.lib
###############################################################################
def make_lib(workdir, tgtname):
print "[2-1] create a windows module definition: lib%s.def" % tgtname
dump_def = 'cd "%s"&pexports lib%s.dll > lib%s.def' % \
(work_dir, tgtname, tgtname)
ret = os.system(dump_def)
if ret == 0:
print "[2-2] use (lib%s.def) to create import module: lib%s.lib" % (tgtname, tgtname)
lib_cmd = 'cd "%s"&lib /def:lib%s.def /machine:i386 /out:lib%s.lib' % (workdir, tgtname, tgtname)
cmds = 'cd "%s"&vsvars32.bat&%s&cd "%s"' % (vs_path, lib_cmd, cwd_path)
ret = os.system(cmds)
if ret == 0:
print "INFO: mklib (%s/lib%s.lib) success." % (cwd_path, tgtname)
return 0;
else:
print "ERROR: mklib (%s/lib%s.lib) failed." % (cwd_path, tgtname)
return (-2)
else:
print "ERROR: mklib (%s/lib%s.def) failed." % (cwd_path, tgtname)
return (-1);
###############################################################################
# current directory:
cwd_path = os.getcwd()
vs_path = get_vspath()
work_dir = "./"
# lib name == parent folder name
target_dll = "ERROR_dll_not_found"
if sys.argv.__len__() == 1:
work_dir, target_dll = os.path.split(cwd_path)
elif sys.argv.__len__() == 2:
work_dir = os.path.dirname(sys.argv[1])
target_dll = os.path.basename(sys.argv[1])
else:
print "ERROR: invalid argument"
sys.exit(-1)
if target_dll[0:3] == "lib":
target_dll = target_dll[3:]
tgtname, extname = os.path.splitext(target_dll)
if extname != ".dll":
tgtname = target_dll
if work_dir == "":
work_dir = cwd_path
print "working directory:", work_dir
print "======== make import (lib%s.lib) from (lib%s.dll) ========" % \
(tgtname, tgtname)
make_lib(work_dir, tgtname)
sys.exit(0)
由于近期开发windows x64程序,因此需要生成64位的DLL对应的LIB,于是我又在mklib-win32.py的基础上完成了mklib-win64.py的脚本,具体原理我就不说了,读者注意脚本之中的细微之处不难理解:
#!/usr/bin/python
# filename: mklib-win64.py
# make import x86_64 import-lib from windows x64 dll
# author: cheungmine@gmail.com
# date: 2013-5
# version: 0.1
#
# MinGW:
# $ python build-win64.py target_dll
# $ python build-win64.py libtiff-5.dll
# $ python build-win64.py libtiff-5
# $ python build-win64.py tiff-5
# $ python build-win64.py c:/path/to/libtiff-5
# ERROR: $ python build-win64.py c:\path\to\libtiff-5
#
# file operation:
# import shutil
#
## copy file:
# shutil.copy(myfile, tmpfile)
#
## copy time of file:
# shutil.copy2(myfile, tmpfile)
#
## copy file dir tree, the 3rd parameter means:
## True: symbol link
## False: use phyical copy
# shutil.copytree(root_of_tree, destination_dir, True)
###############################################################################
import os
import platform
import time
import getopt
import optparse
import sys
import string
###############################################################################
# get installed VS???COMNTOOLS environment:
###############################################################################
def get_vspath():
_vspath = os.getenv('VS110COMNTOOLS')
if not _vspath:
_vspath = os.getenv('VS100COMNTOOLS')
if not _vspath:
_vspath = os.getenv('VS90COMNTOOLS')
if not _vspath:
_vspath = os.getenv('VS80COMNTOOLS')
if not _vspath:
print "VS??COMNTOOLS not found"
sys.exit()
else:
print "VS80COMNTOOLS =", _vspath
else:
print "VS90COMNTOOLS =", _vspath
else:
print "VS100COMNTOOLS =", _vspath
else:
print "VS110COMNTOOLS =", _vspath
return _vspath
###############################################################################
# step (1): create a windows module definition: target_lib.def
# MSCMD:
# > dumpbin /EXPORTS target_lib.dll > ~target_lib.def
# or MinGW:
# $ pexports target_lib.dll > target_lib.def
# step (2): use this target_lib.def to create module import file: target_lib.lib
# MSCMD:
# > lib /def:target_lib.def /machine:amd64 /out:target_lib.lib
###############################################################################
def make_lib(workdir, tgtname):
print "[2-1] create a windows module definition: lib%s.def" % tgtname
dump_def = 'cd "%s"&pexports lib%s.dll > lib%s.def' % \
(work_dir, tgtname, tgtname)
ret = os.system(dump_def)
if ret == 0:
print "[2-2] use (lib%s.def) to create import module: lib%s.lib" % (tgtname, tgtname)
lib_cmd = 'cd "%s"&lib /def:lib%s.def /machine:amd64 /out:lib%s.lib' % (workdir, tgtname, tgtname)
cmds = 'cd "%s"&vcvarsall.bat x86_amd64&%s&cd "%s"' % (vs_path, lib_cmd, cwd_path)
ret = os.system(cmds)
if ret == 0:
print "INFO: mklib (%s/lib%s.lib) success." % (cwd_path, tgtname)
return 0;
else:
print "ERROR: mklib (%s/lib%s.lib) failed." % (cwd_path, tgtname)
return (-2)
else:
print "ERROR: mklib (%s/lib%s.def) failed." % (cwd_path, tgtname)
return (-1);
###############################################################################
# current directory:
cwd_path = os.getcwd()
vs_path = get_vspath() + "..\\..\\VC\\"
work_dir = "./"
# lib name == parent folder name
target_dll = "ERROR_dll_not_found"
if sys.argv.__len__() == 1:
work_dir, target_dll = os.path.split(cwd_path)
elif sys.argv.__len__() == 2:
work_dir = os.path.dirname(sys.argv[1])
target_dll = os.path.basename(sys.argv[1])
else:
print "ERROR: invalid argument"
sys.exit(-1)
if target_dll[0:3] == "lib":
target_dll = target_dll[3:]
tgtname, extname = os.path.splitext(target_dll)
if extname != ".dll":
tgtname = target_dll
if work_dir == "":
work_dir = cwd_path
print "working directory:", work_dir
print "======== make import (lib%s.lib) from (lib%s.dll) ========" % \
(tgtname, tgtname)
make_lib(work_dir, tgtname)
sys.exit(0)
我做了个试验,就是用MinGW构建sqlite3,默认没有构建出我需要的dll,于是我首先进入sqlite3.o所在的目录,运行下面的命令手工构建出dll:
$ gcc -shared -fPIC sqlite3.o -o libsqlite3.dll -s
然后用mklib-win64.py生成lib文件:
$ python mklib-win64.py libsqlite3.dll
最后生成了64位版本的libsqlite3.lib,通过VS2010编写的程序测试,完全正确。
#include "C:/DEVPACK/MinGW/local64/dst/sqlite3/include/sqlite3.h"
# pragma comment(lib, "C:/DEVPACK/MinGW/local64/dst/sqlite3/lib/libsqlite3.lib");
......
void test_sqlite3()
{
int ret;
sqlite3 *dbconn;
sqlite3_stmt *stmt;
char *errmsg;
ret = sqlite3_open_v2("C:/workspace/antelope/test.db", &dbconn,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX |
SQLITE_OPEN_SHAREDCACHE, 0);
if (ret != SQLITE_OK) {
fprintf(stdout, "ERROR: sqlite3_open_v2() error (%d).\n",
ret);
}
}
于是,便有了此文!