从DLL生成LIB
cheungmine
2013-5-14
windows程序链接到一个动态链接库.dll时需要一个导入库.lib。遗憾的是这样的导入库很多时候是不存在的,那么就需要我们自己从.dll生成对应的导入库.lib。假设我们的windows程序为WinApp.exe,它静态链接到一个动态库libABC.dll。我们在WinApp.exe的源代码中可以这样写:
// (程序清单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); } }
于是,便有了此文!