Use the same makefile for make (Linux) and nmake (Windows)

前端 未结 8 1262
星月不相逢
星月不相逢 2020-12-13 19:26

I have a simple C program (one source file) which I want to compile on Linux and on Windows via make and nmake, respectively. Is there a possibility to accomplish this with

相关标签:
8条回答
  • 2020-12-13 20:13

    It's probably not impossible, but most likely so hard that it would be easier to write two makefiles anyway.

    Both GNU make (used in Linux) and nmake have include directives though, so some common things can be put in a common makefile that is included by the main makefile.

    0 讨论(0)
  • 2020-12-13 20:13

    I am not able to find a way to use a common makefile to work for both GNU Make and Microsoft NMAKE, mainly because they have an incompatible syntax for "include" and/or "if" directives. Microsoft NMAKE requires to use ! prefix for directives. For example, !if, !include, etc...

    If it is allowed to have separate macros, however, it could be tricked around. Here I presents the best way I found so far for making a makefile compatible for both GNU Make and Microsoft NMAKE by observing the followings:

    1. Microsoft NMAKE reads TOOLS.ini file for default macros.
    2. The Microsoft suite uses .obj as the object file extension.
    3. GNU Make reads files defined in a MAKEFILES environment variable.
    4. The GNU suite use .o as the object file extension.
    5. GNU make need not give an executable extension .exe for a target.

    Note: The following has been tested using Microsoft Visual Studio 2015 and MINGW32.

    Step 1: create a following DOS batch file and let it run whenever the CMD prompt is invoked.

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

    Step 2: Create a TOOLS.ini file under your working directory as below: (this file is independent of your project dependencies except the libraries possibly)

    [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:$@
    

    Step 3: Create a TOOLS.gcc under your working directory as below: (this file is independent of your project dependencies except the libraries possibly)

    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 $@
    

    Step 4: Edit your makefile as below (note $(X) and $(O)) where only dependencies are specified.

    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 *~
    

    Step 5: Enjoy GNU Make and Microsoft NMAKE with the same makefile

    $ nmake
    $ make clean
    $ nmake clean
    $ make
    
    0 讨论(0)
  • 2020-12-13 20:16

    My solution is to use two different filenames. (since the Makefile name searching priority in different OSes will not be the same)

    For Windows, I use normal "Makefile."

    For Linux, I use the special "GNUmakefile" according to this article.

    So that nmake (Win) will find "Makefile," and make (Linux) will find "GNUmakefile."

    0 讨论(0)
  • 2020-12-13 20:17

    Yes, you can do this with a single Makefile. The best source for this material is the O'Reilly book:

    Managing Projects with GNU Make, Third Edition By Robert Mecklenburg

    See chapter 7: Portable Makefiles.

    In summary, the technique is to test the environment variable ComSpec which says if the Windows command interpreter is present:

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

    I wrap this with a portable shell script which uses sed to edit the makefile for Nmake or GNU make...

    0 讨论(0)
  • 2020-12-13 20:20

    I've recently experimented with using the C preprocessor to generate a portable Makefile from a template Makefile.cc containing preprocessor symbols. So far it's worked surprisingly well. The first observation is that NMAKE will prescan a Tools.ini file, which I provide in the same directory as

    [NMAKE]
    MAKECONFIG=-D_NMAKE
    

    Then I have a 'true' Makefile next to it which is written in only the common sub language of GNU Make and 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)
    

    Note that the -E switch is pretty common for compilers (at least the big three I work with: GCC, Clang, and CL) for only preprocessing the file. With GNU Make the $(MAKECONFIG) expands to nothing, but in NMAKE it provides the preprocessor variable declaring itself. Since your template Makefile.cc can check it with #ifdef, as well as check for common variables with which the compiler declares itself, you can customize your Makefile.mk quite a bit for both the 'make' program, your operating system, and the compiler you're using.

    If you have any 'make' you probably already have a C compiler too; there's no need to install additional software like CMake or autotools. It uses mechanisms that are old and so likely to work in a lot of environments. And from what I've been able to tell so far, it's really fast. Faster at least than running a configuration step in autotools. The only disadvantage I've faced is that it limits the style of your Make rules to being on the same line, because the preprocessor changes the indentation of the code. Also the preprocessor spits out lines with # tags, but since these start a comment in a Makefile, they get ignored anyway.

    A have a somewhat small C++ project with a Makefile.cc that looks like the following snippet. It compiles on GNU Make or NMAKE with either GCC, Clang, or CL and on either Windows or in a POSIX environment. I've yet to support BSD Make or test any other compiler though.

    // 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
    
    0 讨论(0)
  • 2020-12-13 20:28

    I wanted to use the same makefile include to be used by Make and NMAKE. Since make recognises line continuation on comment lines, but NMAKE doesn't, this means that we can have separate instructions for Make and NMAKE. For example:

    # 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
    

    You just have to make sure that NMAKE-specific code is encompassed by # \.

    0 讨论(0)
提交回复
热议问题