本节书摘来自异步社区《C程序设计新思维》一书中的第1章,第1.2节,作者 【美】Ben Klemens,更多章节内容可以访问云栖社区“异步社区”公众号查看
1.2 在Windows下编译C
在多数系统中,C享有一个中心的、贵宾礼遇的地位,以至于所有其他工具都处于从属的地位;但是在Windows机器中,C语言却被奇怪地忽略了。
所以我不得不花点时间讨论如何来准备好一台Windows机器以便用来写C程序。如果你现在不需要在Windows机器上编程,你可以直接跳到“1.3库的路径”。
这并非是针对Microsoft的,请不要用这样的角度来理解这一节。我无意去推测Microsoft的动机和商业战略。不过,如果你想在Windows机器上用C来工作,你需要知道实际状况(坦白地说,是不太友好)以及如何应对。
1.2.1 Windows中的POSIX环境
因为C和Unix是共同进化的,很难在谈到其中一个的时候不提及另一个。我个人认为从POSIX开始会比较容易些。并且,读者中那些希望在Windows机器上编译来自其他环境的代码的人会发现这么讲述也很自然。
总的来说,文件系统的世界可以(稍微有点重叠地)分为两个阵营:
POSIX兼容系统
Windows操作系统族
POSIX兼容并不一定意味着整个系统的外观和使用习惯都和Unix机器一样。比如,典型的Mac用户并不会意识到他们正在用一个界面漂亮的标准BSD系统,但是知道这一点的用户可以到Accessories->Uilities目录中,打开Terminal程序,并为他们心仪的内容运行ls、grep和make。
说实话,我怀疑很多系统是不是真的100%符合标准的要求(比如要具备一个Fortran′77编译器)。为了我们的目的,我们需要一个可以像POSIX shell那样运行的shell程序、一堆工具(sed、grep、make等)、一个C99编译器,以及一些标准C库之外的附加库,比如fork和iconv。这些都可以被添加进主系统。包管理器相关的脚本、Autotools和几乎所有的编写可移植代码的工具都在某种程度上依赖于上面这些工具,所以即便你不想整天盯着命令行,安装那些工具也是会带来一些方便的。
在服务器级的操作系统和Windows 7的全功能版本中,Microsoft提供了一个叫作SUA的子系统(Subsystem for Unix-based Application)——该系统以前称为INTERIX,用来提供通常的POSIX系统调用、Korn shell,以及gcc。这个子系统一般并不是默认提供的,但是可以作为一个插件安装。但是在其他的Windows版本,包括Windows 8上,并不提供SUA,所以我们不能依赖Microsoft为它自己的操作系统提供一个POSIX子系统。
既然这样,可以尝试Cygwin。
如果你计划从头编译安装Cygwin,你需要按照下面的步骤进行:
1. 写一个Windows下的C库,在该库中提供所有的POSIX函数。这需要你抹平一些Windows和POSIX系统之间的差异性,比如Windows中使用明确的盘符(例如:C:),而POSIX采用一个统一的文件系统。在这种情况下,需要为C:与 /cygdrive/c,D:与 /cygdrive/d等等路径表达之间建立别名。
2. 然后你就可以通过连接这个库来编译POSIX标准的程序了,可以尝试:制作一个Windows版本的ls、bash、grep、gcc、X、rxvt、libglib、perl、python,等等。
3. 一旦你已经编译了几百个程序和库,就需要安装一个包管理器以便用户可以从中选择需要安装的组件。
但作为Cygwin的用户,你所要做的只是从Cygwin的官方网站(http://cygwin.com/)下载该包管理器并安装相应的包。你当然想要前面列表的东西,外加一个体面的终端(试一下RXVT吧,或者安装X子系统并使用xterm),但是你将看到实际上这些奢侈品都与某种开发系统相似。现在你可以编译你的C代码了。
1.2.2 在POSIX下编译C
Microsoft在Visual Studio中提供了一个C++编译器,并兼容ANSI C。这是Microsoft提供的唯一一种编译C代码的途径了。很多Microsoft人士明确表示,对C99的支持是不会发生的(更别谈支持C11了)。Visual Studio是主流编译器中唯一至今还停留在C89标准的,因此我们必须从别处找到一个替代品。当然,Cygwin提供了gcc,如果你遵从并安装了Cygwin,那么你已经具有了一个完整的编译环境。
如果你在Cygwin下编译,程序就将依赖于Cygwin的库来提供POSIX函数,即cygwin.dll(不管你的代码实际上是否包含任何POSIX调用)。如果是在一台安装了Cygwin的机器上运行你的程序,那当然不会有问题。用户自然可以点击可执行文件并按照期望的行为运行,因为系统能够找到Cygwin的动态链接库。如果你随程序发布了cygwin1.dll,Cygwin编译的程序也可以在没有Cygwin的情况下运行。
在我的机器上,cygwin的路径是/bin/cygwin1.dll。cygwin1.dll文件有一个与GPL版权类似的授权(见“前言”),因此如果你从Cygwin中剥离发布DLL库,你必须发布你自己程序的源代码[2]。如果这对你是一个问题,那么就必须找到一个不依赖于cygwin1.dll的编译自己程序的方法,比如从代码中移除所有POSIX标准的函数然后使用后面将要讨论的MinGW。可以用cygcheck工具来找出程序所依赖的DLL,验证所生成的可执行文件是否连接了cygwin1.dll。
1.2.3 不在POSIX环境中编译C
如果你的程序不需要POSIX函数(比如fork或popen之类),那么你可以用MinGW(Minimalist GNU for Windows),它提供了一个标准的C编译器和一些基础工具。Msys是与MinGW伴生的,提供了另外的一些工具,例如shell。
缺少POSIX风格的外表并不是MinGW的真正问题。Msys提供了一个POSIX shell,或者干脆忘掉命令行并尝试一下Code::blocks,这是一个在Windows上使用MinGW的IDE集成环境。Eclipse是一个功能更加丰富的可以配置为与MinGW一起工作的IDE集成环境,虽然这需要一点配置工作。
不过如果POSIX命令行能让你感觉更舒服些,那就安装Cygwin,下载提供MinGW版本的gcc,用这些编译环境而不是用与POSIX连接的、默认版本的Cygwin gcc。
如果你还没有遇到过Autotools,那也快了。编译包的三个标志性命令:./configure、make和make install。Msys为这样管理包提供了足够的机制。如果你已经下载了从Cygwin的命令行编译的包,那你可以用下面的命令来配置这个包,使用Cygwin的Mingw32编译器来产生与POSIX无关的代码:
然后像通常那样运行make和make install。
在MinGW下编译后,不管是通过命令行还是Autotools,你都会得到一个Windows本地的二进制代码。因为MinGW并不知道cygwin1.dll的存在,你的程序并不会有任何POSIX调用,你得到的将是一个真正的Windows程序,没有人会知道你是从POSIX环境编译出来的。
然而,MinGW的真正问题是缺乏预编译库[3]。如果你想摆脱cygwin1.dll,那么你也不能使用与Cygwin一起发行的libglib.dll版本。你将必须从源代码重新编译GLib成一个Windows DLL——但是Glib在国际化方面依赖GNU的gettext,所以你将先把那个库编译一遍。现代代码依赖于现代的库,所以你可能会发现自己花费了很多时间来处理类似的工作,而在别的系统上可能只是一行包管理器的命令。如果这样,我们就真的如某些人所说,C已经40岁了,你需要从头写每样东西。
所以,下面就是几句忠告。Microsoft已经离开了对话,任由其他人实现后grunge时代[4]的C编译器。Cygwin完成了这些工作并提供了全功能的包管理器,带有可以满足你的多数或全部工作的足够多的库,但这些都是伴随着POSIX风格的代码和Cygwin的DLL的。如果你觉得这是一个问题,那么你将不得不多做很多工作,以重建那些你体面的代码所需要的整个环境和库。