I've just started trying to use static pattern rules and for loops together within makefiles, I'm still relatively new to using makefiles so please forgive me if I've missed something obvious.
In the code below I have tried to use a for loop to create 6 executables, two for each unique file.
Here is the makefile:
vpath %.h ../headers/
CXX := g++
CXXFLAGS := -std=c++11 -I../headers/
LDFLAGS :=
SUFFIX := fileA fileB fileC
memory-%.exe: primary-%.o memory.cpp
$(CXX) $(CXXFLAGS) $^ -o $#
timing-%.exe: primary-%.o timing.cpp
$(CXX) $(CXXFLAGS) $^ -o $#
all: for i in $(SUFFIX); \
do \
testing-$$i.exe: primary-$$i.o; \
memory-$$i.exe: primary-$$i.o; \
done
I am met with the error:
\bin\sh: 3: memory-fileA.exe:: not found
\bin\sh: 4: timing-fileA.exe:: not found
\bin\sh: 3: memory-fileB.exe:: not found
\bin\sh: 4: timing-fileB.exe:: not found
\bin\sh: 3: memory-fileC.exe:: not found
\bin\sh: 4: timing-fileC.exe:: not found
make: *** [all] Error 127
Is this even possible in the first place? I was just wondering if it were possible to be efficient using this method.
Any help is appreciated as I'd like to know more about the possibilities that makefiles allow.
Thank you.
You are mixing shell and make constructs. As tripleee pointed the recipes of make rules are shell scripts, not other make rules.
Moreover, there are a few issues with your Makefile:
You explain that you want to use static pattern rules but what you wrote is "simple" pattern rules.
You do not need to quote your suffixes. And you should not, make is not the shell, it preserves them. You will get errors because of this.
Your use of the standard CXXFLAGS make variable is extremely unusual. Traditionally it is limited to the compiler's flags, not the compiler itself for which CXX is used.
You are compiling source files and linking simultaneously. This too is not that usual. It causes useless re-compilations.
The c++11 option of g++ is new to me. Are you sure it is not -std=c++11?
The vpath directive is useless because you do not express dependencies on the header files. But let's keep it, I guess you do not show everything.
All-in-all, you can probably achieve want you want with:
vpath %.h ../headers/
CXX := g++
CXXFLAGS := -std=c++11 -I../headers/
LDFLAGS :=
SUFFIX := fileA fileB fileC
TESTING := $(patsubst %,testing-%.exe,$(SUFFIX))
MEMORY := $(patsubst %,memory-%.exe,$(SUFFIX))
.PHONY: all
all: $(TESTING) $(MEMORY)
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $^ -o $#
$(TESTING): testing-%.exe: primary-%.o memory.o
$(CXX) $(LDFLAGS) $^ -o $#
$(MEMORY): memory-%.exe: primary-%.o timing.o
$(CXX) $(LDFLAGS) $^ -o $#
The %.o: %.cpp... rule is a pattern rule. It tells make how to produce any object file from the corresponding C++ source file. The two last rules are really static pattern rules. The first of the two, for instance, declares that each target testing-<suffix>.exe listed in $(TESTING) depends on the corresponding primary-<suffix>.o and on memory.o. This single static pattern rule is thus equivalent to these 3 simple rules:
testing-fileA.exe: primary-fileA.o memory.o
g++ primary-fileA.o memory.o -o testing-fileA.exe
testing-fileB.exe: primary-fileB.o memory.o
g++ primary-fileB.o memory.o -o testing-fileB.exe
testing-fileC.exe: primary-fileC.o memory.o
g++ primary-fileC.o memory.o -o testing-fileC.exe
No need for loops. Note that, if you correctly use the standard make variables CXX and CXXFLAGS, you can drop the pattern rule (%.o: %.cpp...), it is one of the many implicit rules that make knows already.
I am trying to compile a code -
this code uses a few libraries and for starters I am trying to create a makefile to get one library
I am having difficulties.
this is the makefile
HOME = $(shell pwd)
LIBNA = libbv.a
LIBZP = $(HOME)/$(LIBNA)
# FFLAGC = -Mextend -Msave -g -C -Mchkfpstk -Mchkptr -fpic -Ktrap=fp
FC = gfortran
ifeq ($(OSTYPE),linux)
FC = pgf95 -Msave -fpic
endif
# per il gfortran
FFLAGC = -g -Wall-ffixed-line-length-0 -Mextend -Msave -g -C -Mchkfpstk -Mchkptr -fpic -Ktrap=fp
# FC = gfortran
#
SOURCE = \
filename1.f\
filename2.f\
...
filenamen.f
.SUFFIXES: .f
OBJ = $(SRCS:.f=.o)
.f.o:
$(FC) $(FFLAG) -c $< $#
$(LIBZP): $(LIBZP)($(OBJ))
ar -r $(LIBZP) $?
rm -f $?
this is the makefile I am using.
I get the error
make: *** No rule to make target absolutepath/libbv.a()', needed by
absolute_path/libbv.a'. Stop.
I was wondering if any of you can help
Well, your error message shows this:
absolutepath/libbv.a()
with nothing inside the parentheses. But your makefile has this:
$(LIBZP): $(LIBZP)($(OBJ))
with $(OBJ) in the parentheses. So clearly, $(OBJ) is expanding to the empty string. Why is that?
Well, OBJ is set here:
OBJ = $(SRCS:.f=.o)
based on SRCS. Well, what does that variable contain?
Aha. Nothing, because it's never set. You set this though:
SOURCE = \
...
SOURCE != SRCS, so you're modifying an empty variable and OBJ is the empty string.
I'm not sure why you're prefixing the target with the current directory... that's where it will go by default if you don't specify any directory. In any event, you can use $(CURDIR) rather than running $(shell pwd).
If you're going to use GNU make anyway, I recommend you use pattern rules rather than suffix rules: they're much simpler to read/understand:
%.o : %.f
$(FC) $(FFLAG) -c $< $#
Also don't you need a -o here before $#? I don't use Fortran compilers but I would imagine they work more or less the same as C/C++ compilers.
I'm building a makefile that will be used to build a release or debug target library. I want to place the object and auto-generated dependancy files into either a debug or release directory structure, depending on the requested makefile goal. I don't want to specify a testable make command-line argument (i.e. DBG=1), but would prefer to run make -f Makefile, or make -f Makefiel dbg for release and debug target goals, respectively. Got that part down. I understand that I can't assign a target-specific variable containing the name of the object dir (either release or debug) that can be used as part of the Target specification in a rule, like I did in the example shown below. In this example, OBJDIR is the target-specific variable I would like to set depending on the build goal. For that reason, in this example, $(OBJDIR) is empty in the target rule $(OBJDIR)/%.o. Any recommendations on how to perform the suggested steps nicely? (The example shown is simply a copy/paste unverified example...syntax is not verified...in fact, I can't get the tabs to appear correctly...I'm hoping to get some implementation ideas). (Also, $(OBJDIR) is not set in the clean target as shown...since it is not in the dbg/all target dependancy heirarchy...thoughts?) Thanks in advance.
Makefile:
OBJS := a.o b.o c.o
SRCS := $(OBJS:.o=.c)
-- Set up the release and the debug directory paths and object filenames
RELEASE_DIR := ./release
RELEASE_OBJ := $(OBJS:%=$(RELEASE_DIR)/%)
DEBUG_DIR := ./debug
DEBUG_OBJ := $(OBJS:%=$(DEBUG_DIR)/%)
.PHONY : all dbg
all: CFLAGS = -O3
all: OBJDIR := RELEASE_DIR
all: df := $(RELEASE_DIR)/$(*F)
all: init_release lib1.so
dbg: CFLAGS = -g -O0
dbg: OBJDIR := DEBUG_DIR
dbg: df := $(DEBUG_DIR)/$(*F)
dbg: init_debug lib1.so
Lib1.so: $(OBJ)
init_release:
-#mkdir -p $(RELEASE_DIR)
init_debug:
-#mkdir -p $(DEBUG_DIR)
lib1.so: $(OBJ)
#echo '--------------------------------------------------------------'
#echo linking $#
#gcc -shared -o lib1.so $(OBJ)
-Compile including advance dependancy generation alg, per Tom Tromey:
# http://make.paulandlesley.org/autodep.html
$(OBJDIR)/%.o: %.c
echo $#
echo $(OBJDIR)
echo compiling $#
$(COMPILE.c) -MD -o $# $<
cp $(df).d $(df).P; \
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < $(df).d >> $(df).P; \
rm -f $(df)/$*.d
# If the goal is "clean", don't include these to avoid trying to build them
ifneq($(MAKECMDGOALS),clean)
-include $(SRCS:%.c=$(OBJDIR)/%.P)
endif
clean:
-#rm -f $(OBJDIR)/*.[Pdo] lib1.so
Target specific variables can be tricky. Use indirection instead. Make has lots of syntax to cut-down on boilerplate text. .SECONDEXPANSION is often good. A sketch:
.SECONDEXPANSION:
${DEBUG_OBJ} ${RELEASE_OBJ}: $$(patsubst %.o,%.c,$${#F})
gcc ${copts-${#D}} -c $< -o $#
Here we tell make that ./release/a.o depends on a.c. When make decides to build ./release/a.o it expands the shell line. As it does so, ${#D} is naturally release, so make carries on and expands ${copts-release} which you will have defined usefully.
Similarly, when producing ./debug/a.o make expands ${copts-debug}.
Copious use of $(warning [blah]), $(error [blah blah]) and the mandatory --warn-undefined-variables will help you get this right.
The Makefile you wrote is not valid, and it will not generate your targets as you expect. For instance, you cannot set the CFLAGS variable in the targets definitions all and dbg.
The only solution I can think of is to call make with the same Makefile defining the DBG variable as you wish. E.g.:
ifdef DBG
CFLAGS = -O0 -ggdb
OBJDIR = dbgdir
else
CFLAGS = -O2
OBJDIR = reldir
endif
all: $(OBJDIR)/target
#Your commands here
dbg:
$(MAKE) DBG=1
With this, if you call make, you have the release build. If you call make dbg you have the release build.
I routinely work on several different computers and several different operating systems, which are Mac OS X, Linux, or Solaris. For the project I'm working on, I pull my code from a remote git repository.
I like to be able to work on my projects regardless of which terminal I'm at. So far, I've found ways to get around the OS changes by changing the makefile every time I switch computers. However, this is tedious and causes a bunch of headaches.
How can I modify my makefile so that it detects which OS I'm using and modifies syntax accordingly?
Here is the makefile:
cc = gcc -g
CC = g++ -g
yacc=$(YACC)
lex=$(FLEX)
all: assembler
assembler: y.tab.o lex.yy.o
$(CC) -o assembler y.tab.o lex.yy.o -ll -l y
assembler.o: assembler.c
$(cc) -o assembler.o assembler.c
y.tab.o: assem.y
$(yacc) -d assem.y
$(CC) -c y.tab.c
lex.yy.o: assem.l
$(lex) assem.l
$(cc) -c lex.yy.c
clean:
rm -f lex.yy.c y.tab.c y.tab.h assembler *.o *.tmp *.debug *.acts
There are many good answers here already, but I wanted to share a more complete example that both:
doesn't assume uname exists on Windows
also detects the processor
The CCFLAGS defined here aren't necessarily recommended or ideal; they're just what the project to which I was adding OS/CPU auto-detection happened to be using.
ifeq ($(OS),Windows_NT)
CCFLAGS += -D WIN32
ifeq ($(PROCESSOR_ARCHITEW6432),AMD64)
CCFLAGS += -D AMD64
else
ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)
CCFLAGS += -D AMD64
endif
ifeq ($(PROCESSOR_ARCHITECTURE),x86)
CCFLAGS += -D IA32
endif
endif
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
CCFLAGS += -D LINUX
endif
ifeq ($(UNAME_S),Darwin)
CCFLAGS += -D OSX
endif
UNAME_P := $(shell uname -p)
ifeq ($(UNAME_P),x86_64)
CCFLAGS += -D AMD64
endif
ifneq ($(filter %86,$(UNAME_P)),)
CCFLAGS += -D IA32
endif
ifneq ($(filter arm%,$(UNAME_P)),)
CCFLAGS += -D ARM
endif
endif
The uname command (http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/uname.1.html) with no parameters should tell you the operating system name. I'd use that, then make conditionals based on the return value.
Example
UNAME := $(shell uname)
ifeq ($(UNAME), Linux)
# do something Linux-y
endif
ifeq ($(UNAME), Solaris)
# do something Solaris-y
endif
Detect the operating system using two simple tricks:
First the environment variable OS
Then the uname command
ifeq ($(OS),Windows_NT) # is Windows_NT on XP, 2000, 7, Vista, 10...
detected_OS := Windows
else
detected_OS := $(shell uname) # same as "uname -s"
endif
Or a more safe way, if not on Windows and uname unavailable:
ifeq ($(OS),Windows_NT)
detected_OS := Windows
else
detected_OS := $(shell sh -c 'uname 2>/dev/null || echo Unknown')
endif
Ken Jackson proposes an interesting alternative if you want to distinguish Cygwin/MinGW/MSYS/Windows. See his answer that looks like that:
ifeq '$(findstring ;,$(PATH))' ';'
detected_OS := Windows
else
detected_OS := $(shell uname 2>/dev/null || echo Unknown)
detected_OS := $(patsubst CYGWIN%,Cygwin,$(detected_OS))
detected_OS := $(patsubst MSYS%,MSYS,$(detected_OS))
detected_OS := $(patsubst MINGW%,MSYS,$(detected_OS))
endif
Then you can select the relevant stuff depending on detected_OS:
ifeq ($(detected_OS),Windows)
CFLAGS += -D WIN32
endif
ifeq ($(detected_OS),Darwin) # Mac OS X
CFLAGS += -D OSX
endif
ifeq ($(detected_OS),Linux)
CFLAGS += -D LINUX
endif
ifeq ($(detected_OS),GNU) # Debian GNU Hurd
CFLAGS += -D GNU_HURD
endif
ifeq ($(detected_OS),GNU/kFreeBSD) # Debian kFreeBSD
CFLAGS += -D GNU_kFreeBSD
endif
ifeq ($(detected_OS),FreeBSD)
CFLAGS += -D FreeBSD
endif
ifeq ($(detected_OS),NetBSD)
CFLAGS += -D NetBSD
endif
ifeq ($(detected_OS),DragonFly)
CFLAGS += -D DragonFly
endif
ifeq ($(detected_OS),Haiku)
CFLAGS += -D Haiku
endif
Notes:
Command uname is same as uname -s because option -s (--kernel-name) is the default. See why uname -s is better than uname -o.
The use of OS (instead of uname) simplifies the identification algorithm. You can still use solely uname, but you have to deal with if/else blocks to check all MinGW, Cygwin, etc. variations.
The environment variable OS is always set to "Windows_NT" on different Windows versions (see %OS% environment variable on Wikipedia).
An alternative of OS is the environment variable MSVC (it checks the presence of MS Visual Studio, see example using Visual C++).
Below I provide a complete example using make and gcc to build a shared library: *.so or *.dll depending on the platform. The example is as simplest as possible to be more understandable.
To install make and gcc on Windows see Cygwin or MinGW.
My example is based on five files
├── lib
│ └── Makefile
│ └── hello.h
│ └── hello.c
└── app
└── Makefile
└── main.c
Reminder: Makefile is indented using tabulation. Caution when copy-pasting below sample files.
The two Makefile files
1. lib/Makefile
ifeq ($(OS),Windows_NT)
uname_S := Windows
else
uname_S := $(shell uname -s)
endif
ifeq ($(uname_S), Windows)
target = hello.dll
endif
ifeq ($(uname_S), Linux)
target = libhello.so
endif
#ifeq ($(uname_S), .....) #See https://stackoverflow.com/a/27776822/938111
# target = .....
#endif
%.o: %.c
gcc -c $< -fPIC -o $#
# -c $< => $< is first file after ':' => Compile hello.c
# -fPIC => Position-Independent Code (required for shared lib)
# -o $# => $# is the target => Output file (-o) is hello.o
$(target): hello.o
gcc $^ -shared -o $#
# $^ => $^ expand to all prerequisites (after ':') => hello.o
# -shared => Generate shared library
# -o $# => Output file (-o) is $# (libhello.so or hello.dll)
2. app/Makefile
ifeq ($(OS),Windows_NT)
uname_S := Windows
else
uname_S := $(shell uname -s)
endif
ifeq ($(uname_S), Windows)
target = app.exe
endif
ifeq ($(uname_S), Linux)
target = app
endif
#ifeq ($(uname_S), .....) #See https://stackoverflow.com/a/27776822/938111
# target = .....
#endif
%.o: %.c
gcc -c $< -I ../lib -o $#
# -c $< => compile (-c) $< (first file after :) = main.c
# -I ../lib => search headers (*.h) in directory ../lib
# -o $# => output file (-o) is $# (target) = main.o
$(target): main.o
gcc $^ -L../lib -lhello -o $#
# $^ => $^ (all files after the :) = main.o (here only one file)
# -L../lib => look for libraries in directory ../lib
# -lhello => use shared library hello (libhello.so or hello.dll)
# -o $# => output file (-o) is $# (target) = "app.exe" or "app"
To learn more, read Automatic Variables documentation as pointed out by cfi.
The source code
- lib/hello.h
#ifndef HELLO_H_
#define HELLO_H_
const char* hello();
#endif
- lib/hello.c
#include "hello.h"
const char* hello()
{
return "hello";
}
- app/main.c
#include "hello.h" //hello()
#include <stdio.h> //puts()
int main()
{
const char* str = hello();
puts(str);
}
The build
Fix the copy-paste of Makefile (replace leading spaces by one tabulation).
> sed 's/^ */\t/' -i */Makefile
The make command is the same on both platforms. The given output is on Unix-like OSes:
> make -C lib
make: Entering directory '/tmp/lib'
gcc -c hello.c -fPIC -o hello.o
# -c hello.c => hello.c is first file after ':' => Compile hello.c
# -fPIC => Position-Independent Code (required for shared lib)
# -o hello.o => hello.o is the target => Output file (-o) is hello.o
gcc hello.o -shared -o libhello.so
# hello.o => hello.o is the first after ':' => Link hello.o
# -shared => Generate shared library
# -o libhello.so => Output file (-o) is libhello.so (libhello.so or hello.dll)
make: Leaving directory '/tmp/lib'
> make -C app
make: Entering directory '/tmp/app'
gcc -c main.c -I ../lib -o main.o
# -c main.c => compile (-c) main.c (first file after :) = main.cpp
# -I ../lib => search headers (*.h) in directory ../lib
# -o main.o => output file (-o) is main.o (target) = main.o
gcc main.o -L../lib -lhello -o app
# main.o => main.o (all files after the :) = main.o (here only one file)
# -L../lib => look for libraries in directory ../lib
# -lhello => use shared library hello (libhello.so or hello.dll)
# -o app => output file (-o) is app.exe (target) = "app.exe" or "app"
make: Leaving directory '/tmp/app'
The run
The application requires to know where is the shared library.
On Windows, a simple solution is to copy the library where the application is:
> cp -v lib/hello.dll app
`lib/hello.dll' -> `app/hello.dll'
On Unix-like OSes, you can use the LD_LIBRARY_PATH environment variable:
> export LD_LIBRARY_PATH=lib
Run the command on Windows:
> app/app.exe
hello
Run the command on Unix-like OSes:
> app/app
hello
I was recently experimenting in order to answer this question I was asking myself. Here are my conclusions:
Since in Windows, you can't be sure that the uname command is available, you can use gcc -dumpmachine. This will display the compiler target.
There may be also a problem when using uname if you want to do some cross-compilation.
Here's a example list of possible output of gcc -dumpmachine:
mingw32
i686-pc-cygwin
x86_64-redhat-linux
You can check the result in the makefile like this:
SYS := $(shell gcc -dumpmachine)
ifneq (, $(findstring linux, $(SYS)))
# Do Linux things
else ifneq(, $(findstring mingw, $(SYS)))
# Do MinGW things
else ifneq(, $(findstring cygwin, $(SYS)))
# Do Cygwin things
else
# Do things for others
endif
It worked well for me, but I'm not sure it's a reliable way of getting the system type. At least it's reliable about MinGW and that's all I need since it does not require to have the uname command or MSYS package in Windows.
To sum up, uname gives you the system on which you're compiling, and gcc -dumpmachine gives you the system for which you are compiling.
The git makefile contains numerous examples of how to manage without autoconf/automake, yet still work on a multitude of unixy platforms.
Update: I now consider this answer to be obsolete. I posted a new perfect solution further down.
If your makefile may be running on non-Cygwin Windows, uname may not be available. That's awkward, but this is a potential solution. You have to check for Cygwin first to rule it out, because it has WINDOWS in its PATH environment variable too.
ifneq (,$(findstring /cygdrive/,$(PATH)))
UNAME := Cygwin
else
ifneq (,$(findstring WINDOWS,$(PATH)))
UNAME := Windows
else
UNAME := $(shell uname -s)
endif
endif
That's the job that GNU's automake/autoconf are designed to solve. You might want to investigate them.
Alternatively you can set environment variables on your different platforms and make you Makefile conditional against them.
I ran into this problem today and I needed it on Solaris so here is a POSIX standard way to do (something very close to) this.
#Detect OS
UNAME = `uname`
# Build based on OS name
DetectOS:
-#make $(UNAME)
# OS is Linux, use GCC
Linux: program.c
#SHELL_VARIABLE="-D_LINUX_STUFF_HERE_"
rm -f program
gcc $(SHELL_VARIABLE) -o program program.c
# OS is Solaris, use c99
SunOS: program.c
#SHELL_VARIABLE="-D_SOLARIS_STUFF_HERE_"
rm -f program
c99 $(SHELL_VARIABLE) -o program program.c
I finally found the perfect solution that solves this problem for me.
ifeq '$(findstring ;,$(PATH))' ';'
UNAME := Windows
else
UNAME := $(shell uname 2>/dev/null || echo Unknown)
UNAME := $(patsubst CYGWIN%,Cygwin,$(UNAME))
UNAME := $(patsubst MSYS%,MSYS,$(UNAME))
UNAME := $(patsubst MINGW%,MSYS,$(UNAME))
endif
The UNAME variable is set to Linux, Cygwin, MSYS, Windows, FreeBSD, NetBSD (or presumably Solaris, Darwin, OpenBSD, AIX, HP-UX), or Unknown. It can then be compared throughout the remainder of the Makefile to separate any OS-sensitive variables and commands.
The key is that Windows uses semicolons to separate paths in the PATH variable whereas everyone else uses colons. (It's possible to make a Linux directory with a ';' in the name and add it to PATH, which would break this, but who would do such a thing?) This seems to be the least risky method to detect native Windows because it doesn't need a shell call. The Cygwin and MSYS PATH use colons so uname is called for them.
Note that the OS environment variable can be used to detect Windows, but not to distinguish between Cygwin and native Windows. Testing for the echoing of quotes works, but it requires a shell call.
Unfortunately, Cygwin adds some version information to the output of uname, so I added the 'patsubst' calls to change it to just 'Cygwin'. Also, uname for MSYS actually has three possible outputs starting with MSYS or MINGW, but I use also patsubst to transform all to just 'MSYS'.
If it's important to distinguish between native Windows systems with and without some uname.exe on the path, this line can be used instead of the simple assignment:
UNAME := $(shell uname 2>NUL || echo Windows)
Of course in all cases GNU make is required, or another make which supports the functions used.
Here's a simple solution that checks if you are in a Windows or posix-like (Linux/Unix/Cygwin/Mac) environment:
ifeq ($(shell echo "check_quotes"),"check_quotes")
WINDOWS := yes
else
WINDOWS := no
endif
It takes advantage of the fact that echo exists on both posix-like and Windows environments, and that in Windows the shell does not filter the quotes.
Note that Makefiles are extremely sensitive to spacing. Here's an example of a Makefile that runs an extra command on OS X and which works on OS X and Linux. Overall, though, autoconf/automake is the way to go for anything at all non-trivial.
UNAME := $(shell uname -s)
CPP = g++
CPPFLAGS = -pthread -ansi -Wall -Werror -pedantic -O0 -g3 -I /nexopia/include
LDFLAGS = -pthread -L/nexopia/lib -lboost_system
HEADERS = data_structures.h http_client.h load.h lock.h search.h server.h thread.h utility.h
OBJECTS = http_client.o load.o lock.o search.o server.o thread.o utility.o vor.o
all: vor
clean:
rm -f $(OBJECTS) vor
vor: $(OBJECTS)
$(CPP) $(LDFLAGS) -o vor $(OBJECTS)
ifeq ($(UNAME),Darwin)
# Set the Boost library location
install_name_tool -change libboost_system.dylib /nexopia/lib/libboost_system.dylib vor
endif
%.o: %.cpp $(HEADERS) Makefile
$(CPP) $(CPPFLAGS) -c $
Another way to do this is by using a "configure" script. If you are already using one with your makefile, you can use a combination of uname and sed to get things to work out. First, in your script, do:
UNAME=uname
Then, in order to put this in your Makefile, start out with Makefile.in which should have something like
UNAME=##UNAME##
in it.
Use the following sed command in your configure script after the UNAME=uname bit.
sed -e "s|##UNAME##|$UNAME|" < Makefile.in > Makefile
Now your makefile should have UNAME defined as desired. If/elif/else statements are all that's left!
I had a case where I had to detect the difference between two versions of Fedora, to tweak the command-line options for inkscape:
- in Fedora 31, the default inkscape is 1.0beta which uses --export-file
- in Fedora < 31, the default inkscape is 0.92 which uses --export-pdf
My Makefile contains the following
# set VERSION_ID from /etc/os-release
$(eval $(shell grep VERSION_ID /etc/os-release))
# select the inkscape export syntax
ifeq ($(VERSION_ID),31)
EXPORT = export-file
else
EXPORT = export-pdf
endif
# rule to convert inkscape SVG (drawing) to PDF
%.pdf : %.svg
inkscape --export-area-drawing $< --$(EXPORT)=$#
This works because /etc/os-release contains a line
VERSION_ID=<value>
so the shell command in the Makefile returns the string VERSION_ID=<value>, then the eval command acts on this to set the Makefile variable VERSION_ID.
This can obviously be tweaked for other OS's depending how the metadata is stored. Note that in Fedora there is not a default environment variable that gives the OS version, otherwise I would have used that!
An alternate way that I have not seen anyone talking about is using the built-in variable SHELL. The program used as the shell is taken from the variable SHELL. On MS-Windows systems, it is most likely to be an executable file with .exe extension (like sh.exe).
In that case, the following conditional test:
ifeq ($(suffix $(SHELL)),.exe)
# Windows system
else
# Non-Windows system
endif
Would have the same result as using the environment variable OS:
ifeq ($(OS),Windows_NT)
# Windows system
else
# Non-Windows system
endif
However, it seems the latter is the most popular solution, so I would recommend you stick with it.