Beginner in writing makefile; checking my Makefile - c

I am trying to understand how to write my own makefile for small project, so I have a few questions
my project consists of src directory that contains main.c, file1.c, file2.c, header1.h, and finally header2.h these files use some library from non standard library that I have created and non standard header file, the library directory is located in usr/lib/pr__lib and the header directory is located in usr/include/lib
so I should create two makefile.am one will be located in src directory and the other one will be in root directory of the project the makefile.am of the src directory is as shown below:
program_NAME := PRDSL
bin_PROGRAMS = PRDSL_AutoProject
program_INCLUDE_DIRS := /usr/bin/PR__bin
program_LIBRARY_DIRS := /usr/lib/PR__lib
CFLAGS += $(foreach includedir,$(program_INCLUDE_DIRS),-I$(includedir))
program_LIBRARIES := \
libprdependency \
libprdynarray_pic \
libprhistogram_pic \
libprlistofarrays \
libprlistofarrays_pic \
libprmalloc \
libvreo_wrapper_library
AM_LDFLAGS += $(foreach librarydir,$(program_LIBRARY_DIRS),-L$(librarydir))
AM_LDFLAGS += $(foreach library,$(program_LIBRARIES),-l$(library))
PRDSL_AutoProject_SOURCES = \
main.c \
file1.c \
file2.c
depend :
makedepend --$(CFLAGS) --$(PRDSL_AutoProject_SOURCES)
all: $(program_NAME)
2nd makefile.am at the root directory is as shown below:
SUBDIRS = src
PRDSL_AutoProjectdocdir = ${prefix}/doc/PRDSL_AutoProject
PRDSL_AutoProjectdoc_DATA = \
README\
COPYING\
AUTHORS\
ChangeLog\
INSTALL\
NEWS
INTLTOOL_FILES = intltool-extract.in \
intltool-merge.in \
intltool-update.in
EXTRA_DIST = $(PRDSL_AutoProjectdoc_DATA) \
$(INTLTOOL_FILES)
DISTCLEANFILES = intltool-extract \
intltool-merge \
intltool-update \
po/.intltool-merge-cache
# Remove doc directory on uninstall
uninstall-local:
-rm -r $(PRDSL_AutoProjectdocdir)
but I get the below errors and warnings while I was running automake command:
src/Makefile.am:20: error: 'program_LIBRARIES' is used but 'programdir' is undefined
src/Makefile.am:18: warning: 'CFLAGS' is a user variable, you should not override it;
src/Makefile.am:18: use 'AM_CFLAGS' instead
src/Makefile.am:43: error: AM_LDFLAGS must be set with '=' before using '+='
could any one review it and help me?

For src/Makefile.am:43: error: AM_LDFLAGS must be set with '=' before using '+=' see my answer to your previous question.
Yes, the following lines are telling you to use AM_CFLAGS instead of CFLAGS because, as the error indicates, CFLAGS is a user-specified value and you should leave it alone.
src/Makefile.am:18: warning: 'CFLAGS' is a user variable, you should not override it;
src/Makefile.am:18: use 'AM_CFLAGS' instead
I believe, though I am far from certain, that the reason you are getting src/Makefile.am:20: error: 'program_LIBRARIES' is used but 'programdir' is undefined is because you are using a variable program_LIBRARIES that the autotools believe is a variable they should be using but that you are not using in that way and are instead using in the loop which sets the value for AM_LDFLAGS. As such, and assuming I am correct, if you rename that to not follow the autotool variable naming scheme I believe that error will go away.

Related

Adding a loop inside an if statement in a Makefile

I'm trying to write a command which builds either a file that I pass as a flag, or all files.
I have this for now:
SOURCES := $(shell find ./src -name '*.c')
FILE?="all"
# Compile source files into object files
compile:
ifeq ($(FILE), "all")
for file_dir in $(SOURCES); do \
$(CC) $(CFLAGS) -o $($$file_dir:.c=.o) -c $$file_dir \
done;
else
$(CC) $(CFLAGS) -o ./out/$(FILE).o -c ./src/$(FILE).c
endif
But since I've added the loop, I get this error:
$ make compile
for file_dir in ./src/board.c ./src/main.c; do \
gcc "-Wall" -o -c $file_dir \
done;
/bin/sh: -c: line 1: syntax error: unexpected end of file
make: *** [compile] Error 2
I agree with the comments above: this is a bad way to write a makefile. If you just want to compile every file every time, write a shell script.
But, to explain the error you see:
You have your semicolon in the wrong place:
for file_dir in $(SOURCES); do \
$(CC) $(CFLAGS) -o $($$file_dir:.c=.o) -c $$file_dir \
done;
The semicolon needs to be after the compile command, not after the done. Without a semicolon after the compile command, the done keyword is used as an argument to the compiler and then there is no done keyword, leading to the shell syntax error you see.
for file_dir in $(SOURCES); do \
$(CC) $(CFLAGS) -o $($$file_dir:.c=.o) -c $$file_dir; \
done
ETA
Also, this is wrong: $($$file_dir:.c=.o) You are trying to use a make variable expansion on a shell variable. That cannot work: all make variables are expanded BEFORE the shell is invoked.
You need something like $${file_dir%.c}.o, using shell features.
PPS
All your makefile appears to do is compile the files. Make has built-in rules that know how to do this. All you have to do is this:
SOURCES := $(shell find ./src -name '*.c')
compile: $(SOURCES:.c=.o)
That's it. Make knows how to compile a .c file and create a .o file, on its own.

C Project - How to manage a feature list?

I have multiple features that can be enabled or disabled at the build time in a project.
The current implementation uses declarations such as #define FEATURE_FOO. Whenever I need to do something related to a particular feature, I use a pre-processor directive such as #ifdef.
The features definitions are stored inside a global header file.
This approach have two drawbacks:
It requires to #include this global header in every file, before any other header.
I cannot easily disable a C file:
This is not very good:
// file: foo.c
#include <stdio.h>
#include "main_header.h"
#ifdef FEATURE_FOO
...
#endif
Because I prefer this:
// file: foo.c
#ifdef FEATURE_FOO
#include <stdio.h>
...
#endif
So, another approach to this problem is to declare all my features at the build time:
gcc -DFEATURE_FOO -c %< -o %#
What I don't like here is that I manually need to pass each feature to my compiler.
An acceptable workaround would be to read a features.list file that contains all the features. In my Makefile I will have:
DEFINES=$(shell perl -ne 'print "-DFEATURE_$1 " if /(\w+)/' features.list)
%o: %c
gcc $(DEFINES) -c %< -o $#
What better alternative can I find?
You can use gcc's option -include myheader.h.
It adds the content of myheader.h to the very beginning of the current translation unit's source.
I am using a GNU make based build process for most of my projects and although it wasn't about features so far, I used techniques that could help you here, too.
First, the idea to have a configuration file is very good, but why not just have it in make syntax and include it?
I use something like this
# default configuration
CC := gcc
DEBUG := 0
GCC32 := 0
USELTO := 1
# read local configuration
-include defaults.mk
You could use this for having a list of features, e.g. in your defaults.mk
FEATURES := foo bar baz
and then do something like
FEATUREDEFINES := $(addprefix -DFEATURE_, $(FEATURES))
There's a lot more black magic possible with GNU make when you use the $(eval ...) function -- this might be a good alternative for completely excluding a source file from compilation depending on your settings. I use this for platform-specific implementations. For example I have this included Makefile for building a binary:
P:= src
T:= csnake
csnake_SOURCES:= csnake.c utils.c game.c board.c snake.c food.c screen.c
csnake_PLATFORMSOURCES:= ticker.c
csnake_LDFLAGS:= -lm
csnake_posix_LDFLAGS:= -lcurses
csnake_dos_LDFLAGS:= -Wl,-Bstatic -lpdcurses
csnake_win32_LDFLAGS:= -static-libgcc -Wl,-Bstatic -lpdcurses \
-Wl,-Bdynamic -lwinmm
csnake_win32_RES:= res$(PSEP)csnake.rc
$(eval $(BINRULES))
My P is the current relative path in the source tree, T is the target to build and PSEP is just a helper variable containing / or \ in order to be compatible with windows. The rest should be quite self-explanatory -- for $(T)_PLATFORMSOURCES, $(BINRULES) looks in the relative path platform/$(PLATFORM)/. It works like this:
define BINRULES
BINARIES += $$(BINDIR)$$(PSEP)$(T)$$(EXE)
$(T)_SOURCES_FULL := $$(addprefix $(P)$$(PSEP),$$($(T)_SOURCES))
ifneq ($$(strip $$($(T)_PLATFORMSOURCES)),)
$(T)_SOURCES_FULL += $$(addprefix \
$(P)$$(PSEP)platform$$(PSEP)$$(PLATFORM)$$(PSEP), \
$$($(T)_PLATFORMSOURCES))
endif
[...] (... further rules ... )
endef
All these double dollars are there because $(eval ...) will expand variables -- that's desired for $(T) and $(P) but not for all the other ones, so they are protected with an extra dollar. I'm just quoting the part doing the magic for deciding which files to compile here. If you consider doing something like this, see the full example

Recursive makefile No rule to make target `all'. Stop

The project is about a root directory that contains a src directory. I created two makefiles, one at the top directory and the other at the src directory. Here is the makefile of the src directory:
## Process this file with automake to produce Makefile.in
## Created by Netbeans
AM_CPPFLAGS = \
-DPACKAGE_LOCALE_DIR=\""$(localedir)"\" \
-DPACKAGE_SRC_DIR=\""$(srcdir)"\" \
-DPACKAGE_DATA_DIR=\""$(pkgdatadir)"\"
bin_PROGRAMS = scratchautotool
program_INCLUDE_DIRS := /usr/bin/PR__bin
program_LIBRARY_DIRS := /usr/lib/PR__lib
CFLAGS += $(foreach includedir,$(program_INCLUDE_DIRS),-I$(includedir))
AM_LDFLAGS += $(foreach librarydir,$(program_LIBRARY_DIRS),-L$(librarydir))
scratchautotool_SOURCES = \
main.c \
Task.c \
SeedVP.c
depend :
makedepend --$(CFLAGS) --$(scratchautotool_SOURCES)
The makefile of the root directory is shown below:
## Process this file with automake to produce Makefile.in
## Created by Netbeans
SUBDIRS = src
scratchautotooldocdir = ${prefix}/doc/scratchautotool
scratchautotooldoc_DATA = \
README\
COPYING\
AUTHORS\
ChangeLog\
INSTALL\
NEWS
INTLTOOL_FILES = intltool-extract.in \
intltool-merge.in \
intltool-update.in
EXTRA_DIST = $(scratchautotooldoc_DATA) \
$(INTLTOOL_FILES)
DISTCLEANFILES = intltool-extract \
intltool-merge \
intltool-update \
po/.intltool-merge-cache
# Remove doc directory on uninstall
uninstall-local:
-rm -r $(scratchautotooldocdir)
but while I was invoking make from the terminal it gave me the following error:
Making all in src
make[2]: Entering directory `../src'
make[2]: *** No rule to make target `all'. Stop.
make[2]: Leaving directory `../src'
The error is pretty clear: it is expecting to find the target all, and isn't. I'm assuming you called make without specifying a target; the default is (surprise!) all.
I want to share the solution for my question with any one who will encounter the same issue, I have modified the makefile as shown below:
all: scratchautotool
depend :
makedepend $(CFLAGS) $(scratchautotool_SOURCES)
but this also gave the same error, so I've searched and I've found that the makedepend didn't exist so I had to install xutils-dev package(which includes the program makedepend) using the below command:
sudo apt-get install xutils-dev
but this command solves the above mentioned issue only! but the makefile now has a new issue :)

Make implicit rules and header files

Make's implicit rules are supposedly there to make writing Makefiles easier but, if my understanding is correct, if my C files depend on any header files, I need to write the rule, explicitly. Am I right? This seems to serioiusly lower the usefulness of implicit rules, since most C files depend on a couple of header files, so I thought perhaps there's something I'm missing.
You can autogenerate header dependencies with gcc using the following makefile snippet
SOURCES := $(wildcard *.c)
DEPS := $(SOURCES:%.c=%.d)
CFLAGS += -MMD
-include $(DEPS)
The code might need some adjustments to work with your particular ruleset.
You don't need to write the rule, only the dependencies. Example:
foo.o : foo.h bar.h
The file foo.o will still be generated by the implicit rule, but have the additional dependencies foo.h and bar.h. This dependency line can also be auto-generated by most compilers.
make is not a utility which goes and reads inside your C file and determines which header it is including. It works based on changed timestamps of files. Hence, irrespective of whether a target is dependent on a header or any other file, you need to explicitly tell make of the dependencies.
gcc can help you ease your job by generating a dependency list for you like this
main.c
#include<stdio.h>
#include"my_header.h"
int main ()
{
return 0;
}
And then,
gcc -M main.c
Now, with -M preprocessor flag, it will automatically generate a dependency list like
main.o: main.c /usr/include/stdio.h /usr/include/features.h \
/usr/include/bits/predefs.h /usr/include/sys/cdefs.h \
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
/usr/include/gnu/stubs-64.h \
/usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
my_header.h
gcc has figured out all includes inside stdio.h too!

debugging a makefile

I have a makefile which has statements like below:
TOPICS = dmic
SRV_MODE =
ifeq "$(SRV_FLAG)" "ON"
SRV_MODE = 2
endif
vpath d%_srv.h $(CNT_PATH)
USER_PRE_TARGETS := $(foreach topic,$(TOPICS),$(topic)_srv.h)
dmic_srcs = $(wildcard $(CCWSCA)/dmic/src/*.c) \
$(wildcard $(CCWSCA)/dmic/src/*.ppc)
dmic_srv.h: $(dmic_srcs)
srvgen dmic $(SRV_MODE)
users_topic =
users_topic := $(shell ls -tr $(CCWPA)/$(CCBB)/Makefile.pre* | \
tail -1 | awk 'BEGIN{FS="Makefile.pre."}{printf("%s\n", $$2);}')
USER_PRE_TARGETS := $(foreach topic,$(users_topic),d$(topic)_srv.h)
After I run the build, I get messages like the ones below:
gmake: Entering directory `/veluser2/vel/abp/bvijays/proj/c9mi790V64OG/cmi9dl'
echo dmic
dmic
srvgen dmic 2
Working on directory : /veluser2/vel/abp/bvijays/bb/cmi9dl/v79_0/dmic/src
Working on directory : /velhome/vel/ccvel/ccvel/bb/cmi9dl/v79_0/dmic/src
foreach: No match.
gmake: *** [ddmic_srv.h] Error 1
gmake: Target `pre' not remade because of errors.
gmake: Leaving directory `/veluser2/vel/abp/bvijays/proj/c9mi790V64OG/cmi9dl'
So it seems like there is some issue with the foreach command issued?
As I am new to these makefiles, could anybody please suggest how to debug the makefile?
This is a bit of a mess, and it is hard to diagnose without knowing more about the environment it is running it. But lets go with a few basics:
You have only defined on target (dmic_srv.h), so when you run GNU make without arguments it will use that target.
Making the header depend on the source files is very unusual, I doubt that is what you want this to do. but you're doing code generation, so you are OK there.
There are two different kinds of assignment in GNU make. Plain = has lazy evaulation, but := forces immediate evaluation. This effects the environment in which you $(foreach )'s are running.
You have two definitions of USER_PRE_TARGETS, but never use it anywhere. Added: Given that the all the $(foreach ) commands exist in these definitions, you might just remove these and see if it get better.

Resources