对make(Linux)和nmake使用相同的makefile(Windows)

时间:2011-11-25 14:19:11

标签: c makefile nmake

我有一个简单的C-Programm(1个源文件),我想在Linux上和Windows上通过make resp编译。 NMAKE。是否有可能使用单个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',我不想拥有它。

有没有人知道另一种方式?或者我正在尝试的绝对不可能?

9 个答案:

答案 0 :(得分:9)

这可能并非不可能,但最有可能的是,无论如何写两个makefile会更容易。

GNU make(在Linux中使用)和nmake都有include伪指令,因此一些常见的东西可以放在主makefile包含的公共makefile中。

答案 1 :(得分:7)

您应该考虑使用CMake。有一个源文件应该很容易!

编辑:

以下是设置简单项目的方法:

cmake_minimum_required(VERSION 2.8)

project(Simple)

include_directories("${PROJECT_BINARY_DIR}")

add_executable(Simple simple.cpp)

要构建简单项目,您可以执行以下操作(假设您的源和CMakeLists.txt文件位于~/src/simple中:

foo@bar~/src/simple$ mkdir build
foo@bar~/src/simple$ cd build
foo@bar~/src/simple$ cmake ..
foo@bar~/src/simple$ make

答案 2 :(得分:7)

我想使用make和nmake使用的相同makefile include。由于make在注释行上识别行继续但nmake没有,这意味着我们可以为make和nmake提供单独的指令。例如:

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

只需确保#e

终止nmake特定代码

答案 3 :(得分:4)

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

但是,如果允许使用单独的宏,则可能会被欺骗。在这里,我提出了迄今为止我发现的最佳方法,通过观察以下内容使makefile兼容GNU make和MS nmake:

  1. MS nmake读取TOOLS.ini文件以获取默认宏。
  2. MS套件使用.obj作为目标文件扩展名。
  3. GNU make读取MAKEFILES环境变量中定义的文件。
  4. GNU套件使用.o作为目标文件扩展名。
  5. GNU make无需为目标提供可执行的扩展名.exe。
  6. 注意:以下内容已使用MS 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)),其中只指定了dependecies。

    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和MS nmake

    $ nmake
    $ make clean
    $ nmake clean
    $ make
    

答案 4 :(得分:2)

解决方案: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

答案 5 :(得分:1)

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

如果您坚持使用非常简单的Makefile,您可以使用它,只需将“标准”变量CC和CFLAGS放在各自的环境中,比如说

  export CC=gcc
分别

  set CC=CL.EXE

  export CFLAGS=-o myexecutable
分别

  set CFLAGS=/out:myexecutable.exe

它可能会起作用。

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

答案 6 :(得分:1)

是的,您可以使用单个Makefile执行此操作。这种材料的最佳来源是O' Reilly Book:

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 ..

答案 7 :(得分:0)

我最近尝试使用C预处理程序从包含预处理程序符号的模板Makefile.cc生成可移植的Makefile。到目前为止,它的工作出奇地出色。第一个发现是NMAKE将预扫描Tools.ini文件,该文件与我在同一目录中提供

[NMAKE]
MAKECONFIG=-D_NMAKE

然后我旁边有一个“ true” 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程序,操作系统和操作系统自定义Makefile.mk。您正在使用的编译器。

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

A有一个稍微小的C ++项目,带有一个Makefile.cc,看起来像下面的代码片段。它可以在具有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

答案 8 :(得分:0)

我的解决方案是使用两个不同的文件名。 (因为在不同的操作系统中,Makefile名称搜索的优先级将不同)

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

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

nmake(Win)将找到“ Makefile”,而make(Linux)将找到“ GNUmakefile”。