What's the DRY version of the following Makefile targets? - makefile

I don't know how to execute a command stored as a variable or how to use ifeq inside of a target, so I have a very redundant Makefile at the moment!
Ideally I'd like to have just one target (all) which would run the stored command on Mac and run it twice on Linux, once with -m32 and once with -m64.
all:
echo PLEASE SELECT OS, e.g. make linux
exit 1
mac:
gcc $(SHARED_OPT) $(GENERAL_CFLAGS) $(PLATFORM_CFLAGS) -o $(BUILD_DIR)$(BUILD_NAME) $(SOURCE) $(LIBRARIES)
linux:
gcc $(SHARED_OPT) $(GENERAL_CFLAGS) $(PLATFORM_CFLAGS) -o $(BUILD_DIR)$(BUILD_NAME64) $(SOURCE) $(LIBRARIES64) -m64
gcc $(SHARED_OPT) $(GENERAL_CFLAGS) $(PLATFORM_CFLAGS) -o $(BUILD_DIR)$(BUILD_NAME) $(SOURCE) $(LIBRARIES) -m32
UPDATE: This is what I ended up with, after reading the various suggestsions. (Yes I know I should use autoconf . . .) Thanks everyone for your help!
ifeq($(PLATFORM), Linux)
COMMON = -pthread -fPIC
PLATFORM_CFLAGS = $(COMMON) -m32
PLATFORM_CFLAGS64 = $(COMMON) -m64
endif
ifeq ($(PLATFORM), Darwin)
PLATFORM_CFLAGS = -arch i386 -arch ppc -arch x86_64 -mmacosx-version-min=10.5 -isysroot /Developer/SDKs/MacOSX10.5.sdk
endif
all: $(PLATFORM)_all
Darwin_all:
mkdir -p ../../../../tmp
gcc $(SHARED_OPT) $(GENERAL_CFLAGS) $(PLATFORM_CFLAGS) -o $(BUILD_DIR)$(BUILD_NAME) $(SOURCE) $(LIBRARIES)
Linux_all: Darwin_all
gcc $(SHARED_OPT) $(GENERAL_CFLAGS) $(PLATFORM_CFLAGS64) -o $(BUILD_DIR)$(BUILD_NAME64) $(SOURCE) $(LIBRARIES64)

You make macros do most of the work, noting that you should use $(CC) rather than gcc.
BUILD_COMMAND = $(CC) $(SHARED_OPT) $(GENERAL_CFLAGS) $(PLATFORM_CFLAGS)
BUILD_NAME32 = $(BUILD_NAME)
TARGET_32 = $(BUILD_DIR)$(BUILD_NAME32)
TARGET_64 = $(BUILD_DIR)$(BUILD_NAME64)
LIBS_32 = $(LIBRARIES)
LIBS_64 = $(LIBRARIES64)
OPTS_32 = -m32
OPTS_64 = -m64
# We could do some fancy stuff here...
# Except that we will remove the commands momentarily
all:
echo PLEASE SELECT OS, e.g. make linux
exit 1
# Note that without a qualifier
# - MacOS X 10.5.x will build 32-bit
# - MacOS X 10.6.x will build 64-bit
# But why not build both anyway?
mac:
$(BUILD_COMMAND) -o $(TARGET_64) $(SOURCE) $(LIBS_64) $(OPTS_64)
$(BUILD_COMMAND) -o $(TARGET_32) $(SOURCE) $(LIBS_32) $(OPTS_32)
linux:
$(BUILD_COMMAND) -o $(TARGET_64) $(SOURCE) $(LIBS_64) $(OPTS_64)
$(BUILD_COMMAND) -o $(TARGET_32) $(SOURCE) $(LIBS_32) $(OPTS_32)
Oh, and look, the commands are the same for Linux and MacOS X now...so we can do:
BUILD_COMMAND = $(CC) $(SHARED_OPT) $(GENERAL_CFLAGS) $(PLATFORM_CFLAGS)
BUILD_NAME32 = $(BUILD_NAME)
TARGET_32 = $(BUILD_DIR)$(BUILD_NAME32)
TARGET_64 = $(BUILD_DIR)$(BUILD_NAME64)
LIBS_32 = $(LIBRARIES)
LIBS_64 = $(LIBRARIES64)
OPTS_32 = -m32
OPTS_64 = -m64
all:
$(BUILD_COMMAND) -o $(TARGET_64) $(SOURCE) $(LIBS_64) $(OPTS_64)
$(BUILD_COMMAND) -o $(TARGET_32) $(SOURCE) $(LIBS_32) $(OPTS_32)
Gosh, it is hard work writing $(XXX) instead of ${XXX} as I normally do in my makefiles.
Basically, we apply DRY (Don't Repeat Yourself) by making names boringly systematic. Makefiles should not be exciting.
If you still want to have a difference between your platforms, then you can do something along the lines suggested by Ivan Andrus. GNU Make allows you to evaluate shell commands, so:
BUILD_COMMAND = $(CC) $(SHARED_OPT) $(GENERAL_CFLAGS) $(PLATFORM_CFLAGS)
BUILD_NAME32 = $(BUILD_NAME)
TARGET_32 = $(BUILD_DIR)$(BUILD_NAME32)
TARGET_64 = $(BUILD_DIR)$(BUILD_NAME64)
LIBS_32 = $(LIBRARIES)
LIBS_64 = $(LIBRARIES64)
OPTS_32 = -m32
OPTS_64 = -m64
all: $(shell uname)
Linux:
$(BUILD_COMMAND) -o $(TARGET_64) $(SOURCE) $(LIBS_64) $(OPTS_64)
$(BUILD_COMMAND) -o $(TARGET_32) $(SOURCE) $(LIBS_32) $(OPTS_32)
Darwin:
$(BUILD_COMMAND) -o $(TARGET_32) $(SOURCE) $(LIBS_32)
If you feel you can't rely on GNU Make, then:
BUILD_COMMAND = $(CC) $(SHARED_OPT) $(GENERAL_CFLAGS) $(PLATFORM_CFLAGS)
BUILD_NAME32 = $(BUILD_NAME)
TARGET_32 = $(BUILD_DIR)$(BUILD_NAME32)
TARGET_64 = $(BUILD_DIR)$(BUILD_NAME64)
LIBS_32 = $(LIBRARIES)
LIBS_64 = $(LIBRARIES64)
OPTS_32 = -m32
OPTS_64 = -m64
BUILD_32 = use_32_bit
BUILD_64 = use_64_bit
BUILD_TYPE = $(BUILD_32) $(BUILD_64)
.PHONEY: $(BUILD_32) $(BUILD_64)
all: $(BUILD_TYPE)
use_64_bit:
$(BUILD_COMMAND) -o $(TARGET_64) $(SOURCE) $(LIBS_64) $(OPTS_64)
use_32_bit:
$(BUILD_COMMAND) -o $(TARGET_32) $(SOURCE) $(LIBS_32) $(OPTS_32)
By default this will compile both 32-bit and 64-bit versions.
If you want 32-bit only or 64-bit only, run the appropriate one of these two:
make BUILD_TYPE=use_32_bit
make BUILD_TYPE=use_64_bit

This is pretty simple, but you should be able to adapt it to more complicated things by changing the shell command that you run.
PLATFORM := $(shell uname)
all:$(PLATFORM)
Darwin:
echo Darwin
Linux:
echo Linux

You have some good advice there (probe the platform, use variables) but you should also be aware that you're running very close to the point where it is better to stop thinking in terms of supporting platforms and instead in terms of describing features that you require for your software and using autoconf (and family) to discover what is actually present.
Mind you, if it is a GUI app then you'll probably have so many differences between the OSX code and the Linux/X11 code that detecting by platform is reasonable. It's just rare that you need to do that for anything that's command-line oriented, as OSX looks a lot like plain old Unix then.

Related

compiler error gcc-8: error: unrecognized command line option '-no-pie'

I am trying to compile using this makefile but got this error. I can compile using school's linux computer with gcc 6.3. I tried using my MacOS mojave using a few different version of gcc from homebrew (gcc-8, gcc-4.9, gcc-6) but I get this error consistently.
CC = gcc-8
OPT = -O3 -g
LIBS = -lz -lcvp -lz
FLAGS = -std=c++11 -L. $(LIBS) $(OPT) -no-pie
OBJ = myprogram.o
DEPS = cvp.h myprogram.h
all: cvp
cvp: $(OBJ)
$(CC) $(FLAGS) -o $# $^
%.o: %.cc $(DEPS)
$(CC) $(FLAGS) -c -o $# $<
.PHONY: clean
clean:
rm -f *.o cvp
Going off information found here: https://github.com/xd009642/tarpaulin/issues/7#issuecomment-317180523
The problem may very well be related to how gcc is built: "Builds of gcc that don't have the --enable-default-pie flag set at compile time because they are too old or have the --disable-default-pie flag just don't have the -no-pie linker flag"
You might need to update or recompile gcc for that environment to be able to use that flag if important to you.

UEFI application for Intel Galileo (i586)

I'm working on a simple application that will be running on a Intel Galileo's board as UEFI app. I've started with a "Hello, World" app and tested it under qemu-system-i386 and it works well. Then, I've run it under Galileo EFI Shell and it stuck (nothing happend and never returned anything - like a never-ending loop). I know that Intel Galileo's Quark processor is a i586 architecture. It shouldn't be a problem to run application compiled for i386 under i586, due to the backward compatibility, am I right? Or am I missing something?
I'm using Ubuntu 14.04 (32-bit) for development with GCC 5.4.0 (default). Also, I'm using gnu-efi in version 3.0.4.
Should I build a cross-compiler? Will it resolve all of my problems? Is it necessary?
Here is a sample code:
#include <efi.h>
#include <efilib.h>
EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
InitializeLib(ImageHandle, SystemTable);
Print(L"Hello, World!\n");
return EFI_SUCCESS;
}
Here is my Makefile:
ARCH = ia32
OBJS = src/main.o
HEADERS =
TARGET = build/main.efi
EFIINC = lib/gnu-efi/inc
EFIINCS = -I$(EFIINC) -I$(EFIINC)/$(ARCH) -I$(EFIINC)/protocol
LIB = lib/gnu-efi/$(ARCH)/lib
EFILIB = lib/gnu-efi/gnuefi
EFI_CRT_OBJS = $(EFILIB)/crt0-efi-$(ARCH).o
EFI_LDS = $(EFILIB)/elf_$(ARCH)_efi.lds
CFLAGS = $(EFIINCS) -ffreestanding -fno-stack-protector -fpic -fshort-wchar -mno-red-zone -Wall -masm=intel
LDFLAGS = -nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L $(EFILIB) -L $(LIB) $(EFI_CRT_OBJS)
all: $(TARGET)
build: $(TARGET)
src/%.so: $(OBJS) $(HEADERS)
ld $(LDFLAGS) $(OBJS) -o $# -l:libefi.a -l:libgnuefi.a
build/%.efi: src/%.so
objcopy -j .text -j .sdata -j .data -j .dynamic \
-j .dynsym -j .rel -j .rela -j .reloc \
--target=efi-app-$(ARCH) $^ $#
run:
qemu-system-i386 bin/OVMF.fd -hda fat:build
.PHONY: all build run

Make compiler options

I am running a 32 bit machine with Ubuntu 14.04. I want to execute sudo make to generate a C++ compiled file (gcc-4.8.4). My Makefile is
Makefile (edited based on comments from ddumbugie):
CDefines = -DLINUXGXX
COMPFLAGS =
FlameManCC = gcc
FlameManCCOpts = -O3
OBJECTS = alligator.o
COpts = $(COMPFLAGS) $(FlameManCCOpts) $(CDefines)
CC = $(FlameManCC)
alligator.o: alligator.c
$(CC) $(COpts) -c $< -o $#
Executing
make -n
gives me
gcc -O3 -DLINUXGXX -c alligator.c -o alligator.o
But
sudo make -n
gives me
DLINUXGXX -c alligator.c -o alligator.o
Definitely command DLINUXGXX is not understood. However, I need to remain a super-user for a subsequent command executed by make. How can I resolve this?
CDefines = -DLINUXGXX
COpts = -O3 $(CDefines)
alligator.o: alligator.c
$(CC) $(COpts) -c $< -o $#
It requires -o compiler options.
$# means the target. That is alligator.o.
$< means the dependency. That is alligator.c.

Multiple targets and libraries in Makefile

I have this folder/files structure:
./libs/
helpers.c
helpers.h
./a_app.c
./b_app.c
./c_app.c
Each app depends on helpers lib and GStreamer, so I need to compile helpers.o (inside libs/ folder) and then link each app.
Currently I have this Makefile:
CC = gcc
CFLAGS = -g -Wall -w
LFLAGS = -g -Wall -w -c
CFLAGS += `pkg-config --cflags gstreamer-app-0.10`
LFLAGS += `pkg-config --cflags gstreamer-app-0.10`
LDFLAGS =
LDFLAGS += `pkg-config --libs gstreamer-app-0.10`
all: examples
examples: helpers.o a_app
$(info *** examples ***)
helpers.o:
$(info *** helpers.o ***)
$(CC) $(LFLAGS) libs/helpers.c -o libs/helpers.o $(LDFLAGS)
a_app: a_app.o
$(CC) $(CFLAGS) libs/helpers.o a_app.o -o a_app $(LDFLAGS)
a_app.o: a_app.c
$(info *** a_app.o ***)
$(CC) $(LFLAGS) a_app.c $(LDFLAGS)
While I could add b_appand c_app I'm looking for another (more elegant) way of doing it. Can't I just say that I have a, b and c _app and let Makefile compile them all and link them against GStreamer and helpers?
Also, is there any way to make Makefile compile files without needing to tell it -o name_of_file (and perhaps make it compile them in the folder that they are, because of the helpers library).
Okay, so - as we discussed in the comments, make can figure out how to make the .o files, so those rules are unnecessary. To make a generalized rule for all your *_app files (assuming they all have the same dependency on helpers.h, you can do this:
%_app: %_app.o libs/helpers.o
Make uses the % as a wildcard, and in the rule/dependency line the wildcard will expand to the same thing in the dependencies as it did in the rule. In the actual execution, you can use $* to get the same string. So a single rule for all your *_app executables winds up looking a bit like this:
%_app: %_app.o libs/helpers.o
$(CC) $(CFLAGS) libs/helpers.o $*_app.o -o $*_app $(LDFLAGS)
I was testing this on my machine (hence comments instead of answers, and wound up writing this Makefile:
CC = gcc
CFLAGS = -g -Wall -w
LFLAGS = -g -Wall -w -c
CFLAGS += `pkg-config --cflags gstreamer-app-0.10`
LFLAGS += `pkg-config --cflags gstreamer-app-0.10`
LDFLAGS =
LDFLAGS += `pkg-config --libs gstreamer-app-0.10`
new: clean all
clean:
rm -rf *.o */*.o *_app
all: examples
examples: a_app b_app
%_app: %_app.o libs/helpers.o
$(CC) $(CFLAGS) libs/helpers.o $*_app.o -o $*_app $(LDFLAGS)
Does that all make sense?
EDIT: It occurs to me that GNU Make can run some commands on the command line and store the string for its own purposes.
$(shell ls *_app.c | sed 's/.c//') will expand into all the apps you have in the current directory. so you can say:
examples: $(shell ls *_app.c | sed 's/\.c//')
Or, as I think is a little better:
...
ALLAPPS = $(shell ls *_app.c | sed 's/\.c//')
...
all: $(ALLAPPS)
That way make can be used to make everything, and make ?_app can be used to compile one app at a time.
Super ultra mega double EDIT:
Using a bald % operator as a target will bust up Make's ability to auto generate .o files. Here's the solution we worked out in chat:
CC = gcc
CFLAGS = -g -Wall -w
LFLAGS = -g -Wall -w -c
CFLAGS += $(shell pkg-config --cflags gstreamer-app-0.10)
LFLAGS += $(shell pkg-config --cflags gstreamer-app-0.10)
LDFLAGS =
LDFLAGS += $(shell pkg-config --libs gstreamer-app-0.10)
TARGETS = $(shell ls *.c | sed 's/\.c//')
new: clean all
clean:
rm -rf *.o */*.o *_app
all: examples
examples: $(TARGETS)
.SECONDEXPANSION:
$(TARGETS): libs/helpers.o $$#.o
$(CC) $(CFLAGS) libs/helpers.o $#.o -o $# $(LDFLAGS)

How should a very simple Makefile look like for Cuda compiling under linux

I want to compile a very basic hello world level Cuda program under Linux. I have three files:
the kernel: helloWorld.cu
main method: helloWorld.cpp
common header: helloWorld.h
Could you write me a simple Makefile to compile this with nvcc and g++?
Thanks,
Gabor
I've never heard of Cuda before, but from the online documentation it looks as if X.cu is supposed to be compiled into X.o, so having helloWorld.cu and helloWorld.cpp is not a good idea. With your permission I'll rename the "kernel" helloKernel.cu, then this should work:
NVCC = nvcc
helloWorld.o: helloWorld.cpp helloWorld.h
$(NVCC) -c %&lt -o $#
helloKernel.o: helloKernel.cu
$(NVCC) -c %&lt -o $#
helloWorld: helloWorld.o helloKernel.o
$(NVCC) %^ -o $#
(Note that those leading spaces are tabs.)
If that works, try a slicker version:
NVCC = nvcc
helloWorld.o: %.o : %.cpp %.h
helloKernel.o: %.o : %.cu
%.o:
$(NVCC) -c %&lt -o $#
helloWorld: helloWorld.o helloKernel.o
$(NVCC) %^ -o $#
Just in case, here's my variant. I use it to compile CUDA projects on Mac, but I think it will suit Linux too. It requires CUDA SDK.
BINDIR = ./ # places compiled binary in current directory
EXECUTABLE := helloWorld
CCFILES := helloWorld.cpp
CUFILES := helloWorld.cu
# an ugly part - setting rootdir for CUDA SDK makefile
# look for common.mk - I don't know where SDK installs it on Linux -
# and change ROOTDIR accordingly
ROOTDIR := /Developer/GPU\ Computing/C/common
include $(ROOTDIR)/../common/common.mk
My version, verbose but transparent:
myapp: myapp.o
g++ -fPIC -o $# $< -L /usr/local/cuda/lib -lcudart
myapp.o: myapp.cu
/usr/local/cuda/bin/nvcc --compiler-options -fno-strict-aliasing \
-I/usr/local/cuda/include \
-DUNIX -O2 -o $# -c $<
matrixMul: matrixMul.o
g++ -fPIC -o $# $< -L /usr/local/cuda/lib -lcudart
# It MUST be named .cu or nvcc compiles as regular C !!! (no __global__)
matrixMul.o: matrixMul.cu
/usr/local/cuda/bin/nvcc --compiler-options -fno-strict-aliasing \
-I/usr/local/cuda/include \
-DUNIX -O2 -o $# -c $<
Here is an example what my current project looks like. As you can see there is a few OpenGL libraries
ce : cudaExample.c cudaExample.h
cp cudaExample.c cudaExample.cu
/usr/local/cuda/bin/nvcc -arch=sm_20 -o ce -lglut -lGL -lGLU -lXext -lXmu -lX11 -lm cudaExample.cu
then run make ce
and ./ce

Resources