nmake modify macro based on target - macros

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.

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.

Related

Portable check for library

A project I am trying to compile has this command:
cc -xc++ -o/dev/null -lc++ -shared
However I am using PowerShell, which has no notion of /dev/null:
PS C:\> cc -xc++ -o/dev/null -lc++ -shared
C:/msys2/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.2.0/../../../../
x86_64-w64-mingw32/bin/ld.exe: cannot open output file /dev/null.exe: No such
file or directory
I tried using -o$null, but it just creates a file $null.exe. I also tried this:
PS C:\> cc -xc++ -o $null -lc++ -shared
cc.exe: fatal error: no input files
Is PowerShell able to handle this use case? Alternatively, it seems the purpose of the test is to just check if libc++ exists. Is another way available to do that?
It appears the issue is specific to GCC. If I get Clang, the same command
works with nul:
cc -xc++ -onul -lc++ -shared
but if I try the same thing with GCC, I get this:
C:/msys2/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.2.0/../../../../
x86_64-w64-mingw32/bin/ld.exe: nul.exe: final close failed: file truncated
I have posted bug 97574.

cmake seems to not pick up my -DCMAKE_CXX_FLAGS

I have a totally simple setup. Two files in two separate directories.
mkdir a
touch a/a.h
mkdir b
echo '#include <a/a.h>' > b/b.c
Compiling works, when I specify a header path
cd b
gcc -c -I.. b.c
cd ..
OK now let's add cmake to the picture. For my purposes I need to specify the header search path via the command-line. Consider the CMakeLists.txt read only.
cat<<EOF > b/CMakeLists.txt
cmake_minimum_required (VERSION 3.0)
project (b)
add_library(b
b.c
)
EOF
mkdir b/build
cd b/build
cmake -DCMAKE_CXX_FLAGS=-I.. ..
make VERBOSE=1
But make fails and I don't see the -I.. specification in the cc command line.
[ 50%] Building C object CMakeFiles/b.dir/b.c.o
/Applications/Xcode.app/Contents/Developer/Toolchains /XcodeDefault.xctoolchain/usr/bin/cc -o CMakeFiles/b.dir/b.c.o -c /tmp/b/b.c
/tmp/b/b.c:1:10: fatal error: 'a/a.h' file not found
I tried giving an absolute path too, but it just doesn't work for me.
Your file has .c extension, you should use CMAKE_C_FLAGS for it.
And in most cases you should specify needed include search paths in CMakeLists.txt itself:
include_directories(..)

How to use Devel::Cover with prove?

I see there are some similar questions here and on http://www.perlmonks.org but I still do not get it.
Imagine I have a project with a 'lib/' and a 't' directories. I run my tests with 'prove':
$ cd $PROJECT_ROOT
$ prove ./*.t
I want to get a report in html for one or more files in the 'lib/' directory. I do not want reports for the files in the 't' directory.
A simple example should be enough. Thanks
perl Makefile.PL or perl Build.PL
cover -test
The proper way is to always start out with Makefile.PL/Build.PL, just as selected answer suggests. However, sometimes you are not the one who started out, so...
I used to make a fake makefile:
% cat Makefile
test:
prove -Ilib -r t
The following also seems to work (w/o touching ANY files on disk):
cover -t -make 'prove -Ilib -r t; exit $?'
This only works because of how perl's system/exec handle an argument with shell metacharacters in it (; in this case) and may break in the future if cover decides to quote it more rigirously. Also it shouldn't work under windows. I wish cover had a -prove option instead.
This one still generates coverage for *.t as well as CPAN modules at nonstandard locations. This behaviour can be fixed using +select/+ignore options (see the Devel::Cover's manpage):
cover -t +select ^lib +ignore ^
So the tl;dr "magic" command is
cover -t +select ^lib +ignore ^ -make 'prove -Ilib -r t; exit $?'
EDIT The following didn't work for me - it only prints short summary:
PERL5OPT="$PERL5OPT -MDevel::Cover" prove -Ilib -r t
cover -t +select ^lib +ignore ^
Note that prove -MSomething applies Something to prove itself and doesn't pass it on (unlike with -I).
Make prove run every test file with Devel::Cover activated:
$ prove --exec 'perl -MDevel::Cover=-silent,1 -Ilib' t/*.t
By default this will print the statistics after each test file. That’s why I added -silent => 1.
To print the complete statistics at the end add:
$ cover -summary

How to detect OS in Gnu Makefile? [duplicate]

I would like to have the same Makefile for building on Linux and on Windows. I use the default GNU make on Linux and the mingw32-make (also GNU make) on Windows.
I want the Makefile to detect whether it operates on Windows or Linux.
For example make clean command on Windows looks like:
clean:
del $(DESTDIR_TARGET)
But on Linux:
clean:
rm $(DESTDIR_TARGET)
Also I would like to use different directory separator on Windows (\) and Linux (/).
It is possible to detect Windows operating system in Makefile?
PS: I do not want to emulate Linux on Windows (cygwin etc.)
There is similiar question: OS detecting makefile, but I didn't find the answer here.
I solved this by looking for an env variable that will only be set on windows.
ifdef OS
RM = del /Q
FixPath = $(subst /,\,$1)
else
ifeq ($(shell uname), Linux)
RM = rm -f
FixPath = $1
endif
endif
clean:
$(RM) $(call FixPath,objs/*)
Because %OS% is the type of windows, it should be set on all Windows computers but not on Linux.
The blocks then setups up variables for the different programs as well as a function for converting the forward slashes into backslashes.
You to have to use $(call FixPath,path) when you call an outside command (internal commands work fine). You could also use something like:
/ := /
and then
objs$(/)*
if you like that format better.
The SystemRoot trick didn't work for me on Windows XP but this did:
ifeq ($(OS),Windows_NT)
#Windows stuff
...
else
#Linux stuff
....
endif
You should probably use the $(RM) variable to remove some files.
Checking WINDIR or COMSPEC is case-sensitive. Instead, I came up
with the following solution, hope that helps someone someday:
# detect if running under unix by finding 'rm' in $PATH :
ifeq ($(wildcard $(addsuffix /rm,$(subst :, ,$(PATH)))),)
WINMODE=1
else
WINMODE=0
endif
ifeq ($(WINMODE),1)
# native windows setup :
UNLINK = del $(subst /,\,$(1))
CAT = type $(subst /,\,$(1))
else
# cross-compile setup :
UNLINK = $(RM) $(1)
CAT = cat $(1)
endif
I would like to have the same Makefile for building on Linux and on Windows.
Maybe you will like CMake

Emacs: set compilation command per-buffer

so, suppose I have a file file.c, and a file anothr.c. I would like to set the compilation command for each of those files separately, say: gcc -Wall -O3 -o f file.c, and gcc -Wall -std=c99 -o a another.c. How can I set the gcc command for that buffer so that every time I open it, it knows how to compile it? Is there something with the // -*- directive or something like that? Thanks.
Yes, you can use the directive in the file, and also set other value. Try this in line one or two:
// -*- compile-command: "gcc -Wall -O3 -o f file.c" -*-
and then re-load the file with C-x v so that the setting takes effect.
I sometimes set things like c-indent-level: 4; c-basic-offset: 4; in there too.
What you're looking for are called file local variables, or sometimes just "local variables", per the in-comment declarative format described here.
In addition to the syntax given in Dirk's answer, you can write a "local variables" block at the end of your file:
/* Local Variables: */
/* compile-command:"gcc -Wall -O3 -o f file.c" */
/* End: */
You can use the interactive function add-file-local-variable to help maintain this list if you don't want to type each entry manually.