在将代码从Linux平台移植到Windows平台的时候, 首先想到的是Cygwin这个项目, 其核心是利用cygwin1.dll来模拟linux接口, 可是这样也带来了一个问题, 就是只能用Cygwin里面自带的GCC编译环境来编译程序, 不能在MinGW或VS中编译。因此便想到了在MinGW以及VS中使用cygwin1.dll。html
对于这个问题, Cygwin官方的FAQ有相关的解释说明:How do I use cygwin1.dll
with Visual Studio or Mingw-w64。从中能够看到, 对于在MinGW和VS中动态调用cygwin1.dll, 官方有具体的说明, 只要按照上面所写的去作就能够了。可是对于静态调用cygwin1.dll, 官方并无尝试过, 可是有人在mailinglist中分享了本身的操做步骤。本身分别在MinGW和VS中尝试并改进了一下, 也成功了, 因而在这里分享一下个人具体步骤。linux
1 . 在MinGW中调用cygwin1.dllgit
在MinGW中调用cygwin1.dll与在Cygwin中直接编译程序有点相似。首先, 去找一个Cygwin的源, 如http://mirrors.ustc.edu.cn/cygwin/x86/release/cygwin/, 下载最新版本的源代码, 如cygwin-2.5.2-1-src.tar.xz, 而后将其解压, 提取其中的winsup\cygwin\crt0.c文件, 其部分源代码以下:github
/* crt0.c This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ /* In the following ifdef'd i386 code, the FPU precision is set to 80 bits and all FPU exceptions are masked. The former is needed to make long doubles work correctly. The latter causes the FPU to generate NaNs and Infinities instead of signals for certain operations. */ #include "winlean.h" #include <sys/cygwin.h>
注意到crt0.c中引用了winlean.h这个头文件, 因此在提取时要将crt0.c与winlean.h一块儿提取出来。这个是在编译时要加入到工程中的源文件。bash
接着下载最新版本的编译好的二进制文件, 如cygwin-2.5.2-1.tar.xz, 解压, 找到并提取usr\bin\cygwin1.dll文件, 这个是最后程序运行所须要的动态库文件。函数
最后, 到源(如http://mirrors.ustc.edu.cn/cygwin/x86/release/cygwin/cygwin-devel/)里面去下载dev库, 如cygwin-devel-2.5.2-1.tar.xz, 解压, 找到并提取其中的usr\lib\libcygwin.a文件, 这个是连接时用到的导入库。工具
以上步骤仅仅是准备工做, 接下来开始编译及连接源程序。命令行
把crt0.c和winlean.h文件添加进项目源代码中, 而后正常编译, 生成目标文件。code
连接libcygwin.a库, 能够用-lcygwin, 也能够直接把libcygwin.a做为参数传给编译器, 而后再额外添加以下选项:orm
-nostartfiles -e mainCRTStartup
若是不添加-nostartfiles选项, 就会报符号重定义的错误。选项-e mainCRTStartup指明程序入口点为mainCRTStartup, 但实际使用时, MinGW的GCC总会报一个warning说"cannot find entry symbol mainCRTStartup; defaulting to 00401000"。可是最终的程序是能够正常运行的。
2 . 在VS中调用cygwin1.dll
首先, 新建一个源文件my_crt0.c, 内容以下:
#include <sys/cygwin.h> #include <stdlib.h> typedef int (*MainFunc) (int argc, char *argv[], char **env); void my_crt0 (MainFunc f) { cygwin_crt0(f); }
其中, my_crt0函数名能够改成任意符合C语言的命名规范的函数名, 只是要记住后续的操做也应跟着同步修改。同时, 在代码中main函数的定义应尽可能与my_crt0.c中的声明保持一致, 防止出现一些比较奇怪的问题。
而后, 在Cygwin的命令行环境下用gcc编译并连接这个文件:
gcc -shared my_crt0.c -o my_crt0.dll -Wl,--output-def,my_crt0.def
在VS的命令行环境下用上一步骤生成的def文件生成my_crt0.dll的导入库, 用于后续的连接步骤:
lib /def:my_crt0.def /out:my_crt0.lib
接着, 去newlib-cygwin的镜像上面去下载Cygwin 1.7.5版本的源代码并解压, 找到并提取winsup\cygwin\crt0.c文件, 这个文件是在编译时要加入工程中的源文件。注意, 这里选用的版本是1.7.5的。至于为何选这个版本, 将代码比对一下就能够看出来。这是cygwin1.7.5版本的crt0.c的部分代码:
void mainCRTStartup () { #ifdef __i386__ //... #endif cygwin_crt0 (main); }
这是cygwin1.7.5版本后的crt0.c的部分代码:
void mainCRTStartup () { #ifdef __i386__ //... #endif cygwin_crt0 (main); /* These are never actually called. They are just here to force the inclusion of things like -lbinmode. */ cygwin_premain0 (0, NULL, NULL); cygwin_premain1 (0, NULL, NULL); cygwin_premain2 (0, NULL, NULL); cygwin_premain3 (0, NULL, NULL); }
代码中主要多了cygwin_premainx()函数, 虽然从注释看出这些调用并无什么实际用处, 可是为了不出现一些奇怪的问题, 仍是用老版本为好。
在提取crt0.c以后, 对其进行修改, 将其中的cygwin_crt0调用修改成my_crt0, 即crt0.c中调用的函数应与my_crt0.c中的函数保持一致。
而后, 去MYSY2的源里面下载工具mingw-w64-i686-tools-git-x.x.x.xxxx.xxxxxxx-x-any.pkg.tar.xz, 而后解压, 找到里面的gendef.exe, 用以下命令生成cygwin1.dll的def文件:
gendef cygwin1.dll
按照刚才生成my_crt0.lib的方法去从cygwin1.def生成cygwin1.lib, 用于后续的连接步骤:
lib /def:cygwin1.def /out:cygwin1.lib
至此, 准备工做完成, 而后开始编译和连接源程序。
将crt0.c加入工程完成编译生成目标文件便可。
连接时, 把my_crt0.lib和cygwin1.lib文件加入连接器, 同时给传递以下选项以设置程序入口点:
/entry:mainCRTStartup
还有, 运行时不要忘了加上cygwin1.dll动态库。若是在编译时报错, 能够先在MinGW中编译成目标文件, 而后再在VS中完成连接。另外, 这个方法一样适用于MinGW。
从上面的整个流程来看, 比较繁琐的仍是在VS中调用cygwin1.dll。并且, 源代码在VS中还不必定可以完成编译, 须要借助MinGW编译成目标文件, 而后才能继续在VS中使用。所以, 通常状况下能够优先考虑在MinGW中使用cygwin1.dll。