Execute object files from different directories using Makefile - makefile

I have the following Makefile entry:
TEST_DIRS = abcd pqr xyz
test_lib :
for dir in $(TEST_DIRS); do \
$(MAKE) -C $$dir; \
done
run :
./abcd/test/abcd_test.o --log_level=message
./pqr/test/pqr_test.o --log_level=message
./xyz/test/xyz_parser_test.o --log_level=message
test : test_lib run
I don't want to write 3 separate commands for run target instead make it generic. So that everytime a new test file gets added, I dont want add a new command under run target. Can somebody help me?

TESTS := $(addprefix TEST_, $(TEST_DIRS))
run: $(TESTS)
TEST_%:
./$*/test/$*_test.o --log_level=message

Assuming the naming scheme is consistent (and those are just oddly named binaries and not actually object files) then something like this should work:
Using a shell loop:
run :
for name in $(TEST_DIRS); do \
./$$name/test/$${name}_test.o --log_level=message; \
done
Using make foreach:
run :
$(foreach name,$(TEST_DIRS),./$(name)/test/$(name)_test.o --log_level=message;)

Related

Using Makefile to selectively simulate a test

I would like a Makefile variable to hold a list of tests but run each test with a different flag and have the ability to run all tests or selectively one from the command line. Here's what I have so far and I am not sure how to add more tests but prevent creating individual variables for each test. In the example below I can say make all or make run_tests TESTS=block_test_A to either run all tests or a specific test. This works but if I were to add another 10 tests I would like to modify only TESTS string and add them there instead of adding TEST4, TEST5 and so on. Is there a clever way to make a loop that can parse the list of tests in TESTS and create the variables. I tried a couple of variations but couldnt get it to work. sim_design is target not shown that will execute the compile/sim using the values in the Makefile variables SIM_FLAGS and SIM_LOG. Thanks.
TESTS := block_test_A block_test_B block_test_C
TEST1 := $(word 1, $(TESTS))
$(TEST1) : SIM_FLAGS:= "$(SIM_FLAGS) -top $(TEST1)"
$(TEST1) : SIM_LOG:= "logs/$(TEST1).log"
TEST2 := $(word 2, $(TESTS))
$(TEST2) : SIM_FLAGS:= "$(SIM_FLAGS) -top $(TEST2)"
$(TEST2) : SIM_LOG:= "logs/$(TEST2).log"
TEST3 := $(word 3, $(TESTS))
$(TEST3) : SIM_FLAGS:= "$(SIM_FLAGS) -top $(TEST3)"
$(TEST3) : SIM_LOG:= "logs/$(TEST3).log"
$(TESTS):
$(shell mkdir -p logs)
#(echo Running TEST $# with SIM_FLAGS=$(SIM_FLAGS) SIM_LOG=$(SIM_LOG))
$(MAKE) sim_design SIM_FLAGS=$(SIM_FLAGS) SIM_LOG=$(SIM_LOG)
run_tests: $(TESTS)
all:
#(echo Running Regression on $(DESIGN_TOP) with following list of tests $(TESTS))
#(echo To run a single test generating a dump use: make run_tests TESTS=\"valid_test_name\" REGRESS=0)
$(MAKE) run_tests REGRESS=1
This seems like exactly what automatic variables were created for...?
What's wrong with this:
TESTS := block_test_A block_test_B block_test_C
SIM_FLAGS += -top $#
SIM_LOG = logs/$#.log
$(TESTS):
#mkdir -p logs
#echo Running TEST $# with SIM_FLAGS=$(SIM_FLAGS) SIM_LOG=$(SIM_LOG)
$(MAKE) sim_design SIM_FLAGS="$(SIM_FLAGS)" SIM_LOG="$(SIM_LOG)"
Other things:
You never want to use the make shell function inside a recipe.
I don't really see the point in putting echo into a subshell with (...)

Define target name in other recipe

It may be ugly but I'd like to use a macro in a target name which gets a value assigned in another recipe. I know how to define macros/variables in other recipes with the eval function like this:
read :
$(eval TEXT := $(shell cat somefile.txt))
say : read
echo ${TEXT}
It would result in:
$ make say
what ever's in this file
That's fine. Also one can use Macros to define target names. Now let's say I have a file, for example a tar ball, the name of which always starts with 'program-' and ends whith '.bar' and in between is the version which is located in a separate file, e.g. 'program-1.2.3.tar' . So in principle I could define the target for this file with:
VERSION = $(shell cat version.txt)
program-${VERSION}.tar :
tar cf program-${VERSION}.tar program-${VERSION}
Alright, this also works fine. But actually I want to read this file only in a target, e.g.:
.PHONY : getversion
getversion :
$(eval VERSION := $(shell cat version.txt))
program-${VERSION}.tar : gerversion
tar cf program-${VERSION}.tar program-${VERSION}
but of course this doesn't work since make already determines the target name by invoking make in the first place. Is there some way to solve this weird problem without having an extra makefile?
I would suggest you to evaluate VERSION and TAR_VERSION outside out the targets. To set TAR_VERSION as the target name, you should also add it to .PHONY target. The whole thing can be like this,
.PHONY : getversion $(TAR_VERSION)
VERSION := $(shell cat version.txt)
TAR_VERSION := $(addprefix program-, $(addsuffix .tar, ${VERSION}))
getversion :
echo ${VERSION}
$(TAR_VERSION) :
tar cf ${TAR_VERSION} program-${VERSION}
I don't know if I understood your problem in all nuances, but I think you are seeing this more complicated than it really is. As far as your description can be understood, you have the following makefile, which you use to make this and make that, where the former is a target which is not dependant on the program-x.y.z.tar and only the latter asks over a slow connection which version you are actually dealing with:
.PHONY : this that getversion
this:
#echo etc....
that: program-${VERSION}.tar
#echo done with program-${VERSION}
getversion :
$(eval VERSION := $(shell cat version.txt))
program-${VERSION}.tar : getversion
tar cf program-${VERSION}.tar program-${VERSION}
This however is your dependencies misunderstood, if I may say this. The build of that is never dependant on the specific version, only on the pure act of accessing version.txt. You also couldn't sensibly build one specific program-${VERSION}.tar (by passing it as the target on the command line) as you aren't able to specifiy which version, because this piece of information comes from the network. All this hints very strongly to the conclusion, that there isn't a dependency involved between that and program-${VERSION} which simplifies the makefile to:
.PHONY : this that getversion
this:
#echo etc....
that: getversion
#echo done with program-${VERSION}
getversion:
$(eval VERSION := $(file < version.txt))
tar cf program-${VERSION}.tar program-${VERSION}

Create directory only once when running makefile in parallel

I'm using make to write a pipeline for biological data analysis. My project directory is:
PROJECT
- DATA
- SAMPLEA
- A1.FASTQ A2.FASTQ
- SAMPLEB
- B1.FASTQ B2.FASTQ
- RESULTS
- SRC
- makefile
My current makefile uses a wildcard to list the directory of all .FASTQ files in the DATA directory. Using pattern rules each .FASTQ file then goes through a series of recipes with the final output file written to the RESULTS directory. Instead, I would like to create a directory for each SAMPLE where the final output file is written:
PROJECT/RESULTS/SAMPLEA/A1.out
PROJECT/RESULTS/SAMPLEA/A2.out
PROJECT/RESULTS/SAMPLEB/B1.out
PROJECT/RESULTS/SAMPLEB/B2.out
I can do this by having the first recipe make the directory, however this throws an error when the second of the FASTQ files from the same SAMPLE also tries to create the directory. A few posts on stack overflow suggest using the -p flag on mkdir to ignore errors, however this apparently causes problems when I run the makefile in parallel using the -j flag. I thought about forcing a shell script at the start of the makefile to run, to check if the results directories are present, and if not then it should create them, but I'd like to try and solve this issue using just make.
Create directory before executing rule.
DATADIR := $(shell cd DATA; find * -type d)
create_results_dir:= $(shell for i in $(DATADIR); \
do test -d DATA/$$i && mkdir -p RESULTS/$$i; \
done)
all:
#echo do something.

Make: why do I get error saying "no such file or directory"

When I tried my makefile, I got error saying that No such file or directory, but my directory is right there, what do I do wrong? Thanks.
my project structure :
dev |--- ev
|--- display
|--- install ( makefile is here, try to call makefiles in ev and display folder)
My makefile :
MODULES :=ev display
SRC_DIR :=$(addprefix ../, $(MODULES))
BUILD_DIR:=$(addsuffix /build, $(SRC_DIR))
x:=../ev ------> add temporarily just for test,
------> the same error if x:=$(HOME)/dev/ev
INSTALL_DIR:=EX Frameworks Add-ons
INSTALL_DIR:=$(addprefix $(HOME)/EX/, $(INSTALL_DIR))
vpath %.cpp %.java $(SRC_DIR)
.PHONY: all clean
checkdirs: $(INSTALL_DIR)
$(INSTALL_DIR):
#echo "INSTALL DIR"
#mkdir -p $#
define make-goal
$1:
#echo "start building each part"
cd $# && make -f Makefile_new.osx clean
cd $# && make -f Makefile_new.osx package
endef
clean:
#echo "clean up"
#echo "BUILD_DIR IS $(BUILD_DIR)"
#rm -rf $(BUILD_DIR)
all:
#echo "start build subdirectory"
#echo "SRC_DIR IS $(SRC_DIR)"
#echo "x is $(x)"
$(call make-goal, $(x))) ----> when it comes to here, I got error message
The error messages:
x is ../ev
../x:
make: ../ev:: No such file or directory.
I guess it is about relative path, because I call this makefile from Install folder, then $(x) can't be found from Install folder, but when I tried to make a folder named ev (Install/ev), I still got the same error.
I think it must be something basic I missed here, but what it is.
Thanks.
Update:
I am trying to build a project which includes several sub-projects. the structure is:
dev |---- ev
|---- edf
|----- dt
|------af
|------Install
Inside of Install, I have a makefile, which is at the top level. The makefile in Install folder will call makefiles in other folders to build different subjects,
Ideally, I want to build every sub projects without touching sources. My sources include c++ and java code.
It's not clear what you're trying to do. Also due to some indentation hiccups I can't be sure, but you appear to be defining a variable make-goal that contains a template for a make rule, then using it with $(call ...) inside the recipe for the all target. That cannot work: you cannot create a make rule inside the recipe for another make rule!
The reason this fails is that the $(call ...) is expanding to content which is added to the recipe of the all target, so instead of creating a new make rule it's treating the result as a shell script. The first line is $1:, and you passed in ../ev, so make is trying to run the command ../ev: just as the error shows you.
If you describe what you want to do at a higher level we can give you some ideas on how to do it.
ETA:
If you just want your all target to also build a subdirectory, there's no need for all this complex GNU make advanced capabilities. That stuff is only needed when you get to guru-level makefile creation. Simple "build a target after another target is finished" is the exact thing make was designed to do: nothing special is needed to do that.
For example:
.PHONY: all $(SRC_DIR)
all: $(SRC_DIR)
$(SRC_DIR):
#echo "start building $#"
cd $# && $(MAKE) -f Makefile_new.osx clean
cd $# && $(MAKE) -f Makefile_new.osx package
This is still a pretty non-standard setup but I think it will work the way you want. Remember you'll have to either move the all target up to be the first one in the makefile, or you'll have to run make all explicitly: make only builds the first target in the makefile unless you give it specific targets on the command line.

Using make to build several binaries

I want to create a Makefile (in a parent dir) to call several other Makefiles (in sub dirs) such that I can build several binaries (one per project sub dir) by invoking just the one parent Makefile.
My research has been hampered by finding loads of stuff on recursive Makefiles, but I think this is where you are trying to build several directories Makefiles into a single binary?
Maybe what I want to do is better handled by a shell script perhaps invoking make in each sub directory in turn, but I thought a Makefile might be a more elegant solution?
any pointers gratefully received
PS using linux and the GNU tool chain
The for loop solution given in the first answer above actually shouldn't be used, as-is. In that method, if one of your sub-makes fails the build will not fail (as it should) but continue on with the other directories. Not only that, but the final result of the build will be whatever the exit code of the last subdirectory make was, so if that succeeded the build succeeds even if some other subdirectory failed. Not good!!
You could fix it by doing something like this:
all:
#for dir in $(SUBDIRS); \
do \
$(MAKE) -C $${dir} $# || exit $$?; \
done
However now you have the opposite problem: if you run "make -k" (continue even if there are errors) then this won't be obeyed in this situation. It'll still exit on failure.
An additional issue with both of the above methods is that they serialize the building of all subdirectories, so if you enable parallel builds (with make's -j option) that will only happen within a single subdirectory, instead of across all subdirectories.
Eregrith and sinsedrix have solutions that are closer to what you want, although FYI you should never, ever use "make" when you are invoking a recursive make invocation. As in johfel's example you should ALWAYS use $(MAKE).
Something like this is what you want:
SUBDIRS = subdir1 subdir1 subdir3 ...
all: $(addprefix all.,$(SUBDIRS))
all.%:
# $(MAKE) -C '$*' '$(basename $#)'
.PHONY: $(addprefix all.,$(SUBDIRS))
And of course you can add more stanzas like this for other targets such as "install" or whatever. There are even more fancy ways to handle building subdirectories with any generic target, but this requires a bit more detail.
If you want to support parallel builds you may need to declare dependencies at this level to avoid parallel builds of directories which depend on each other. For example in the above if you cannot build subdir3 until after both subdir1 and subdir2 are finished (but it's OK for subdir1 and subdir2 to build in parallel) then you can add something like this to your makefile:
all.subdir3 : all.subdir1 all.subdir2
You can call targets in subdirectory makefiles via
all:
$(MAKE) -C subdirectory1 $#
$(MAKE) -C subdirectory2 $#
...
or better
SUBDIRS=subd1 subd2 subd3
all:
#for dir in $(SUBDIRS); \
do \
$(MAKE) -C $${dir} $#; \
done
you should indeed use cmake to generate the Makefile automatically from a given CMakeLists.txt configuration file.
Here's a random link to get you started. Here you can find a simple sample project, including multiple subdirectories, executables, and a shared library.
Each makefile can have several target, it's still true with recursive makefiles, usually it's written:
all: target1 target2 target3
target1 :
make -C subdir
Then make all

Resources