使用make进行跨平台编译

37

我目前正在Linux和Win32下开发一个C项目。 "可交付物" 是一个共享库,所有开发都在Linux下使用GNU工具链完成。 我正在使用Makefile编译共享库。

偶尔我需要从相同的源代码在Win32下构建.dll

我已经在Win32上安装了MinGW,以便我可以使用make并且与MSVC相比,编译器投诉要少得多。 我已经处于这样的阶段,即源代码在两个平台上都可以编译。

但是Linux Makefile和Win32 Makefile是不同的。 我很好奇如何最好地处理这个问题 - 我应该:

  1. 有2个Makefile,例如Makefile for linux和Makefile.WIN32,然后在Windows上运行 make -f Makefile.WIN32

  2. 我应该在单个Makefile中制作不同的目标,并在Windows上执行像 make WIN32 这样的操作吗?

  3. 我应该放弃make并使用CMake(是否值得为这样一个简单的项目,即1个共享库努力)

6个回答

34

使用单个make文件,并将平台特定信息放入条件语句中,例如:

ifeq ($(OS),Windows_NT)
    DLLEXT := .dll
else
    DLLEXT := .so
endif

DLL := libfoo$(DLLEXT)

lib : $(DLL)

虽然这种方法可能存在一些问题(不太模块化,在每个make调用上重新运行特征检测),但我认为它们对您的使用情况并不重要;此外,GNU make足够强大,可以在其上构建配置系统(例如,您可以自动生成要include和通过$(shell)运行任意代码的makefile片段,通过$(info)$(error)生成诊断信息等)。 - Christoph
请查看此书以了解编写可移植的Makefile。链接为:http://oreilly.com/catalog/make3/book/ch07.pdf - zhanxw

20
我在我的Makefile中使用UNAME := $(shell uname)来检测平台(Linux或MS-Windows)。
下面提供了一个基于makegcc的完整示例,用于构建共享库:*.so*.dll,具体取决于平台。
该示例非常基本/简单/愚蠢,以便更易于理解 :-)
要在MS-Windows上使用makegcc,可以安装CygwinMinGW
该示例使用五个文件:
 ├── app
 │   └── Makefile
 │   └── main.c
 └── lib
     └── Makefile
     └── hello.h
     └── hello.c

The Makefiles

app/Makefile

app.exe: main.o
        gcc -o $@ $^ -L../lib -lhello
        # '-o $@'    => output file => $@ = the target file (app.exe)
        # '   $^'    => no options => Link all depended files 
        #            => $^ = main.o and other if any
        # '-L../lib' => look for libraries in directory ../lib
        # '-lhello   => use shared library hello (libhello.so or hello.dll)

%.o: %.c
        gcc -o $@ -c $< -I ../lib
        # '-o $@'     => output file => $@ = the target file (main.o)
        # '-c $<'     => COMPILE the first depended file (main.cpp)
        # '-I ../lib' => look for headers (*.h) in directory ../lib

clean:
        rm -f *.o *.so *.dll *.exe

lib/Makefile

UNAME := $(shell uname)

ifeq ($(UNAME), Linux)
TARGET = libhello.so
else
TARGET = hello.dll
endif

$(TARGET): hello.o
        gcc  -o $@  $^  -shared
        # '-o $@'    => output file => $@ = libhello.so or hello.dll
        # '   $^'    => no options => Link all depended files => $^ = hello.o
        # '-shared'  => generate shared library

%.o: %.c
        gcc  -o $@  -c $<  -fPIC
        # '-o $@' => output file => $@ = the target file (main.o)
        # '-c $<' => compile the first depended file (main.cpp)
        # '-fPIC' => Position-Independent Code (required for shared lib)

clean:
        rm -f *.o *.so *.dll *.exe

源代码

app/main.c

#include "hello.h" //hello()
#include <stdio.h> //puts()

int main()
{
    const char* str = hello();
    puts(str);
}

lib/hello.h

#ifndef __HELLO_H__
#define __HELLO_H__

const char* hello();

#endif

lib/hello.c

#include "hello.h"

const char* hello()
{
    return "hello";
}

编译

修复Makefiles的复制粘贴问题(将前导空格替换为制表符)。

> sed  -i  's/^  */\t/'  */Makefile

make 命令在两个平台上都相同。给出的输出是针对 MS-Windows 的(已删除不必要的行)。

> cd lib
> make clean
> make
gcc  -o hello.o  -c hello.c  -fPIC
gcc  -o hello.dll  hello.o  -shared
> cd ../app
> make clean
> make
gcc -o main.o -c main.c -I ../lib
gcc -o app.exe main.o -L../lib -lhello

运行

该应用程序需要知道共享库的位置。

在MS-Windows上,简单/基本/愚蠢的方法是将库复制到应用程序所在的位置:

> cp -v lib/hello.dll app
`lib/hello.dll' -> `app/hello.dll'

在Linux上,使用LD_LIBRARY_PATH环境变量:
> export LD_LIBRARY_PATH=lib

在两个平台上,运行命令行和输出结果都是相同的:

> app/app.exe
hello

7
作为一个既使用过autotools又使用过CMake的人,我建议使用CMake而不是自己编写Makefiles或使用autotools。即使是一个简单的项目,CMake也有许多有用且易于使用的好处,例如,CMake将创建一个NSIS安装程序,管理生产与调试编译,并拥有一个不错的测试框架。我唯一的抱怨是它很难找到真正的使用示例。因为很多开源软件都使用autotools,所以可以很容易地找到它的实际应用示例。但是,如果您下载CMake源代码,则在Example目录和Test目录中有很多示例。
换句话说,值得一试。

2
提供链接到相关资源? - Alex Gray

5

几年前我遇到了类似的问题,发现cmake更适合跨平台编译,并且会使用本地系统的编译器。语法更清晰,抽象出大部分不必要的细节(有时候会妨碍,但通常可以绕过这些问题)。


你需要编写一个工具链文件来让CMake选择gcc,还是它会自动检测MinGW并选择它而不是MSVC? - Christoph
你只需要通过指定生成器(选项-G)来指定想要的编译器。详情请参见http://www.cmake.org/cmake/help/cmake-2-8-docs.html#section_Generators。 - Taylor Southwick
我之前一直在 Windows 上使用 VS2010,在 *nix 上使用 GCC,两者都能自动识别。如果你没有 VS,它可能会自动检测并使用 MinGW,但如果它无法自动检测,你总可以指定生成器。 - Taylor Southwick
2
我不会说CMake对于跨平台编译很有用。它对于多平台是有用的,因为一组CMake文件可以在CMake支持的所有平台上使用。但是,它不容易创建某种类型的make文件,然后在其他平台上无需CMake即可使用。它也不理解交叉编译器,这些编译器在不同的平台上编译一个平台的代码。 - fork2execve

3
作为重要建议,我建议使用libtool、autoconf和automake;它们使交叉编译非常容易,比CMake更容易。
如果你选择手动构建路线,我建议使用不同的目标。在不同的makefile之间切换倾向于隐藏本来很明显的Makefile错误,例如使用不同规则的重复对象。例如:将对象foo.o分别编译为DLL目标和.so目标,但使用了不同的标志。如果有人切换Makefiles,则使用现有的带有错误标志的.o文件,从而破坏了构建过程。如果您使用一个Makefile,则可以通过规则冲突发现此问题。

自动工具在正常情况下非常好用,但并非总是如此;例如,我只能通过向“make”提供显式配置参数来构建MinGW上的git,而不是通过自动化系统。 - Christoph

2
如果您愿意在Windows上使用MSYS2,与您为Linux编写的代码相比,您可能会使其运行而不需要进行任何更改。这适用于您的C/C++源代码以及您的makefile文件。(!)
我一直在专门为Linux开发代码。当我尝试在MSYS2终端内运行它时,代码运行得非常好,并且生成了Windows可执行二进制文件。我感到非常惊喜。
当然,您需要知道如何安装和使用MSYS2。例如,在MSYS2终端中安装make和g++,请运行以下命令:
yes | pacman -Syu msys/make
yes | pacman -Syu gcc

如果你想找出Windows中g++安装的位置,可以在MSYS2终端中运行where g++
参考资料: https://www.msys2.org/wiki/MSYS2-installation/ https://github.com/msys2/MSYS2-packages/issues/293 https://superuser.com/questions/21067/windows-equivalent-of-whereis#39309

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接