Makefile Linux how to execute function for each variable - linux

I am trying to setup a Makefile with dependencies. The dependencies are specified in a variable.
MATH_VER=1.1
EXTERNAL_DEPS=MATH GC LOG
I want it to run a function that tries to figure out the location of each of the external libs based on whats available.
So I added a rule setversion,
all:setversion myexe
setversion:
$(foreach CHKLIB, $(EXTERNAL_DEPS), $(call checklib, $(CHKLIB)))
I have the function that does the checking
checklib = ifeq ($(wildcard $(ROOT)/$(var)/$(var)_VER),)
echo 'Bad dir'
$(var)_ROOT=$SOMEOTHERDIR
else
echo 'Good dir'
$(var)_ROOR=$(ROOT)/$(var)/$(var)_VER
endif
This dosent work - but I think it gives a good idea of what Im looking for. Can anyone point me to how this can be accomplished?
Thanks

Edit: I tested this out on my system and it seemed to do what you need.
define set_deps
ifeq ($(wildcard $(ROOT)/$(1)/$($(1)_VER)/),)
$(1)_ROOT=$(DEFAULT_DIR)
else
$(1)_ROOT=$(ROOT)/$(1)/$($(1)_VER)/
endif
endef
$(foreach lib, $(EXTERNAL_DEPS), $(eval $(call set_deps,$(lib))))
$(foreach lib, $(EXTERNAL_DEPS), $(eval $(info $(lib) => $($(lib)_ROOT))))
Make sure to check out the documentation for the eval function.
Also, if you wanted to match against a list of possible directories you could use the following.
define set_deps
$(1)_ROOT = $(firstword $(wildcard $(ROOT)/$(1)/$($(1)_VER)/) $(DEFAULT_DIR))
endef

This site might help:
Note that in the example, the result of the foreach call is what is stored, not the side-effects of calling each function in the foreach loop.

Related

how to set a compiler flag by enabling a feature in autoconf

I am very new to Autoconf, I would like to have a configure file that when I call: configure --enable-gtest=yes, such that it adds a compiler flag. The following code that I put up after searching looks as follow, but does not do the trick.
Thanks a lot
this is how my makefile looks like.
-include Makefile.config
SRC = $(wildcard *.cpp)
OBJ = $(SRC:.cpp=.o)
install: $(OBJ)
$(CC) $(CXXFLAGS) $(DEBUGFLAG) $(OBJ) -o run
%.o:%.cpp
$(CC) $(CXXFLAGS) $(DEBUGFLAG) -c $<
clean:
rm -f *.o
this is my configure.ac
AC_INIT([test], [1.7.0])
AC_PREREQ([2.59])
AC_CONFIG_MACRO_DIR([m4])
AC_CHECK_PROGS(CXX, [g++ c++ clang], ":")
AC_PROG_CXX
AC_SUBST(CXX)
AC_ARG_ENABLE([debug],
[ --enable-debug Turn on debugging],
[case "${enableval}" in
yes) debug=true ;;
no) debug=false ;;
*) AC_MSG_ERROR([bad value ${enableval} for --enable-debug]) ;;
esac],[debug=false])
AM_CONDITIONAL([DEBUG], [test x$debug = xtrue])
AC_CONFIG_FILES(Makefile.config)
AC_OUTPUT
and my
Makefile.config.in
CC = #CXX#
CXXFLAGS = -std=c++14
if DEBUG
DBG = debug
else
DBG =
endif
thanks
Pretty close! But not quite.
You're probably best off using Automake, which automates a lot of the Makefile drudgery for you. But if you really want to avoid it, then you have to write your Makefile correctly according to what you write in configure.ac.
AM_CONDITIONAL([DEBUG], [test x$debug = xtrue])
This defines a couple of autoconf substitutions, like DEBUG_TRUE and DEBUG_FALSE. The if form you've chosen only works in Automake, in an ordinary Makefile you have to write something like:
#DEBUG_TRUE#...stuff when
#DEBUG_TRUE#...true
Alternatively you can test the values of the substitutions using GNU make's if statement.
Another approach is not to use AM_CONDITIONAL at all but rather AC_SUBST the thing you want to use in your Makefile.config.in.

Gnu make - how to handle sources from outside my project tree

Using:
Linux as build host (kubuntu 14.04)
Gnu make 3.81
Compiling some C/C++ projects
I have a directory tree like this:
Repository/
Framework/
Source/
Subdir1/
Subdir2/
Subdir3/
et cetera
something more
Projects/
Project1/
Source/
SubdirA/
SubdirB/
SubdirX/
et cetera
Out/            ← subdirs structure below Out/ is for example
Source/
SubdirA/
SubdirB/
SubdirX/
Framework/
Source/
Subdir1/
Subdir2/
Subdir3/
Makefile
Project2/
Source/
...
Out/
...
Makefile
et cetera
The content of the Framework/Source directory is just a connection of some general purpose source files which will be used in a couple of projects. The Framework directory has no own makefile and e.g. won't build a lib. It's just, that the projects uses some of the sourcecode from the Framework directory.
When building Project1, I first cd into the related projects dir and then calling make, e.g.:
cd ~/Repository/Projects/Project1
make
And all build output has to be put into Project1/Out directory.
Below the Out directory, my makefile mirrors the directory hierarchy from the source tree(s).
Within my makefile, I do something like this:
SRCS += $(shell find Source -name "*.c")
SRCS += $(shell find ../../Framework/Source -name "*.c")
BUILDDIR := Out
OBJS = $(addprefix $(BUILDDIR)/, $(addsuffix .o, $(basename $(SRCS))))
$(BUILDDIR)/%.o : %.c
$(CC) $(CFLAGS) -c $< -o $#
and this causes the problem that during the pattern rule and the ../.. part, some of the generated object files will be placed outside of my build output directory, e.g.:
gcc -c ../../Framework/Source/*.c -o Out/../../Framework/Source/*.o
My first approach was, to force all source filenames to absolute path names like this:
OBJS = $(addprefix $(BUILDDIR)/, $(addsuffix .o, $(basename $(abspath $(SRCS)))))
This works (even with realpath), but it makes my build output quite ugly because of the long path + filename outputs.
I have a second approach, were I just substitute all ../ parts of the .o files with something like up/ and it looks like this:
OBJS = $(addprefix $(BUILDDIR)/, $(addsuffix .o, $(subst ../,up/,$(basename $(SRCS)))))
but then it unfortunately seems. that I have to duplicate all my pattern rules like:
$(BUILDDIR)/%.o : %.c
$(CC) $(CFLAGS) -c $< -o $#
$(BUILDDIR)/up/up/%.o : ../../%.c
$(CC) $(CFLAGS) -c $< -o $#
So, I'm still looking for a slightly nicer solution for this problem.
For some reasons, I have the following restrictions:
no symlinks
no recursive make calls
no use of vpath
no libs
no git submodules ...
Any suggestions for a bit more elegant approach?
Here is the solution:
COLLAPSED_SRC_PREFIXES_IN_BLD := ../../
SRCS += $(shell find Source -name "*.c")
SRCS += $(shell find ../../Framework/Source -name "*.c")
BUILDDIR := Out
.PRECIOUS: %/.
%/.:
mkdir -p $#
define BLD_FROM_TO
$2/%.o: $1%.c Makefile | $$$$(#D)/.
$(CC) $(CFLAGS) $$< $$#
endef
.SECONDEXPANSION:
$(foreach prefix, $(COLLAPSED_SRC_PREFIXES_IN_BLD), $(eval $(call BLD_FROM_TO,$(prefix), $(BUILDDIR))))
$(eval $(call BLD_FROM_TO,, $(BUILDDIR)))
My original answer is preserved below for reference, but the consensus of discussion seems to be that my answer is wrong. When you have been around long enough, as I have, there is a certain "ring of truth" you can hear in certain criticism. One just ignores criticism that lacks the ring of truth, but this criticism has that ring.
The odd thing is that I have been using recursive Make for years in my own work, in a properly refactored manner (so that I do not, in effect, keep multiple instances of more or less the same rule) with the help of include and symlinks to makefiles (and of course by letting my compiler automatically emit the header dependencies), and it all seems to work fine for me. It is not easy to set up, and there is some hassle whenever I open a new source subdirectory; but then I had believed that Make with multiple source directories just wasn't going to be easy to set up, so there I am.
I do not know whether the OP has learned what he had sought to learn today, but I seem to have learned something.
I withdraw my answer. Thank you for having read my answer and having commented on it.
ORIGINAL ANSWER
Makefiles always seem to be hard. There is no definite, unalterable rule, but there are some practices that help.
In general, a file is best made by a makefile, not in the directory of the file's prerequisites, but in the directory in which the file is to be made. Thus, you can use several makefiles, one in each directory in which files are to be made. In your example, for instance, you can put a makefile in the Out/ directory, then move the $(BUILDDIR)/%.o: rule over to that makefile.
If you do not know it already, learn Make's -C option, and start using it extensively and consistently. There is nothing wrong with your cd ~/Repository/Projects/Project1 before make, exactly, unless the reason for the cd is that you didn't think to use -C, instead. Consistent use of -C, both on the command line and within makefile rules, eventually leads you to think in a more productive way about makefiles. It gradually clarifies your perception in the matter, which is good.
Also if you do not know it already, learn the use of Make's $(MAKE), for recursive invocation of Make.
Use $(MAKE) and -C together in your rules.
How exactly you assemble these elements into a solution that works for you depends on the details of what you want to do, but the above advice will tend to keep you from fighting Make's design philosophy. Not fighting will make things easier for you. Since Make's manual explains features, but does not explain the design philosophy underlying the features very well, this is important.
One could write whole chapters in the matter, so I'll stop here; but if there is a specific point upon which you would like me to expand, feel free to let me know.

Foreach template in makefile recipe

Given the following Makefile fragment:
TOOLS=foo bar
define TOOL_install
install -c $(1) $$(prefix)/bin/$(1)
endef
.PHONY: install
install: all
$(foreach tool,$(TOOLS),$(eval $(call TOOL_install,$(tool))))
Why does make install print "Nothing to be done for `install'." instead of executing the commands specified by the foreach? (There is a tab before the foreach, and I have tried putting a tab inside the TOOL_install definition; it doesn't help.)
According to make -p the install target has no commands, which is obviously not as intended.
Elsewhere in the same makefile I've successfully used the same technique to create entire rules including command recipes; how can I make this work within an existing rule?
(If I replace eval with info then it prints the commands that I am expecting it to run.)
In this simple case I can get it to work by inlining the command:
install: all
#$(foreach tool,$(TOOLS),install -c $(tool) $(prefix)/bin/$(tool) ; )
But I would like to know how to get the first form to work as expected, in case I need something more complicated in future.
You should not use eval here, because you do not want to create dynamic constructs but constant rules. So just remove eval and add a ; at the end of your TOOL_install define:
TOOLS := foo bar
define TOOL_install
echo $(1);
endef
.PHONY: install
install:
$(foreach tool,$(TOOLS),$(call TOOL_install,$(tool)))
After expansion your Makefile will look like:
.PHONY: install
install:
echo foo; echo bar;
ANOTHER ANSWER:
We are using some temporary target install_foo and install_bar. These two targets are added to the install dependencies and declare just after. Moreover, in each temporary target we add dependency about the file to install (foo or bar). This way you can add as rules as you want, plus it's "parallel compliant".
prefix := foobar
TOOLS := foo bar
install: $(addprefix install_,$(TOOLS))
$(addprefix install_,$(TOOLS)): install_%: %
install -c $< $(prefix)/bin/$<
.PHONY: install $(addprefix install_,$(TOOLS))
EDIT:
For the uninstal targets (without dependencies) you can use the patsubst function like this:
prefix := foobar
TOOLS := foo bar
uninstall: $(addprefix uninstall_,$(TOOLS))
$(addprefix uninstall_,$(TOOLS)):
install -c $(patsubst uninstall_%,%,$#) $(prefix)/bin/$(patsubst uninstall_%,%,$#)
.PHONY: uninstall $(addprefix uninstall_,$(TOOLS))
I've ended up going with a hybrid approach from my original question and jml's answer (thanks for the inspiration!).
TOOLS:=foo bar
.PHONY: all
all : $(TOOLS)
.PHONY: install $(addprefix install_,$(TOOLS))
install : $(addprefix install_,$(TOOLS))
.PHONY: uninstall $(addprefix uninstall_,$(TOOLS))
uninstall : $(addprefix uninstall_,$(TOOLS))
define TOOL_template
$(1): $$($(1)_OBJS)
#commands to build it
install_$(1): $(1)
install -c $(1) $$(prefix)/bin/$(1)
uninstall_$(1):
rm $$(prefix)/bin/$(1)
endef
$(foreach tool,$(TOOLS),$(eval $(call TOOL_template,$(tool))))
Seems to do the trick.

Cannot use wildcard in kernel module makefile

I am pretty familiar with Makefiles and kernel modules, but recently I got a problem in my Makefile that doesn't make any sense -- on using wildcards.
To demonstrate this, I am compiling a hello world kernel module from scratch.
The directory structure is like this:
hello_mod/
|
--- hello.c
|
--- Makefile
Here is the actual makefile :
CFILES := $(wildcard hello.c*)
#CFILES := hello.c
OBJS := $(CFILES:.c=.o)
KSRC := /lib/modules/$(shell uname -r)/build
obj-m += hello_world.o
hello_world-y := $(OBJS)
all:
#echo $(CFILES)
$(MAKE) -C $(KSRC) M=$$PWD modules
clean:
$(MAKE) -C $(KSRC) M=$$PWD clean
.PHONY: clean
The problem is that even though the commented $(CFILES) and the uncommented $(CFILES) are exactly the same, the build fails on using the first $(CFILES) with the following error:
*** No rule to make target `/home/test/hello_mod/hello_world.c', needed by
/home/test/hello_mod/hello_world.o'. Stop.
If the commented $(CFILES) is used, it works perfectly.
If someone wants to test this out, I'm including the source for the hello world source which is hello.c :
#include <linux/kernel.h>
#include <linux/module.h>
static int mod_init()
{
printk("Hello\n");
return 0;
}
static void mod_exit()
{
printk("Bye world\n");
}
module_init(mod_init);
module_exit(mod_exit);
Does anyone know why it is behaving as such? And I need to use wildcards in the makefile. Any help will be appreciated.
There are two makes happening here. The first really only relies on the KSRC variable and the recursive make call. The second make only needs the CFILES, OBJS, obj-m, and hello_world-y variables, and doesn't make use of the all: target. So your debug is showing that CFILES is set correctly for the first Make, where it's not being used, and is not showing it in the second make, where it is.
You're wildcard expanding from a different directory, and not picking up the right files. Try this for CFILES:
CFILES := $(notdir $(wildcard $M/hello.c*))
SRCDIRS := subdir1 subdir2
CFILES := $(strip $(foreach dir,$(SRCDIRS),$(wildcard $(dir)/*.c)))
should probably be (see foreach example in documentation)
SRCDIRS := subdir1 subdir2
CFILES := $(foreach dir,$(SRCDIRS),$(wildcard $(dir)/*.c))
(no need to $(strip), .... or perhaps
CFILES := $(wildcard {subdir1,subdir2}/*.c)
Use remake, probably as remake -x, to debug such issues.

Makefile that distinguishes between Windows and Unix-like systems

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

Resources