Makefile isn't rebuilding dependencies? - linux

Fair warning: I'm something of a newb at using makefiles, so this may be something obvious. What I'm trying to do is to use make to run a third-party code generation tool when and only when the source files for that generation tool (call them .abc files) change. I referenced the example at http://www.cmcrossroads.com/ask-mr-make/6795-rebuilding-when-a-files-checksum-changes which shows how to build MD5s, and I tweaked the idea a bit:
File: abc.mk
target = all
files := $(wildcard Abc/*.abc)
bltfiles := $files $(addsuffix .built,$files)
all: $bltfiles
%.built: %.abc %.abc.md5
#echo "Building $*"
# #Command that generates code from a .abc file
#touch $#
%.md5: FORCE
#echo "Checking $* for changes..."
# #Command to update the .md5 file, if the sum of the .abc file is different
FORCE:
What I'm intending to happen is for each .abc file to have two auxilary files: .abc.built & .abc.md5 . The .built file is just a dummy target & timestamp for the last time it was built, as the code produced by the generation tool cannot be readily defined as a target. The .md5 file contains a hash of the last known content of the .abc file. It should only be updated when the hash of the file changes.
However, the .built file is only created if it doesn't exist. The .md5 rule never runs at all, and the .built rule doesn't re-build even if the .abc file has a newer timestamp. Am I doing something wrong?
Update:
For posterity, here's the version I got to work:
File: abc.mk
# Call this makefile as: make all --file=abc.mk
# Default Target
target = all
COMP_ABC_FILES := $(wildcard Abc/*.abc)
COMP_BLT_FILES := $(COMP_ABC_FILES) $(addsuffix .built, $(COMP_ABC_FILES) )
# This line is needed to keep make from deleting intermediary output files:
.SECONDARY:
# Targets:
.PHONY: all
all: $(COMP_BLT_FILES)
Abc/%.abc.built: Abc/%.abc Abc/%.abc.md5
#echo "Building $*"
# #Command that generates code from a .abc file
#touch $#
%.md5: FORCE
#echo "Checking $* for changes..."
#$(if $(filter-out $(shell cat $# 2>/dev/null),$(shell md5sum $*)),md5sum $* > $#)
# Empty rule to force re-build of files:
FORCE:
clean:
#echo "Cleaning .built & .md5 files..."
#rm Abc/*.built
#rm Abc/*.md5

Fixing your makefile in three places:
target = all
files := $(wildcard Abc/*.abc)
bltfiles := $(files) $(patsubst %.abc,%.built,$(files))
all: $(bltfiles)
#Abc/%.abc.built: Abc/%.abc Abc/%.abc.md5
%.built: %.abc %.abc.md5
#echo "Building $*"
# #Command that generates code from a .abc file
#touch $#
%.md5: FORCE
#echo "Checking $* for changes..."
# #Command to update the .md5 file, if the sum of the .abc file is different
FORCE:
Note the changes:
bltfiles := $(files) $(patsubst %.abc,%.built,$(files))
Results in "Abc/a.built Abc/b.built" instead of "Abc/a.abc.built Abc/b.abc.built", which was required given how the rule for %.built was defined
all: $(bltfiles)
As above, with $(files), '$bltfiles' needed to be $(bltfiles), since otherwise make will interpret this as $(f)iles and $(b)ltfiles instead.
Tip: Having an editor with syntax highlighting for makefiles is nice here
DEMO
mkdir -pv Abc; touch Abc/{a,b,c,d,e,f,g}.abc
make -Bs -f abc.mk
output like
Checking Abc/e.abc for changes...
Building Abc/e
Checking Abc/g.abc for changes...
Building Abc/g
Checking Abc/b.abc for changes...
Building Abc/b
Checking Abc/f.abc for changes...
Building Abc/f
Checking Abc/a.abc for changes...
Building Abc/a
Checking Abc/c.abc for changes...
Building Abc/c
Checking Abc/d.abc for changes...
Building Abc/d

As sehe fixed but didn't explain: Makefile syntax isn't the same as shell syntax. By default (for reasons lost to history) make variables are only one character long. If you want a longer variable name, you have to put it in parenthesis so it parses correctly. Writing $files, for example, actually expands the string "iles" because make parses and expands only the value of the "f" variable (which is empty).
Yes, it's weird. But it's the way make works. Always put your variables in parentheses.

Related

Getting a portable (gnu) Makefile operate with pattern rules on files in subdirectories matching a regexp

I would like to use this Makefile both on Debian and Windows 10.
I have a set of measurements (and keep adding more) and the raw data files go into subdirectories called "measurement-12-Apr-2020" (with the date of the day I took the measurement). On those raw data files I run a series of post processing steps, each encapsulated in its own little tool, creating new output files and building on each other. Those different output files have distinct extensions: .raw for raw data files, .rot for coordinate rotations, .off for offset removal etc. I have pattern rules in my Makefile for those steps. For a few such steps I calculate special transformation matrices, that of course match just their set of data files. Those i call e.g. "transform.calibrate" or "transform.aligne"
So far the Makefile operated in just one directory and I moved the data files manually in and out of their directories.
Now I would like my top level Makefile with its pattern rules to operate on the data in the subdirectories.
How do i get it to look at the files in directories that match the "measurement-DATE" pattern? And how do i get the pattern rules to operate on one data set, using the local transformation files?
Here is my code with a recursive makefile. I would like a non-recursive makefile without the ugly
BIN := ${CURDIR}/../bin and half-good variable usage and content when the Makefile runs in the subdirectories. Furthermore i get an python error 9009 since i switched to the subdirs and the bin-directory (which is most likely related, but an other problem, really.).
ifdef OS
RM = del /Q
FixPath = $(subst /,\,$1)
COPY = copy
PYTHON = "c:\Users\Blah\AppData\Local\Programs\Python\Python37\python.exe"
else
ifeq ($(shell uname), Linux)
RM = rm -f
FixPath = $1
endif
COPY = cp
PYTHON = python
endif
.PHONY: test default
.DEFAULT_GOAL := default
# This is for running inside PyCharm
export PATH:=${CURDIR}/venv/bin:${PATH}
# file endings:
# raw
# calibrate file ending for transformation matrix
# cal calibrated data file
# ali alignement transformation
# rot rotated, aligned sensors
# off offset removed
# pha phase and
measurement_dirs := $(filter %/, $(wildcard messung-*/))
BIN := ../bin
DIRS_CMD = $(foreach subdir, $(measurement_dirs), make-rule/$(subdir))
# Makefiles = $(foreach subdir, $(measurements_dirs), $(subdir)Makefile )
default: transform.align transform.calibrate $(wildcard messung-*.pha)
all: $(DIRS_CMD)
#echo $(BIN)
transform.calibrate: calibrationsequence.raw
$(PYTHON) $(BIN)/calculateCalibration.py $<
%.cal: transform.calibrate %.raw
$(PYTHON) $(BIN)/applyCalibration.py $*.raw
transform.align: calibrationsequence.cal transform.calibrate
$(PYTHON) $(BIN)/calulateAlignement.py calibrationsequence.cal
%.rot: transform.align %.cal
$(PYTHON) $(BIN)/applyAlignment.py $*.cal
%.off: %.rot
$(PYTHON) $(BIN)/clearOffset.py $*.rot
%.pha: %.off
$(PYTHON) $(BIN)/demodulateSignals.py $*.off
test: test_%.py
$(PYTHON) pytest -v test
make-rule/%:
cp Makefile $* && cd $* && make
The heart of the problem is that two variables are involved and unknown beforehand: the names of the directories and the names of the raw files. Make can handle one variable easily, but struggles with two.
Suppose we have one directory, messung-12-Apr-2020, with three raw files alpha.raw, beta.raw and calibrationsequence.raw.
The rule is simple:
.PHONY: 12-Apr-2020
12-Apr-2020: messung-12-Apr-2020/apha.pha messung-12-Apr-2020/beta.pha messung-12-Apr-2020/calibrationsequence.pha
#echo $# done
If we do not know the names of the raw files beforehand, we can derive them:
RAW_FILES := $(wildcard messung-12-Apr-2020/*.raw)
PHA_FILES := $(patsubst %.raw,%.pha,$(RAW_FILES))
12-Apr-2020:$(PHA_FILES)
#echo $# done
If we do not know the name of the directory beforehand, we could use subst instead of patsubst and put everything into a pattern rule:
TARGETS := 12-Apr-2020
.SECONDEXPANSION:
$(TARGETS): %: $$(subst .raw,.pha,$$(wildcard messung-%/*.raw))
#echo $# done
(Note the use of $$.) This is safe as long as there is no chance of ".raw" embedded in a file name. There is a safer, more rigorous approach, but it is more complicated; if you need it, let me know in a comment and I will post it.
Now to generate the list of targets:
TARGETS := $(patsubst messung-%,%,$(wildcard messung-*))
.PHONY: all $(TARGETS)
all: $(TARGETS)
and modify the other pattern rules to be executed from the master directory:
%.pha: %.off
cd $(dir $*); $(PYTHON) $(BIN)/demodulateSignals.py $(notdir $*).off
(Clumsy, but effective.) It should be clear how to modify the pattern rules for %.off, %.rot, %.cal, %/transform.calibrate and %/transform.align the same way.

Makefiles with source / object files in different directories

I have a project with the following directory structure:
$tests/ $doc/
| |-makefile
+------+-------+ |
| | | tests/
test1/ test2/ test3/ |
| | | test1.rst, test2.rst, test3.rst
test1.e test2.e test3.e
A file in the $doc/tests directory e.g test1.rst is created from $tests/test1/test1.e. I'm having problems with the makefile specifying that the source files are in $tests/*/*.e and the destination files are in $doc/tests/*.rst.
I've seen several similar questions but haven't been able to workout the correct makefile syntax.
This makefile works for a single file example
SOURCES = $(wildcard $(tests)/*/*.e)
OBJECTS = $(addprefix $(doc)/tests/,$(notdir $(SOURCES:.e=.rst)))
# single file trial
SRC = $(tests)/test1/test1.e
OBJ = $(doc)/tests/test1.rst
$(OBJ): $(SRC)
debugvars:
#echo SOURCES=$(SOURCES)
#echo OBJECTS=$(OBJECTS)
# define how to create any RST file from a testcase
%.rst:
$(scripts)/wr_rst.py --infile $<
# define how to create an RST file from a testcase
%.rst: %.e
$(scripts)/wr_rst.py --infile $<
.e.rst:
$(scripts)/wr_rst.py --infile $<
.SUFFIXES: .e .rst
I'm having trouble when using the full list of objects i.e.
all: $(OBJECTS)
$(OBJECTS): $(SOURCES)
$(scripts)/wr_rst.py --infile $<
test1.rst is generated 3 times and test2,3.rst are ignored. The $(SOURCES) and $(OBJECTS) are correct. I suspect that $< does not iterate over the (SOURCES)
Here is some of the output from make -d
No implicit rule found for `$tests/test3/test3.e'.
Finished prerequisites of target file `$tests/test3/test3.e'.
No need to remake target `$tests/test3/test3.e'.
Considering target file `tests/test3.rst'.
File `tests/test3.rst' does not exist.
Pruning file `$tests/test1/test1.e'.
Pruning file `$tests/test2/test2.e'.
Pruning file `$tests/test3/test3.e'.
Finished prerequisites of target file `tests/test3.rst'.
Must remake target `tests/test3.rst'.
$scripts/wr_rst.py --inile $tests/test1/test1.e
Putting child 0x00ee6420 (tests/test3.rst) PID 11720 on the chain.
Live child 0x00ee6420 (tests/test3.rst) PID 11720
Writing RST file $doc/tests/test1.rst
Reaping winning child 0x00ee6420 PID 11720
Removing child 0x00ee6420 PID 11720 from chain.
Successfully remade target file `tests/test3.rst'.
(This question looks very familiar-- I'd almost swear that one essentially the same has been asked and answered.)
Let's take this in stages. We could write the rules one at a time:
$(doc)/tests/test1.rst: $(tests)/test1/test1.e
...
but that's tedious. It's the kind of situation that cries out for a wildcard solution, such as a pattern rule, but one of Make's serious shortcomings is its crude handling of wildcards. A pattern rule in which the wildcard is repeated:
$(doc)/tests/%.rst: $(tests)/%/%.e
...
is not allowed. But we could write the rules using eval:
define template
$(doc)/tests/$(1).rst: $(tests)/$(1)/$(1).e
use some tool to build $$# from $$<
endef
$(eval $(call template,test1))
$(eval $(call template,test2))
...
Then instead of writing all of those eval statements, we can delegate that job to foreach:
TESTS := test1 test2 ...
$(foreach TEST,$(TESTS),$(eval $(call template,$(TEST)))
Then instead of writing that list of tests, we can delegate that to wildcard, and use the same list to construct a list of target files:
TESTS := $(notdir $(wildcard $(tests)/*))
TARGETS := $(patsubst %,$(doc)/tests/%.rst,$(TESTS))
all: $(TARGETS)
Putting all of these together is straightforward, but this answer is getting long.

How to run Linux shell commands from GNU make file to setup preconditions for a build target

My issue is that in my make file I want to create the directory for all object code before building it and I cannot find a way to do this whithout having it impact the output of make. Also, i want to do it in ONE place and not for each and every object file.
I have tried this:
build: setup exe.i686
setup:
mkdir -p obj
exe.i686: $(OBJS)
#echo 'Building target: $#'
#echo 'Invoking: GCC C++ Linker'
${GCC_LOCATION}/Linux/i686/bin/i686-pc-linux-gnu-g++ $(OBJS) -o "exe.i686" -L...
#echo 'Finished building target: $#'
#echo ' '
where OBJS is all my object code. If I don't run setup first and there is no ./obj directory present the build will fail because of the missing directory when trying to build the object code.
Just to be clear, here is an example of how one object file is built:
./obj/Cell.o: ${CELL_ROOT}/main/cell/Cell/src/Cell.cc
#echo 'Building file: $<'
#echo 'Invoking: GCC C++ Compiler'
$(GCC_C++_BUILD) -MF"$(#:%.o=%.d)" -MT"$(#:%.o=%.d)" -o "$#" "$<"
#echo 'Finished building: $<'
#echo ' '
I don't want to build the object code in the same directory I have my make file.
Now to the actual problem. When I build tarket "build" the first time like this everything works fine:
make build
But if I build it again and there are no updates in my source code (object code is up-to-date) it behaves like this:
[7:37] [user1] [/local/repo/user1/project_x/test/bin] $ make build
mkdir -p obj
[7:37] [user1] [/local/repo/user1/project_x/test/bin] $
In this situation I would have liked make to return the nothing-to-be-done string like this:
[7:37] [user1] [/local/repo/user1/project_x/test/bin] $ make build
make: Nothing to be done for `build'.
[7:41] [user1] [/local/repo/user1/project_x/test/bin] $
This would have been the case if I didn't have to depend on the setup target (which is added to .PHONY). Is the only alternative to do it for each object code file? That would be possible I suppose but I wanted to avoid that if possible as it clutters the make file quite a bit. At least in my opinion.
Thanks
Your target setup is always executed because it has no dependency.
You have two choices:
(1) Make a stamp or use some other mechanism so that there is an actual dependency. The directory itself can be the target but that may incur some wrinkles: see http://www.gnu.org/software/make/manual/html_node/Prerequisite-Types.html for an example.
Modified relevant extract from the linked article:
$(OBJS): | obj
obj:
mkdir $(OBJDIR)
(2) Suppress the output by testing for existence:
setup:
test -d obj || mkdir -p obj
This is similar to the earlier answer, just to make it easier to read:
build: exe.i686
$(OBJS): | obj
obj:
#if [ ! -d obj ]; then mkdir -p obj; fi
exe.i686: $(OBJS)
#echo 'Building target: $#'
...

Make ignores the rule when run for the first time

SO
I can't find out why these lines are not called for the first time I run 'make' but are called the next time:
sb_path = sb
sb_src := $(sb_path)/src
sb_build := $(sb_path)/build
ifndef DO_NOT_GENERATE_COMMIT_INFO
commit_sb: | $(sb_bin)
#$(sb_build)/generate-commit-info $(sb_path)
$(sb_src)/last_git_commit_info.h: | commit_sb ;
endif
I'm just curious because there is no file generate-commit-info file and make crashes when I call it for the second time, but it compiles my program ok for the first try.
I use script on my local machine to copy sources over ssh to another machine and to run compile.sh script there:
...
scp -r $sbfolder/build $sbfolder/Makefile "$buildserver:$root/$curdate"
check_retcode
scp -r $sbfolder/sb/Makefile "$buildserver:$root/$curdate/sb/"
...
ssh $buildserver "$root/compile.sh $curdate $debug"
compile.sh:
# fix Makefile: we don't have git installed here
#DO_NOT_GENERATE_COMMIT_INFO=true
#now we can compile sb
curdir="/home/tmp/kamyshev/sb_new/$1"
cd $curdir
check_retcode
t_path=$curdir
debug=$2
config=RELEASE
if [[ debug -eq 1 ]]; then
config=DEBUG
fi
echo "building sb... CONFIG=$config"
make -j2 CONFIG=$config
check_retcode
As you see DO_NOT_GENERATE_COMMIT_INFO=true is commented out. So I just don't see a reason why the code is not run when I call a make or the script for the first time (either from the remote script or myselft from command line).
Do you have any clues?
UPDATE on Etan Reisner comment:
commit_sb target is checked, it does not exist, so it's rule is being run and it updates last_git_commit_info.h. Thus it forces to update the .h file. It also gives me a .PHONY target commit_sb so I could do it directly by calling make commit_sb.
The generate-commit-info also creates a file in a $(sb_bin) folder.
My another guess is that you are talking about a better way to organize this code.
I can update last_git_commit_info.h directly with a such rule:
commit_sb $(sb_src)/last_git_commit_info.h: FORCE | $(sb_bin)
#$(sb_build)/generate-commit-info $(sb_path)
FORCE:
Thanks to the commenters on my question I've done some additional research: I've tried to make a minimal complete example. And this led me to the answer.
My code generates dependency files (look at -MMD command in SB_CXXFLAGS):
# just example - in real Makefile these are calculated on the fly
sb_deps := file1.d file2.d [...]
# rules with dependances of .o files against .h files
-include $(sb_deps)
SB_CXXFLAGS = $(CXXFLAGS) [...] -MMD
# compile and generate dependency info;
$(sb_obj)/%.o:$(sb_src)/%.cpp
$(CXX) $(SB_CXXFLAGS) $< -o $#
And when I run make for the first time there no *.d files, so no *.cpp depends on last_git_commit_info.h file and the rule is not applied.
On the subsequent runs the dependency rule appears in one of *.d files, the rule is executed and I get the error.
UPDATE: This does not concern the question directly, but this is the better way to write these rules:
ifndef DO_NOT_GENERATE_COMMIT_INFO
commit_sb $(sb_src)/last_git_commit_info.h: FORCE | $(sb_bin)
#$(sb_build)/generate-commit-info $(sb_path)
FORCE:
endif

Header file not found during compilation with make (Linux)

I am getting the following error during make:
In file included from modGPRS.c:25:
../inc/intModGPRS.h:33:21: error: datapkt.h: No such file or directory
The directory structure is as follows:
datapkt.h is placed at main_proj/libs/ipsec/itf.
libs folder has a Makefile which is as follows:
include $(BUILD_TOP)/build_control/stream/config.mk
SUBDIRS = tracing config stats srandom
SUBDIRS_$(B_HAS_EEPROM) += eeprom
SUBDIRS_$(B_LIBARCHIVE) += libarchive
ifeq ($(B_PLATFORM_openat),y)
SUBDIRS += openat openssl asn1c mib zlib diff ipsec
else
SUBDIRS += iniparser daemon ipsec
endif
stats_needs = config
include $(BUILD_TOP)/build_control/stream/subdirs.mk
Makefile for ipsec is as follows:
SDIRS += src itf
TOP?=..
RECURSIVE_MAKE= [ -n "$(SDIRS)" ] && for i in $(SDIRS) ; do \
(cd $$i && echo "making $$target in $(DIR)/$$i..." && \
$(MAKE) -e TOP=$(TOP)/.. $$target INCLUDES='$(INCLUDES)') || exit 1; \
done;
subdirs:
#target=all; $(RECURSIVE_MAKE)
all: subdirs -I /itf
include $(BUILD_TOP)/build_control/stream/subdirs.mk
Also subdirs.mk is as follows:
# This is a stand-alone file to distribute targets to
# sub-directories. Include this file alone.
# Make bug: It loses track of if/endif over a $(eval) call.
ifndef __subdirs_include
__subdirs_include = yes
# This only works for the standard targets defined in $(TARGETS)
# in utils.mk. However this list can be extended with +=.
install:: install-hdrs
include $(BUILD_TOP)/build_control/stream/utils.mk
include $(BUILD_TOP)/build_control/stream/platform.mk
# This creates a recursion rule for each pair of target and subdirectory.
# The target for doing T in directory D is named T+D.
# If there is a "$(D_needs) = subdirs" then the subdirs become prerequisites
# of D.
define __target_subdir
.PHONY:: $(1)+$(2)
$(1)+$(2) :: $(3); +$(MAKE) -C $(2) $(1)
endef
$(foreach t,$(TARGETS),\
$(foreach d,$(strip $(SUBDIRS) $(SUBDIRS_y)),\
$(eval $(call __target_subdir,$(t),$(d),$(patsubst %,$(t)+%,$($(d)_needs))))))
$(foreach t,$(TARGETS),\
$(foreach d,$(strip $(SUBDIRS) $(SUBDIRS_y)),$(eval $(t)::$(t)+$(d))))
endif # __subdirs_include
could someone please help me figure out, how to solve this issue of the header file: datapkt.h? Please let me know if any other details are required.
Thanks
Sunny
The error message is from your compiler. It is saying that the file modGPRS.c (in whatever directory it is in, call it A) has included a file A/../inc/intModGPRS.h, which is itself trying to include a file called datapkt.h, which cannot be found. This is either because your makefiles are not properly telling the compiler where the file datapkt.h is, or (more likely) you have not installed a prerequisite library.
Make sure you have installed everything that needs to be installed before trying to build whatever it is that you're trying to build. Without at least telling me what it is you're trying to install I can't help you further.

Resources