Search code examples
macrosnmaketargets

nmake modify macro based on target


I've got a Makefile.mak where I optionally create a test.exe or a DLL from my C-based source code. I'm using CL.EXE and NMAKE.

I'd like to modify my CFLAGS macro like this when the target is TEST.EXE:

CFLAGS = $(CFLAGS) -DMAIN

And, of course, I use this in my C code:

#ifdef MAIN
... int main()... yada yada
#endif

I had tried

!IF $@ == "test.exe"

but it crashed out and doesn't work logically since the $@, target, isn't deterministic in that part of the makefile.

The logical place to define the additional macro is when defining the target but I don't see how to do that without NMAKE interpreting it as DOS command.

test.exe: test.obj
  CFLAGS = $(CFLAGS) -DMAIN
  $(LINKER) /out:$@ $(LIB) $*.obj $(LIBS)

It'd be easier with gmake, I know. I don't have that option.


Solution

  • I will present two solutions: one which does what you request, namely modifying CFLAGS based on the target, and a second one which may be a better approach.

    Suppose you have a file multiply.c:

    #include <stdio.h>
    
    int multiply(int a, int b) {
       return a * b;
    }
    
    #ifdef MAIN
    int main() {
       printf("Unit test: multiply(2, 3) = %d\n", multiply(2, 3));
    }
    #endif
    

    which you would like to add to a static library my_lib.lib, and also use as a stand-alone unit test.

    One standard way of adding -DMAIN to CFLAGS is to use NMAKE recursively. The second invocation of NMAKE could use a different makefile or, as as presented here, use the same makefile with a flag to prevent an infinite recursive loop.

    TARGETS = my_lib.lib multiply.exe
    CFLAGS  = -W4 -O2 -nologo -Zi
    
    all: $(TARGETS)
    my_lib.lib: multiply.obj
    
    my_lib.lib:
        lib -nologo -out:$@ $**
    
    !ifndef RECURSE
    multiply.exe:
        nmake -nologo CFLAGS="-DMAIN $(CFLAGS)" RECURSE= $@
    !endif
    
    multiply.obj: .FORCE
    
    .FORCE:
    

    If RECURSE is defined, the built-in rule is used to create the test program multiply.exe.

    This solution works. But it requires that multiply.obj be remade every time it is used, because there are two versions of it floating around: one with a main and one without.

    The second solution distinguishes between these object files.

    TARGETS = my_lib.lib multiply.exe
    CFLAGS  = -W4 -O2 -nologo -Zi
    
    all: $(TARGETS)
    my_lib.lib: multiply.obj
    multiply.exe: $*.TEST_obj
    
    my_lib.lib:
        lib -nologo -out:$@ $**
    
    multiply.exe:
        link -nologo -out:$@ $**
    
    .c.TEST_obj:
        $(CC) -DMAIN $(CFLAGS) -c $< -Fo$@
    

    This gives:

    >nmake -nologo
            cl -W4 -O2 -nologo -Zi /c multiply.c
    multiply.c
            lib -nologo -out:my_lib.lib multiply.obj
            cl -DMAIN -W4 -O2 -nologo -Zi -c multiply.c -Fomultiply.TEST_obj
    multiply.c
            link -nologo -out:multiply.exe multiply.TEST_obj
    

    Trying to create a .exe file directly from the .c file, as in:

    .c.exe:
        $(CC) -DMAIN $(CFLAGS) $<
    

    does not work, because this still creates a .obj file which clobbers the other version.

    Edit: .SUFFIXES: .TEST_obj does not seem to be needed.