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

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: $#'
...

Related

How to creat only object file without executable?

I am trying to create an object file through a make file which will be called in another script.
The following is my make file for creating an object file.
SOURCE_CK = ../SOURCES_COUNTERFLOW/
SOURCES_f77 = $(SOURCE_CK)density.f
#TARGET =
OBJECTS = $(SOURCES_f77:.f=.o)
COMPILE = f77
.f.o :
$(COMPILE) -o $*.o -c $*.f
#$(TARGET) : $(OBJECTS)
# $(COMPILE) $(OBJECTS) -o $#
#del :
# /bin/rm $(OBJECTS)
When I run the above script, the following error gets generated.
make: *** No targets. Stop.
Now I know I have to make some modification with TARGET but not sure where to start or how to modify the target.
Again, my goal is to run the script and generate density.o file.
Any help would be appreciated.
GNU Make has built-in rules for making fortran object files from sources, and fortran programs from object files, see https://www.gnu.org/software/make/manual/html_node/Catalogue-of-Rules.html
Your makefile can therefore be condensed into just the following:
$(TARGET): $(OBJECTS)
Should you want to generate just the object, you can even do that without a makefile:
# make density.o from a sourcefile
make density.o
Note the built-in rules put the object files next to the source files (unless using VPATH etc etc, more about that in the manual), so you'd call it like
make ../SOURCES_COUNTERFLOW/density.o

compiling a makefile that has an extenstion .x86 [duplicate]

I have a makefile in a directory of mine which builds scripts with certain environment variables set. What if I want to create another makefile in the same directory with different environment variables set? How should I name the two make files? Does makefile.1 and makefile.2 work? How do I call them?
You can give sensible names to the files like makefile.win and makefile.nix and use them:
make -f makefile.win
make -f makefile.nix
or have a Makefile that contains:
win:
make -f makefile.win
nix:
make -f makefile.nix
and use make win or make nix
You can name makefile whatever you want. I usually name it like somename.mk. To use it later you need to tell make what makefile you want. Use -f option for this:
make -f somename.mk
Actually you can have two set of environment variables in the same make file. for example
COMPILER = gcc
CCFLAGS1 = -g
CCFLAGS2 = -Wall
a: main.c
${COMPILER} ${CCFLAGS1} main.c
b: test.c
${COMPILER} ${CCFLAGS2} test.c
then you can just say make a or make b. Depending on what you want.
Also it is possible with -f flag to call which makefile you want to call.
You can do something like this rather than using multiple makefiles for the same purpose. You can pass the environment or set a flag to the same makefile. For eg:
ifeq ($(ENV),ENV1)
ENV_VAR = THIS
else
ENV_VAR = THAT
endif
default : test
.PHONY : test
test:
#echo $(ENV_VAR)
Then you can simply run the make command with arguments
make ENV=ENV1
I have two makefiles in the same directory. Many of the recipes have identical names and here are two solutions:
1. Prefix in make
proja_hello:
#echo "hello A"
projb_hello:
#echo "hello N"
2. Keep two separate files
Project A has makefile. Type make hello.
Project B has a separate make file called projb.mk. Type bmake hello.
This works since I've added alias bmake ='make -f projb.mk to my .bashrc. Note! This command can be called anywhere but only works where projb.mk exists.
Note! You lose autocompletion of make with the alias and typing make -f projb.mk hello is not better than typing make projb_hello.

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

Makefile isn't rebuilding dependencies?

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.

How do I create a directory in a makefile

I'm using Visual Studio 2005 nmake, and I have a test makefile like this:
sometarget:
-mkdir c:\testdir
I want to always create the directory, without having to specify 'sometarget'. For example, I can do this:
!if [if not exist c:\testdir\$(null) mkdir c:\testdir]
!endif
But that requires two lines, where I really only want to do the "-mkdir c:\testdir". If I just replace it with "-mkdir c:\testdir" I get an error from nmake - "fatal error U1034: syntax error : separator missing".
How can I always execute the mkdir, without messing about with !if [] stuff?
I think this will work:
-# if NOT EXIST "dir" mkdir "dir"
Make always wants to do things based on targets. It's not a general scripting tool. It looks at the targets and checks to see if they exist. If the target does not exist it executes the commands for that target.
The usual way to do this is to have a dummy target that is never going to be generated by the make scripts, so every time make runs it has to execute the relevant commands.
Or, you could add the command to a batch file that then calls your make file.
I'm not sure if there is an equivalent in Windows with nmake, but I managed to create a directory without using targets on Linux. I used the make function "shell". For example:
# Find where we are
TOPDIR := $(shell pwd)
# Define destination directory
ROOTFS := $(TOPDIR)/rootfs
# Make sure destination directory exists before invoking any tags
$(shell [ -d "$(ROOTFS)" ] || mkdir -p $(ROOTFS))
all:
#if [ -d "$(ROOTFS)" ]; then echo "Cool!"; else echo "Darn!"; fi
I hope Windows has the equivalent.
$(DIRNAME):
#[ -d $# ] || mkdir -p $#
Try using this:
-mkdir -p c:\testdir

Resources