【问题标题】:Using make for cross platform compilation使用 make 进行跨平台编译
【发布时间】:2012-04-05 04:12:36
【问题描述】:

我目前正在 Linux 和 Win32 下开发一个 C 项目。 'deliverable' 是一个共享库,所有的开发都是在 Linux 下使用 GNU 工具链完成的。我正在使用 Makefile 来编译共享库。

时不时我必须从同一个 src 在 Win32 下构建一个 .dll。

我已经在 Win32 机器上安装了 MinGW,这样我就可以使用 make 并从编译器获得更少的投诉(与 MSVC 相比)。我正处于 src 代码在两个平台上都可以编译的阶段

但是 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 个共享库)

【问题讨论】:

    标签: c makefile cross-platform


    【解决方案1】:

    几年前我遇到过类似的问题,发现cmake 更容易进行跨平台编译,并且会使用该系统的任何本机编译器。语法更清晰,并抽象出大部分不必要的细节(有时会妨碍,但通常有办法绕过它)

    【讨论】:

    • 您需要编写一个工具链文件让 CMake 获取 gcc,还是它会自动检测 MinGW 并通过 MSVC 选择它?
    • 您只需通过指定生成器(选项 -G)来指定您想要的编译器。详情在cmake.org/cmake/help/cmake-2-8-docs.html#section_Generators
    • 我一直在 windows 上使用 VS2010,在 *nix 上使用 GCC,两者都是自动拾取的。如果你没有 VS,它可能会自动检测并使用 MinGW,但如果它不自动检测,你总是可以指定生成器
    • 我不会说 CMake 对跨平台编译很有用。它对多平台很有用,因为一组 CMake 文件可以在 CMake 支持的所有平台上使用。它并不适合在一个平台上创建某种 make 文件,然后在其他平台上无需 CMake 即可使用。它也不理解为一个平台在不同平台上编译的交叉编译器。
    【解决方案2】:

    作为主要建议,我建议使用 libtool、autoconf 和 automake;它们使交叉编译变得非常容易,而且比 CMake 容易得多。

    如果您要走手工路线,我建议您使用不同的目标。在 makefile 之间切换往往会隐藏 Makefile 中其他明显的错误,例如重复使用具有不同规则的对象。示例:对象 foo.o 是为 DLL 目标和 .so 目标编译的,但具有不同的标志。如果有人切换 Makefile,则会使用带有错误标志的现有 .o 文件,从而破坏构建。如果您使用的是一个 Makefile,这将通过规则冲突变得明显。

    【讨论】:

    • 自动工具在工作时很棒,但情况并非总是如此;例如,我只能通过向make 提供显式配置参数来让 git 在 MinGW 上构建,而不是通过自动化系统...
    【解决方案3】:

    使用单个 make 文件并将平台特定信息放在 conditionals 中,例如

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

    【讨论】:

    • 虽然这种方法可能存在一些问题(不是非常模块化,每次make 调用都重新运行功能检测),但我认为它们对您的用例并不重要;此外,GNU make 足够强大,可以在其之上构建配置系统(例如,您可以自动生成 makefile sn-ps 到 include 并通过 $(shell) 运行任意代码,通过$(info)$(error), ...)
    • oreilly.com/catalog/make3/book/ch07.pdf 阅读本书以编写可移植的 Makefile。
    【解决方案4】:

    作为同时使用 autotools 和 CMake 的人,我建议使用 CMake 而不是滚动您自己的 Makefile 和使用 autotools。 CMake 有很多有用、易于使用的好处,即使它是一个简单的项目。例如,CMake 将创建一个 NSIS 安装程序,管理生产与调试编译,并拥有一个很好的测试框架。我的一个打击是很难找到如何使用它的真实例子。如此多的开源软件使用自动工具,以至于很容易找到真实世界的示例。但是如果你下载了CMake源码,Example目录和Test目录下都有很多例子。

    换句话说,果汁值得挤压。

    【讨论】:

    • 上述来源的链接?
    【解决方案5】:

    我在Makefile 中使用UNAME := $(shell uname) 来检测平台(Linux 或MS-Windows)。

    我在下面提供了一个基于makegcc 的完整示例来构建共享库:*.so*.dll,具体取决于平台。

    这个例子是基本/简单/愚蠢的,更容易理解:-)

    要在 MS-Windows 上使用 makegcc,可以安装 CygwinMinGW

    该示例使用了五个文件:

     ├── app
     │   └── Makefile
     │   └── main.c
     └── lib
         └── Makefile
         └── hello.h
         └── hello.c
    

    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
    

    【讨论】:

      【解决方案6】:

      如果你愿意在 Windows 上使用 MSYS2,你可能会得到它运行 与为 Linux 编写的代码相比,进行任何更改。 这适用于您的 C/C++ 源代码以及您的 makefile。(!)

      我一直在专门为 Linux 开发代码。当我尝试运行它时 在 MSYS2 终端中,代码运行得很好,并且 生成了一个 Windows 二进制可执行文件。我非常惊讶。

      当然,您需要知道如何安装和使用 MSYS2。例如, 要安装 make 和 g++,在 MSYS2 终端中运行命令:

      yes | pacman -Syu msys/make
      yes | pacman -Syu gcc
      

      如果你想找出 Windows g++ 的安装位置,你可以运行 where g++ 在 MSYS2 终端中。

      参考资料:
      https://www.msys2.org/wiki/MSYS2-installation/
      https://github.com/msys2/MSYS2-packages/issues/293

      【讨论】:

        猜你喜欢
        • 2010-11-09
        • 2012-03-19
        • 2014-09-28
        • 2010-10-16
        • 2013-04-08
        • 1970-01-01
        • 1970-01-01
        • 2023-03-27
        • 2015-06-10
        相关资源
        最近更新 更多