I want to automate building thirdparty libraries and include them in my Qt project.
The .pro file of my Qt project looks like this:
QT -= gui
CONFIG += c++11 console
CONFIG -= app_bundle
QMAKE_EXTRA_TARGETS += stalib
PRE_TARGETDEPS += stalib
stalib.commands = make ../thirdparty/stalib/Makefile
LIBS += -L$${PWD}/../thirdparty/stalib/lib -lStalib
INCLUDEPATH += $${PWD}/../thirdparty/stalib/src
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += main.cpp
The Makefile of my thirdparty depedency submodule looks like this:
#-----------------------------------------------#
# Makefile for hello world #
#-----------------------------------------------#
# add preprocessor define
DEF_PARAMS = -DGREET=\"world\"
# Just to make sure
ifndef CROSS_COMPILE
$(error "cross-compilation environment not activated properly")
endif
# add debug symbols, DO NOT overwrite contents of FLAGS, append!
CFLAGS += -g $(DEF_PARAMS)
#Compilation only ignore warnings (ignore/-w, show all/-Wall).
CFLAGS += -c -w
SOURCEDIR=./src
LIBDIR=./lib
#-----------------------------------------------#
# Project Specific Settings #
#-----------------------------------------------#
# include directories relative to $SDKTARGETSYSROOT/usr/include (system HEADERS) or this Makefile (project headers).
INC_PARAMS = $(SOURCEDIR)
# project headers
HEADERS = $(SOURCEDIR)/math.h
# project sources
SOURCES = $(SOURCEDIR)/math.c
# Object files.
OBJECTS=$(SOURCES:%.c=%.c.o)
# Link libraries
# Libraries search directories relative to $SDKTARGETSYSROOT/usr/libs
# Library name without lib and .so e.g. libm.so -> -lm.
LINK_LIBS=
#Target name
TARGET_STATIC = $(LIBDIR)/libStalib.a
#-----------------------------------------------#
# Print Make Parameters #
#-----------------------------------------------#
print-%:
#echo "SOURCES=$(SOURCES)"
#echo "HEADERS=$(HEADERS)"
#echo "DEF_PARAMS=$(DEF_PARAMS)"
#echo "CFLAGS=$(CFLAGS)"
#echo "LDFLAGS=$(LDLAGS)"
#echo $* = $($*)
#-----------------------------------------------#
# Makefile Make Executable #
#-----------------------------------------------#
.SUFFIXES: .c
#Build rules begin.
all: $(TARGET_STATIC)
#Build rule for static library target.
$(TARGET_STATIC): $(OBJECTS)
$(AR) rc $# $(OBJECTS)
#Build rule for dynamic library target.
$(TARGET_SHARED): $(OBJECTS)
$(LD) $(LDFLAGS) $(OBJECTS) $(LINK_LIBS) -o $#
#Build rule for executeable target
$(TARGET): $(OBJECTS)
$(CC) $(LDFLAGS) $^ $(LINK_LIBS) -o $#
#Compilation rule for c files.
%.c.o: %.c $(HEADERS)
$(CC) $(CFLAGS) $(INC_PARAMS) $< -o $#
#Clean-up object files and target.
clean:
rm -f $(OBJECTS) $(TARGET) $(TARGET_STATIC) $(TARGET_SHARED)
However I get a linker error when building. It can not find the function defined in the math.h file:
#ifndef MATHH
#define MATHH
int addNums(int a, int b);
#endif
But the strange thing is that QtCreator is able to follow the reference to the header file.
For all of you who want to check the sources directly, or fiddle around with them:
https://github.com/faxe1008/myapp
https://github.com/faxe1008/stalib
Any help or suggestions on how to improve are appreciated.
If you want to build the library automatically, then you need to modify this line in your .pro:
stalib.commands = make -C../thirdparty/stalib CROSS_COMPILE=1
But that is not your question. You don't show your .cpp code, but I guess that you forgot to surround your #include like this:
extern "C" {
#include "math.h"
}
You can't include non system C headers in C++ sources without that. See: https://isocpp.org/wiki/faq/mixing-c-and-cpp
Related
I have a problem while attempting to generate a Makefile for my C project. It contains 3 files alea.c crypto.c and main.c. Here is my Makefile :
SHELL = /bin/bash
CC = gcc
RM = rm -rf
TAR = tar
MKDIR = mkdir
CHMOD = chmod
CP = cp
MV = mv
PROGNAME = Crypto_ivsk
EXEC = Crypt
PACKAGE = $(PROGNAME)
VERSION = 0.3
DISTDIR = $(PACKAGE)-$(VERSION)
HEADERS = alea.h crypto.h gmp.h
SOURCES = alea.c crypto.c main.c
LDFLAGS = -lgmp
CFLAGS = -Wall
OBJ = $(SOURCES:.c=.o)
DISTFILES = $(SOURCES) Makefile $(HEADERS)
all: $(EXEC)
$(EXEC): $(OBJ)
$(CC) $(OBJ) -o $(LDFLAGS) $(EXEC)
%.o:%.c $(HEADERS)
$(CC) -c $< $(CFLAGS)
dist: distdir
$(CHMOD) -R a+r $(DISTDIR)
$(TAR) zcvf $(DISTDIR).tar.gz $(DISTDIR)
$(RM) $(DISTDIR)
distdir: $(DISTFILES)
$(RM) $(DISTDIR)
$(MKDIR) $(DISTDIR)
$(CHMOD) 777 $(DISTDIR)
$(CP) -rf $(DISTFILES) $(DISTDIR)
clean:
$(RM) $(PROGNAME) $(OBJ) *~ $(DISTDIR).tar.gz
but when i type "make" in my shell the following error appears :
"no rule to make target gmp.h needed by alea.o STOP"
I can't do anything about this with my low level, can someone please help me ?
Thank you :)
You have said that all your %.o files depend of the file of same name with extension .c and from the header files they include.
You have put gmp.h as an include in this directory that make does not find (I imagine you refer to the GNU Multiprecision library include file) which is a file you should not put in your $(HEADER) variable. Why?
Very simple, because you are never going to change that file, so the files dependent of it have to be recompiled, as it is a system file.
There are some recommendations I'm givin to you to avoid problems in the future.
Don't define variables you are not going to use, or variables you don't plan to change. Variables work in make as constants. You define them in a single place, but you repeat them here and there on your Makefile. These are the candidates to use.
make has a bunch of already defined rules (indeed, the rule to build an object .o file from a .c file is already included in almost any of the make implementations actually in use. So you have better to respect the already installed rule, than provide you yours, because this rule is normally adapted to your system, and the Makefile contents is more portable.
The same applies to variables that represent system programs, like CC, RM, LEX, YACC, etc. Normally, one redefines it when the program has special compilation needs (which is not your case)
You have an error in the final link phase of your makefile as you specify options -o $(LDFLAGS) $(EXEC) to the linker, while it should be $(LDFLAGS) -o $(EXEC) (better if you put $(LDFLAGS) first, as the linking options will be applied once all object files have been processed)
With this premises, this is the suggested Makefile for your project.
PACKAGE = Crypto_ivsk
VERSION = 0.3
DISTNAME = $(PACKAGE)-$(VERSION)
# don_t include system headers, because 1) you are never to
# modify them and 2) because they are in a different
# directory and make will not find them, and then it will
# try to create them in the local dir, if you put any in a
# dependency rule.
headers = alea.h cripto.h main.h
# This variable is understood by the C source compilation rule.
CFLAGS = -Wall
# targets holds the list of programs we are to build.
targets = Crypt
# toclean is a variable we initialize to the programs we are
# to build and for each program we add all the objects we
# compile of it.
toclean = $(targets)
# put here programs that must be built before linking Crypt,
# e.g. program resource files, that are not used in the linking. In this case we have none.
Crypt_deps =
# Put here things that must be built before linking Crypt,
# e.g. object files, that ***are*** used to link it.
Crypt_objs = alea.o cripto.o main.o
# Place for the libraries to build Crypt.
Crypt_libs = -lgmp
# For each program we add to toclean the object files that
# compose it, so we can erase just doing $(RM) $(toclean)
# Pay special attention to the += operator.
toclean += $(Crypt_objs)
# If we have more programs to build, add (prefixed) groups
# of variables like the above.
DISTFILES = $(Cryp_objs:.o=.c) Makefile $(headers)
# all should be a .PHONY target.
.PHONY: all clean dist
all: $(targets)
#echo Build finished at: `date`
# repeat this for each file in $(targets)
# $# is the target of the rule
Crypt: $(Crypt_deps) $(Crypt_objs)
$(CC) $(LDFLAGS) $(Crypt_ldfl) -o $# $(Crypt_objs) $(Crypt_libs)
dist: $(DISTNAME).tar.gz
# we link the files in $(DISTFILES) so we don_t consume
# disk space with the copies.
$(DISTNAME).tar.gz: $(DISTFILES)
mkdir $(DISTNAME)
ln $(DISTFILES) $(DISTNAME)/.
tar cvfz $# $(DISTNAME)
$(RM) $(DISTNAME)
# this cleans everything built.
clean:
$(RM) $(toclean)
# this can be written as
# alea.o cripto.o main.o: $(headers)
# but in case they include different lists of header is
# better to put it this way.
alea.o: $(headers)
cripto.o: $(headers)
main.o: $(headers)
This Makefile schema will give you better results, and it is easily extensible to build several programs or libraries.
As you see, there's no compilation rule for the sources to object files. This is because there's a builtin rule in make. There's also a default definition for RM (in GNU make) and for CC.
It's important that you don't redefine CC to gcc, as your code will be more portable if you don't force everybody to build your program with gcc.
In any case, if you use optional assignments,
CFLAGS ?= -O -Wall -std=c98 -pedantic
instead, then you'll allow the builder to specify a different value for CFLAGS in the command line (or through the environment) and your script will be more flexible.
I am new to using Make and I am having some trouble figuring out the syntax. I went through a few examples and I essentially combined a few of them to create my own file. I am not sure how to tell make to ignore already compiled source or header files that haven't changed. How can I get make to compile only the files that have changed?
I looked on the GNU website: https://www.gnu.org/software/make/manual/html_node/Avoiding-Compilation.html
I tried some of those flags, but I still am not getting the results I wanted.
# specify compiler
CC=gcc
# set compiler flags
CFLAGS=-Igen/display -Igen/logic -Iman -Ilib/include -pipe -march=native
# set linker flags
LDFLAGS=-lglut32 -loglx -lopengl32 -Llib
# include all sources
SOURCES=gen/display/*.c gen/logic/*.c man/*.c
# create objects from the source files
OBJECTS=$(SOURCES:.cpp=.o)
# specify the name and the output directory of executable
EXECUTABLE=win32/demo
all: $(SOURCES) $(EXECUTABLE)
# compile the target file from sources
# $# = placeholder for target name
$(EXECUTABLE): $(OBJECTS)
$(CC) $(CFLAGS) $(OBJECTS) $(LDFLAGS) -o $#
.c.o:
$(CC) $(CFLAGS) $< -o $#
I have a number of header and source files in different directories that are being compiled, but no matter what I do everything recompiles.
Okay, let's do it, as it ought to be done ;-)
# set up our variables
# note: you may also prefer := or ?= assignments,
# however it's not that important here
CC=gcc
CFLAGS=-Igen/display -Igen/logic -Iman -Ilib/include -pipe -march=native
# linker's flags are different from compiler's
LDFLAGS=
# these are really libs, not flags
LDLIBS=-Llib -lglut32 -loglx -lopengl32
# 1) make is not your shell - it does not expand wildcards by default
# 2) use := to force immediate wildcard expansion;
# otherwise make could perform it several times,
# which is, at the least, very ineffective
SOURCES:=$(wildcard gen/display/*.c gen/logic/*.c man/*.c)
# use the right extensions here: .c -> .o
OBJECTS=$(SOURCES:.c=.o)
# note: it's okay to omit .exe extension if you're using "POSIX"-like make
# (e.g. cygwin/make or msys/make). However, if your make was built on Windows
# natively (such as mingw32-make), you'd better to add '.exe' here
EXECUTABLE=win32/demo
# don't forget to say to make that 'all' is not a real file
.PHONY: all
# *.c files are what you write, not make
# note: in fact, you don't need 'all' target at all;
# this is rather a common convention
all: $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $#
# note: 1) suffix rules are deprecated; use pattern rules instead
# 2) this doesn't add much to the built-in rule, so you can even omit it
# 3) .o files are created in the same directories where .c files reside;
# most of the time this is not the best solution, although it's not
# a mistake per se
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $#
i'm starting with make and i was searching how to automaticly generate dependencies for my c files, i found this piece of code :
# pull in dependency info for *existing* .o files
-include $(OBJS:.o=.d)
# compile and generate dependency info
%.o: %.c
gcc -c $(CFLAGS) $*.c -o $*.o
gcc -MM $(CFLAGS) $*.c > $*.d
what i don't understand is when i generate the dependencies file %.d, i already have built the %.o file, so what it the point to creating this dependencies file, and the -include i executed before everything so no dependencies file will exist.
-include means to include the dep file if it is there but not fail if it isn't.
The trick, and this is common in make dependency tracking, is that your dependencies are actually one build out of date. You're including, if they are there, the dependency files that were built the last time around.
This is not a problem because for dependencies to change, changes have to be made to something that the target depended on during the last build -- so even though make doesn't know the full new dependencies, it knows that it has to rebuild the target (and generate a new dependency file in the process).
Addendum: By the way, gcc and clang have a -MD option that can generate a dependency file while building the .o (by default with a .d suffix). This means that you can do automatic dependency tracking with implicit rules and cut down your Makefile to the bare minimum like so (for a simple project with .c files in a flat directory):
#!/usr/bin/make -f
# name of the binary to build
TARGET = target
CC = gcc
# These flags are used by the implicit rules for C preprocessor flags,
# C compiler flags, linker flags, and libraries to link (-lfoo options)
# -MD in CPPFLAGS means that the implicit rules for .o files will also
# generate a corresponding .d file that contains the dependencies.
# The values here are just examples (thank you, Rear Admiral Obvious!)
CPPFLAGS = -MD -I somewhere/include
CFLAGS = -O2 -g
LDFLAGS = -L somewhere/lib
LDLIBS = -lsomelibrary
# SRCS is a list of all .c files in the directory, the other two are
# pattern substitution expressions that take SRCS and replace the .c with .o
# and .d, respectively
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
DEPS = $(OBJS:.o=.d)
all: $(TARGET)
$(TARGET): $(OBJS)
# Look, Ma, no explicit rules for .o files!
clean:
rm -f $(TARGET) $(OBJS)
.PHONY: all clean
# include dep files (if available).
-include $(DEPS)
I usually add phony target depend, like this:
depend: $(SOURCES)
makedepend -Y. $(CFLAGS) $^ 2>/dev/null
and run make depend time to time to update depenencies.
See man makedepend for details.
I've been attempting to write test a simple program using the opencv library. I have the following test code I found on a tutorial. I am running OSX 10.9.2 and managed (I think) to successfully install opencv on my computer using homebrew. My problem is that I cannot get this code to compile because my make file throws errors anytime I try to compile. I believe the issue is that I have not properly linked the libraries, and no amount of googling seems to have helped me solve my problem.
////////////////////////////////////////////////////////////////////////
//
// This is a simple, introductory OpenCV program. The program reads an
// image from a file, inverts it, and displays the result.
//
////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <cv.h>
#include <highgui.h>
int main(int argc, char** argv)
{
IplImage* img = cvLoadImage( "Conumdrum.jpeg", 0 ); //change the name (image.jpg) according to your Image filename.
cvNamedWindow( "Example1", CV_WINDOW_AUTOSIZE );
cvShowImage("Example1", img);
cvWaitKey(0);
cvReleaseImage( &img );
cvDestroyWindow( "Example1" );
return 0;
}
I found a sample makefile online that I've attempted to modify but to no avail, here is my makefile below:
# define the C compiler to use
CC = gcc
# define any compile-time flags
CFLAGS = -Wall -g
# define any directories containing header files other than /usr/include
#
INCLUDES = -I/Users/MY_NAME/Projects/Tank\ Game/Webcam_Tests/Webcam_Test_v1/include
# define library paths in addition to /usr/lib
# if I wanted to include libraries not in /usr/lib I'd specify
# their path using -Lpath, something like:
LFLAGS = -L/Users/MY_NAME/Projects/Tank\ Game/Webcam_Tests/Webcam_Test_v1/lib
# define any libraries to link into executable:
# if I want to link in libraries (libx.so or libx.a) I use the -llibname
# option, something like (this will link in libmylib.so and libm.so:
LIBS = -l libopencv_core.dylib -lm
# define the C source files
SRCS = Webcam_Test_v1.c
# define the C object files
#
# This uses Suffix Replacement within a macro:
# $(name:string1=string2)
# For each word in 'name' replace 'string1' with 'string2'
# Below we are replacing the suffix .c of all words in the macro SRCS
# with the .o suffix
#
OBJS = $(SRCS:.c=.o)
# define the executable file
MAIN = mycc
#
# The following part of the makefile is generic; it can be used to
# build any executable just by changing the definitions above and by
# deleting dependencies appended to the file from 'make depend'
#
.PHONY: depend clean
all: $(MAIN)
#echo Simple compiler named mycc has been compiled
$(MAIN): $(OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $(MAIN) $(OBJS) $(LFLAGS) $(LIBS)
# this is a suffix replacement rule for building .o's from .c's
# it uses automatic variables $<: the name of the prerequisite of
# the rule(a .c file) and $#: the name of the target of the rule (a .o file)
# (see the gnu make manual section about automatic variables)
.c.o:
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $#
clean:
$(RM) *.o *~ $(MAIN)
depend: $(SRCS)
makedepend $(INCLUDES) $^
# DO NOT DELETE THIS LINE -- make depend needs it
Terminal gives me the following output when I try to compile:
gcc -Wall -g -I/Users/MY_NAME/Projects/Tank\ Game/Webcam_Tests/Webcam_Test_v1/include -c Webcam_Test_v1.c -o Webcam_Test_v1.o
gcc -Wall -g -I/Users/MY_NAME/Projects/Tank\ Game/Webcam_Tests/Webcam_Test_v1/include -o mycc Webcam_Test_v1.o -L/Users/MY_NAME/Projects/Tank\ Game/Webcam_Tests/Webcam_Test_v1/lib -l libopencv_core.dylib -lm
ld: library not found for -llibopencv_core.dylib
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [mycc] Error
Any help would be much appreciated, I've been beating my head against a wall for a couple of days now trying to solve this issue. It should also be noted that I am a novice, especially when it comes to makefiles. Thanks.
Your linker option is wrong. Try with -lopencv_core instead of -l libopencv_core.dylib.
For example, if you want to link a library which filename is libfoo.dylib, the right linker option is -lfoo.
And you will need to add -lopencv_highgui too.
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 a single makefile?
I thought about something like
ifeq($(MAKE), nmake)
// nmake code here
else
// make code here
endif
Unfortunately nmake seems not to understand ifeq, so I cannot use that. I have a working makefile, but that produces very ugly results:
hello: hello.c
$(CC) hello.c
That works on both systems. The problem is that the outcome depends on the default behaviors of the respective compilers. Under Linux I get an executeable named 'a.out' rather than 'hello'. Under Windows I get 'hello.exe' but there is also 'hello.obj' which I do not want to have.
Is there an alternative way? Or is what I'm trying absolutely impossible?
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.
You should look at using CMake for this. With one source file, it should be quite easy. Here is how you could set up a simple project:
cmake_minimum_required(VERSION 3.10)
# set the project name
project(Hello)
# add the executable
add_executable(Hello hello.c)
To build the simple project, you would do the following (this assumes your source and CMakeLists.txt files are in the same directory as the source file hello.c:
mkdir build
cd build
cmake ..
cmake --build .
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 # \.
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:
Microsoft NMAKE reads TOOLS.ini file for default macros.
The Microsoft suite uses .obj as the object file extension.
GNU Make reads files defined in a MAKEFILES environment variable.
The GNU suite use .o as the object file extension.
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
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."
Solution: 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
I just thought of something completely different.
If you stick to your extremely simple Makefile, which, you say, works, and just put the 'standard' variables CC and CFLAGS in your respective environments, say
export CC=gcc
respectively
set CC=CL.EXE
and
export CFLAGS=-o myexecutable
respectively
set CFLAGS=/out:myexecutable.exe
it might just work.
Be aware, I'm not firm in the exact options to use, you'll have to figure them out yourself. But AFAIK both make variants recognize the same set of flags. You may even set those on the respective command lines (but not in the makefile, since NMAKE uses a different 'ifeq' syntax...)
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...
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
Wouldn't it be possible to use a script in gnu sed, or perl to translate the host's Makefile to a Microsoft compatible NMakefile? The Makefiles, after all, are text files to provide input for whichever helper tool you're using. Sed and Perl both exist for Linux and Windows.