【问题标题】:Use the same makefile for make (Linux) and nmake (Windows)对 make (Linux) 和 nmake (Windows) 使用相同的 makefile
【发布时间】:2012-01-06 09:50:02
【问题描述】:

我有一个简单的 C 程序(一个源文件),我想分别通过 make 和 nmake 在 Linux 和 Windows 上编译它。有没有可能用一个makefile来完成这个?

我想过类似的事情

ifeq($(MAKE), nmake)
    // nmake code here
else
    // make code here
endif

不幸的是,nmake 似乎不理解ifeq,所以我不能使用它。我有一个可以工作的 makefile,但这会产生非常难看的结果:

hello: hello.c
    $(CC) hello.c

这适用于两个系统。问题是结果取决于各自编译器的默认行为。在 Linux 下,我得到一个名为 'a.out' 而不是 'hello' 的可执行文件。在 Windows 下,我得到了“hello.exe”,但也有我不想拥有的“hello.obj”。

还有其他方法吗?还是我正在尝试的事情绝对不可能?

【问题讨论】:

  • 您可以在您的 Windows 系统上构建或获取 GNU make。
  • 对于跨平台项目我可以推荐 cmake (cmake.org)。
  • 我已经阅读了关于 cmake 的内容。但我需要在大学里进行练习,我们应该使用 nmake。

标签: c makefile nmake


【解决方案1】:

解决方案:https://github.com/jaykrell/w3/blob/master/Makefile

# This one Makefile works with Microsoft nmake and GNU make.
# They use different conditional syntax, but each can be
# nested and inverted within the other.

all: default

ifdef MAKEDIR: # gmake: false; nmake: unused target
!ifdef MAKEDIR # gmake: not seen; nmake: true

#
# Microsoft nmake.
#

!else # and now the other
else

#
# GNU (Posix?) make.
#

endif    # gmake: close condition; nmake: not seen
!endif : # gmake: unused target; nmake close conditional

default: # default target for both

【讨论】:

    【解决方案2】:

    我想使用 Make 和 NMAKE 使用的相同 makefile 包含。由于 make 识别注释行的续行,但 NMAKE 不识别,这意味着我们可以对 Make 和 NMAKE 有单独的指令。例如:

    # NMAKE code here \
    !ifndef 0 # \    
    MV=move # \
    RM=del # \
    CP=copy # \
    !else
    # Make code here
    MV=mv -f
    RM=rm -f
    CP=cp -f
    # \
    !endif
    

    您只需确保特定于 NMAKE 的代码包含在 # \ 中。

    【讨论】:

    • 这行得通,尽管 nmake 不允许在命令上使用 cmets。您只能将此技术用于宏和 单行 命令。不过,您可以使用 ; 连接多个命令以将它们保持在一行中。
    • 这在 VS2017 中不起作用 - 遗憾的是,反斜杠被解释为预期。
    • 这太棒了!
    【解决方案3】:

    您应该考虑为此使用CMake。使用一个源文件,应该很容易。以下是如何建立一个简单的项目:

    cmake_minimum_required(VERSION 3.10)
    
    # set the project name
    project(Hello)
    
    # add the executable
    add_executable(Hello hello.c)
    

    要构建简单项目,您需要执行以下操作(假设您的源文件和 CMakeLists.txt 文件与源文件 hello.c 位于同一目录中:

    mkdir build
    cd build
    cmake ..
    cmake --build .
    

    【讨论】:

    • @JohanBezem 当我发布此答案时,该评论不可用。但是,我认为这个答案是在不受家庭作业的人为限制时解决问题的正确方法......
    • CMake 可以生成 nmake,因此使用 CMake 并不难。至少值得考虑。
    • 我不确定为什么到目前为止没有人批评这个答案。首先,虽然使用 CMake 是一个规范的选择,但它不是问题的答案。其次,Windows 没有make 命令,因此首先是这个问题。如果你想使用 CMake 来构建,那么cmake --build .. 是正确的方法。我要编辑这篇文章,这样对其他读者会有帮助。
    【解决方案4】:

    我的解决方案是使用两个不同的文件名。 (由于不同操作系统的Makefile名称搜索优先级不一样)

    对于 Windows,我使用普通的“Makefile”。

    对于 Linux,我根据this article 使用特殊的“GNUmakefile”。

    这样,nmake (Win) 会找到“Makefile”,而 make (Linux) 会找到“GNUmakefile”。

    【讨论】:

    • 您是要回答还是要问问题?即使两者都有,请删除询问部分。通过“您的答案”字段和“发布您的答案”按钮发布的帖子只能是答案,而不是问题。
    • 这是我的解决方案。由于 WIndows 和 Linux 中默认的 Makefile 名称搜索优先级不同,因此该方案可行。
    • 你们两个为什么不试着理解我的回复?
    • 我同意和不同意@Das_Geek 关于您的回复是否回答了发布的问题。我认为您暗示“是否有可能使用单个 makefile 完成此操作?”的答案?没有。然后你继续为这个问题提供一种替代方法——我认为这很好,但严格来说并不是 OP 所要求的。 IMO,这个问题有点松散,你的答案也是。 GIGO
    【解决方案5】:

    我最近尝试使用 C 预处理器从包含预处理器符号的模板 Makefile.cc 生成可移植的 Makefile。到目前为止,它的效果出奇的好。第一个观察结果是 NMAKE 将预扫描一个 Tools.ini 文件,我在同一目录中提供该文件

    [NMAKE]
    MAKECONFIG=-D_NMAKE
    

    然后我旁边有一个“真正的”Makefile,它只用 GNU Make 和 NMAKE 的通用子语言编写。

    MAKEFILE=Makefile.mk
    TEMPLATE=Makefile.cc
    
    all: $(MAKEFILE)
        $(MAKE) -f $(MAKEFILE)
    
    clean: $(MAKEFILE)
        $(MAKE) -f $(MAKEFILE) clean
    
    $(MAKEFILE): $(TEMPLATE)
        $(CXX) $(MAKECONFIG) -E $(TEMPLATE) > $(MAKEFILE)
    

    请注意,-E 开关对于仅用于预处理文件的编译器(至少我使用的三大编译器:GCC、Clang 和 CL)非常常见。使用 GNU Make,$(MAKECONFIG) 扩展为空,但在 NMAKE 中,它提供了声明自身的预处理器变量。由于您的模板 Makefile.cc 可以使用 #ifdef 检查它,以及检查编译器用来声明自身的公共变量,因此您可以为“make”程序、您的操作系统和您正在使用的编译器。

    如果你有任何“make”,你可能也已经有一个 C 编译器;无需安装其他软件,如 CMake 或 autotools。它使用旧的机制,因此很可能在很多环境中工作。据我目前所知,它真的很快。至少比在 autotools 中运行配置步骤快。我面临的唯一缺点是它将 Make 规则的样式限制在同一行,因为预处理器会更改代码的缩进。预处理器也会吐出带有 # 标记的行,但由于这些在 Makefile 中开始注释,因此它们无论如何都会被忽略。

    A 有一个有点小的 C++ 项目,其 Makefile.cc 看起来像下面的 sn-p。它使用 GCC、Clang 或 CL 在 GNU Make 或 NMAKE 上以及在 Windows 或 POSIX 环境中编译。不过,我还没有支持 BSD Make 或测试任何其他编译器。

    // Make Version
    
    #ifdef _NMAKE
    # define ifdef !ifdef
    # define ifndef !ifndef
    # define else !else
    # define endif !endif
    # define err(x) !error x
    # define cat(x, y) x=$(x) y
    #else // GNU Make
    # define err(x) $(error x)
    # define cat(x, y) x += y
    #endif
    
    // System Commands
    
    ifdef SHELL
    RM=rm -f
    else
    ifdef COMSPEC
    RM=del /f
    else
    err("Cannot determine your system commands.")
    endif // COMSPEC
    endif // SHELL
    
    // Project Variables
    
    STD=c++17
    SRC=test.cpp dbg.cpp dir.cpp dll.cpp env.cpp err.cpp fifo.cpp file.cpp shm.cpp sig.cpp socket.cpp sys.cpp xdg.cpp
    BIN=test
    
    .SUFFIXES: .cpp .hpp .o .d .obj .pdb .lib .exp .ilk .log .i .db
    
    // Operating system
    
    #ifdef _WIN32
    cat(CFLAGS, -D_WIN32)
    EXE=$(BIN).exe
    #else
    cat(CFLAGS, -D_POSIX_C_SOURCE)
    cat(LDFLAGS, -ldl -lrt -lpthread)
    EXE=$(BIN)
    #endif
    
    // Make Targets
    
    all: $(EXE)
    
    clean: ; $(RM) $(EXE) *.o *.d *.obj *.pdb *.lib *.exp *.ilk *.log *.i
    
    // Compiler Options
    
    #ifdef _MSC_VER
    
    cat(CFLAGS, -nologo -std:$(STD) -W4 -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS -EHsc -permissive-)
    ifndef NDEBUG
    cat(CFLAGS, -Zi)
    endif
    cat(LDFLAGS, -nologo)
    
    OBJ=$(SRC:.cpp=.obj)
    
    $(EXE): $(OBJ); $(CXX) $(LDFLAGS) $(OBJ) -Fe$@
    .cpp.obj: ; $(CXX) $(CFLAGS) -c $<
    
    #elif defined(__GNUC__) || defined(__llvm__) || defined(__clang__)
    
    cat(CFLAGS, -std=$(STD) -Wall -Wextra -Wpedantic -MP -MMD)
    ifndef NDEBUG
    cat(CFALGS, -g)
    endif
    cat(LDFLAGS, -rdynamic)
    
    OBJ=$(SRC:.cpp=.o)
    
    $(EXE): $(OBJ); $(CXX) $(LDFLAGS) $(OBJ) -o $@
    .cpp.o: ; $(CXX) $(CFLAGS) -c $<
    
    # ifndef _NMAKE
    -include $(SRC:.cpp=.d)
    # endif
    #else
    # error "Cannot determine your compiler."
    #endif
    

    【讨论】:

      【解决方案6】:

      我无法找到一种方法来使用通用的 makefile 来同时为 GNU Make 和 Microsoft NMAKE 工作,主要是因为它们的“include”和/或“if”指令的语法不兼容。 Microsoft NMAKE 需要使用 !指令的前缀。例如,!if、!include 等...

      如果允许它有单独的宏,但是,它可能会被欺骗。在这里,我介绍了迄今为止我发现的通过观察以下几点来使 makefile 兼容 GNU Make 和 Microsoft NMAKE 的最佳方法:

      1. Microsoft NMAKE 读取 TOOLS.ini 文件以获取默认宏。
      2. Microsoft 套件使用 .obj 作为目标文件扩展名。
      3. GNU Make 读取在 MAKEFILES 环境变量中定义的文件。
      4. GNU 套件使用 .o 作为目标文件扩展名。
      5. GNU make 不需要为目标提供可执行扩展名 .exe。

      注意:以下内容已使用 Microsoft Visual Studio 2015 和 MINGW32 进行测试。

      第1步:创建以下DOS批处理文件,并在调用CMD提示时让它运行。

      set MAKEFILES=TOOLS.gcc
      call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
      

      第 2 步:在您的工作目录下创建一个 TOOLS.ini 文件,如下所示:(此文件独立于您的项目依赖项,可能的库除外)

      [NMAKE]
      LDLIBS  =
      CDEBUG  = /Zi
      LDEBUG  = /debug:full
      WDFLAGS = /wd4996 /wd4774 /wd4018 /wd4710 /wd4820
      CFLAGS  = /nologo $(CDEBUG) /EHsc /Wall $(WDFLAGS)
      LDFLAGS = /nologo $(LDEBUG)
      RM      = del /F /Q
      LINK     = "$(VCINSTALLDIR)bin\link" $(LDFLAGS)
      CP    = copy
      CC    = cl
      CPP = $(CC) /P
      X    = .exe
      O    = .obj
      
      .obj.exe:
          $(LINK) $** $(LOADLIBES) $(LDLIBS) /Out:$@
      

      第 3 步:在您的工作目录下创建一个 TOOLS.gcc,如下所示:(此文件独立于您的项目依赖项,可能的库除外)

      LD_LIBS =
      LDLIBS  =
      CDEBUG  = -g
      LDEBUG  = -g
      CFLAGS  = $(CDEBUG)
      LDFLAGS = $(LDEBUG)
      RM      = rm -f
      LINK     = gcc $(LDFLAGS)
      CP        = cp
      CC        = gcc
      CPP     = $(CC) -E
      X        =
      O        = .o
      
      %: %.o
          $(LINK) $^ $(LOADLIBES) $(LDLIBS) -o $@
      

      第 4 步:编辑您的 makefile,如下所示(注意 $(X) 和 $(O)),其中仅指定了依赖项。

      SHELL    = /usr/bin/sh
      app: app1$(X) app2$(X)
      app1$(X): app1$(O)
      app2$(X): app2$(O)
      
      clean:
          $(RM) *.exe *.o *.obj *.ilk *.pdb *.tmp *.i *~
      

      第 5 步:使用相同的 makefile 享受 GNU Make 和 Microsoft NMAKE

      $ nmake
      $ make clean
      $ nmake clean
      $ make
      

      【讨论】:

        【解决方案7】:

        是的,你可以用一个 Makefile 来做到这一点。该材料的最佳来源是 O'Reilly 的书:

        Managing Projects with GNU Make, Third Edition By Robert Mecklenburg

        参见第 7 章:可移植 Makefile。

        总之,该技术是测试环境变量 ComSpec,它表示 Windows 命令解释器是否存在:

        ifdef COMSPEC
          MV ?= move
          RM ?= del
        else
          MV ?= mv -f
          RM ?= rm -f
        endif
        

        我用一个可移植的 shell 脚本来包装它,它使用 sed 来编辑 Nmake 或 GNU make 的 makefile...

        【讨论】:

        • hm,因为?= 语法对nmake 无效。我认为您的示例仅检测 GNU make 是否在 W32 上运行(而不是作为 nmake/GNUmake 多语言))
        • 还不错; otoh,由于可移植的 shell 脚本是真正回答问题的神奇粘合剂,因此分享它可能会很好:-)
        • 投了反对票,因为 GCC 也存在于 Windows 上,并且在 Windows 上很常用,这意味着 Windows 用户在遇到这种 makefile 时会更加头疼。
        • @Dúthomhas 是的;我使用这种技术的脚本可以在 windows 或 CL 上使用 GCC。使用正确编写的脚本应该不是问题。
        【解决方案8】:

        我只是想到了一些完全不同的东西。

        如果你坚持使用极其简单的 Makefile(你说它可以工作),只需将“标准”变量 CC 和 CFLAGS 放在各自的环境中,比如说

          export CC=gcc
        

        分别

          set CC=CL.EXE
        

          export CFLAGS=-o myexecutable
        

        分别

          set CFLAGS=/out:myexecutable.exe
        

        它可能只是工作。

        请注意,我不确定要使用的确切选项,您必须自己弄清楚。但是 AFAIK 都使变体识别同一组标志。您甚至可以在各自的命令行上设置它们(但不在 makefile 中,因为 NMAKE 使用不同的 'ifeq' 语法......)

        【讨论】:

        • 编写一个简单的 shell 脚本和批处理文件来设置环境并调用 make 可能是个好主意...
        【解决方案9】:

        这可能不是不可能,但很可能太难了,无论如何编写两个 makefile 会更容易。

        尽管 GNU make(在 Linux 中使用)和 nmake 都有包含指令,所以一些常见的东西可以放在一个通用的 makefile 中,该文件包含在主 makefile 中。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-10-01
          • 2011-01-17
          • 2014-06-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多