Consider this simple makefile that does very little:
define template
copy := $(1)
$(info $(1) $(copy))
.PHONY : rule_$(1)
rule_$(1) : VAR := $(1)
rule_$(1) :
#echo $# $(VAR)
all : rule_$(1)
endef
$(foreach mode,DEBUG OPT, \
$(eval $(call template,$(mode))))
.PHONY : all
.DEFAULT_GOAL := all
I thought that this would create two rules, rule_DEBUG and rule_OPT, whose recipes would echo their names and arg. Furthermore, I thought that the info line would just print DEBUG DEBUG and then OPT OPT. However, I am wrong on both accounts. The info lines log:
DEBUG
OPT DEBUG
And when I run make, I get back nothing but empty lines. If I run make -p, I do see this for rule_OPT:
rule_OPT:
# Phony target (prerequisite of .PHONY).
# Implicit rule search has not been done.
# Implicit/static pattern stem: `'
# File does not exist.
# File has been updated.
# Successfully updated.
# automatic
# # := rule_OPT
# makefile (from `makefile', line 10)
# VAR := OPT
# automatic
# % :=
# automatic
# * :=
# automatic
# + :=
# automatic
# | :=
# automatic
# < :=
# automatic
# ^ :=
# automatic
# ? :=
# variable set hash-table stats:
# Load=9/32=28%, Rehash=0, Collisions=1/12=8%
# recipe to execute (from `makefile', line 10):
#echo
Looks like # and VAR have the values I want and expect - but why doesn't it echo correctly? It looks like if I have the recipe:
rule_$(1) :
#echo $$# $$(VAR)
That echos what I want, but I am still not sure how to info the copy. Why?
The $(info) output issue is what I explained in the comments on this answer of mine. You have an evaluative order problem. The assignments in the define aren't happening until the define expansion is processed by eval so your assigned variable value isn't visible until later. You need extra eval's or post-eval expansion to do that.
Either
$(eval copy := $(1))
in the template or splitting up the contents into multiple defines.
The problem with #echo $(VAR) is the same thing. That is being expanded by the eval of the define's expansion and $(VAR) is unset at that point so you need $$(VAR) in the template to have a literal $(VAR) in the expanded recipe so that it works correctly and recipe execution time.
Related
I'm trying to build a project with subdirectories using make, I've gotten the recursive make part working but for some reason, it seems to take the prerequisites of the source file dependencies and appends .o to them and then tries to compile them, which doesn't work obviously, why is it doing this?
The rule in question looks like this:
operations_cache.o : memory_management/operations_cache.cpp memory_management/operations_cache.hpp \
function.hpp operations/operation_base.hpp
cd memory_management && $(MAKE) $#
The rule that compiles the operations directory looks like this:
operation_%.o : function.hpp
cd operations && $(MAKE) $#
for some reason, make keeps trying to say that operations/operation_base.hpp.o is a valid target even though I don't have it listed anywhere in the make file. I've read the documentation for and I didn't see anything in it about trying to implicitly define objects based on prerequisite filenames, so I'm super confused as to what is compelling it to do this.
The error I'm getting is this:
g++ -c -o main.o main.cpp
g++ -c -o node.o node.cpp
cd memory_management && make unique_table.o
make[1]: Entering directory './memory_management'
g++ -o ../unique_table.o -c unique_table.cpp
make[1]: Leaving directory './memory_management'
cd operations && make operations/operation_base.hpp.o
make[1]: Entering directory './operations'
g++ -o ../operations/operation_base.hpp.o -c
g++: fatal error: no input files
compilation terminated.
Makefile:10: recipe for target 'operations/operation_base.hpp.o' failed
make[1]: *** [operations/operation_base.hpp.o] Error 1
make[1]: Leaving directory './operations'
Makefile:24: recipe for target 'operations/operation_base.hpp.o' failed
make: *** [operations/operation_base.hpp.o] Error 2
Edit Added complete files
At user request, here are the complete make files of my 3 directories
./Makefile
CC = g++
objects = main.o node.o unique_table.o operations_cache.o function.o operation_base.o operation_and.o
# shared_lib = nodelib.so
all : edit # $(shared_lib)
edit : $(objects)
$(CC) -o edit $(objects)
main.o : main.cpp node.hpp memory_management/unique_table.hpp memory_management/operations_cache.hpp
node.o : node.cpp node.hpp
unique_table.o : memory_management/unique_table.cpp memory_management/unique_table.hpp node.hpp
cd memory_management && $(MAKE) $#
operations_cache.o : memory_management/operations_cache.cpp memory_management/operations_cache.hpp \
function.hpp operations/operation_base.hpp
cd memory_management && $(MAKE) $#
function.o : function.cpp function.hpp node.hpp memory_management/unique_table.hpp
operation_%.o : function.hpp
cd operations && $(MAKE) $#
.PHONY : clean
clean :
rm edit $(objects)
./operations/Makefile
CC = g++
objects = operation_base.o operation_and.o operation_or.o operation_xor.o operation_restrict.o operation_composition.o operation_satisfy.o operation_satisfy_all.o
proj_dir = ../
operation_base.o : operation_base.cpp operation_base.hpp $(proj_dir)function.hpp
operation_and.o : operation_and.cpp operation_and.hpp operation_base.hpp $(proj_dir)function.hpp
operation_or.o : operation_or.cpp operation_or.hpp operation_base.hpp $(proj_dir)function.hpp
operation_xor.o : operation_xor.cpp operation_xor.hpp operation_base.hpp $(proj_dir)function.hpp
operation_composition.o : operation_composition.cpp operation_composition.hpp operation_base.hpp $(proj_dir)function.hpp
operation_restrict.o : operation_restrict.cpp operation_restrict.hpp operation_base.hpp $(proj_dir)function.hpp
operation_satisfy.o : operation_satisfy.cpp operation_satisfy.hpp operation_base.hpp $(proj_dir)function.hpp
operation_satisfy_all.o : operation_satisfy_all.cpp operation_satisfy_all.hpp operation_satisfy.hpp
%.o :
$(CC) -o $(proj_dir)$# -c $<
./memory_management/Makefile
CC = g++
objects = operations_cache.o unique_table.o
proj_dir = ../
operations_cache.o : operations_cache.cpp operations_cache.hpp \
$(proj_dir)function.hpp $(proj_dir)operations/operation_base.hpp
unique_table.o : unique_table.cpp unique_table.hpp $(proj_dir)node.hpp
%.o :
$(CC) -o $(proj_dir)$# -c $<
Edit I found a solution
Removing memory_management/operations_cache.cpp memory_management/operations_cache.hpp function.hpp operations/operation_base.hpp from the operations_cache.o line solves the problem, it doesn't explain why the error was there, but it works, I'd still be interested in understanding why that happened though.
That's because your operations/operation_base.hpp happens to match operation_%.o rule and due to the fact of built-in rule:
.o:
# Builtin rule
# recipe to execute (built-in):
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $#
It works this way:
check if operations/operation_base.hpp can be updated with an explicit rule (no),
check all implicit (pattern) rules whether they can be resolved (candidate: built-in %: %.o),
check if prerequisite operations/operation_base.hpp.o can be resolved (candidate: operation_%.o: functions.hpp),
check if prerequisite functions.hpp can be resolved (yes, file exists),
follow the rule operation_%.o: functions.hpp with stem operations/base.hpp (execute recipe that fails).
I have prepared a simplified Makefile that reproduces the issue. It requires that both functions.hpp exist and built-in rules are enabled.
all: operations/operation_base.hpp
operation_%.o: functions.hpp
echo Making $# from $<
Output:
# Only Makefile (no functions.hpp)
$ ls
Makefile
$ make
make: *** No rule to make target 'operations/operation_base.hpp', needed by 'all'. Stop.
# Create functions.hpp to fulfill pattern rule (this is the case in question)
$ touch functions.hpp
$ make
echo Making operations/operation_base.hpp.o from functions.hpp
Making operations/operation_base.hpp.o from functions.hpp
# The following output comes from built-in rule %: %.o
cc operations/operation_base.hpp.o -o operations/operation_base.hpp
make: cc: Command not found
<builtin>: recipe for target 'operations/operation_base.hpp' failed
# The same, but with disabled built-in rules
$ make -r
make: *** No rule to make target 'operations/operation_base.hpp', needed by 'all'. Stop.
Generally when you don't know why make decided to do something, it's useful to run it with -d to debug decision making and -p to get final resolved Makefile. It confirms the scenario:
$ make -d
…
No implicit rule found for 'all'.
Considering target file 'operations/operation_base.hpp'.
File 'operations/operation_base.hpp' does not exist.
Looking for an implicit rule for 'operations/operation_base.hpp'.
…
Trying implicit prerequisite 'operations/operation_base.hpp.o'.
Looking for a rule with intermediate file 'operations/operation_base.hpp.o'.
Avoiding implicit rule recursion.
Trying pattern rule with stem 'base.hpp'.
Trying rule prerequisite 'functions.hpp'.
Found an implicit rule for 'operations/operation_base.hpp'.
…
No need to remake target 'functions.hpp'.
Considering target file 'operations/operation_base.hpp.o'.
File 'operations/operation_base.hpp.o' does not exist.
Pruning file 'functions.hpp'.
Finished prerequisites of target file 'operations/operation_base.hpp.o'.
Must remake target 'operations/operation_base.hpp.o'.
echo Making operations/operation_base.hpp.o from functions.hpp
…
Successfully remade target file 'operations/operation_base.hpp.o'.
Finished prerequisites of target file 'operations/operation_base.hpp'.
Must remake target 'operations/operation_base.hpp'.
cc operations/operation_base.hpp.o -o operations/operation_base.hpp
…
make: cc: Command not found
…
<builtin>: recipe for target 'operations/operation_base.hpp' failed
Fragments of resolved Makefile (make -p):
operations/operation_base.hpp: operations/operation_base.hpp.o
# Implicit rule search has been done.
# Implicit/static pattern stem: 'operations/operation_base.hpp'
# Modification time never checked.
# File has been updated.
# Failed to be updated.
# automatic
# # := operations/operation_base.hpp
# automatic
# % :=
# automatic
# * := operations/operation_base.hpp
# automatic
# + := operations/operation_base.hpp.o
# automatic
# | :=
# automatic
# < := operations/operation_base.hpp.o
# automatic
# ^ := operations/operation_base.hpp.o
# automatic
# ? := operations/operation_base.hpp.o
# variable set hash-table stats:
# Load=8/32=25%, Rehash=0, Collisions=1/22=5%
# recipe to execute (built-in):
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $#
operations/operation_base.hpp.o: functions.hpp
# Implicit rule search has been done.
# Implicit/static pattern stem: 'operations/base.hpp'
# File is an intermediate prerequisite.
# File does not exist.
# File has been updated.
# Successfully updated.
# automatic
# # := operations/operation_base.hpp.o
# automatic
# % :=
# automatic
# * := operations/base.hpp
# automatic
# + := functions.hpp
# automatic
# | :=
# automatic
# < := functions.hpp
# automatic
# ^ := functions.hpp
# automatic
# ? := functions.hpp
# variable set hash-table stats:
# Load=8/32=25%, Rehash=0, Collisions=1/13=8%
# recipe to execute (from 'Makefile', line 4):
echo Making $# from $<
Apologies if this is not the place to post this. I'm not really sure where is.
I'm new to make and after a lot of research I stumbled across boilermake and have implemented it successfully on my project.
I build a release version of my project by default. I would like to know how to build a debug version of my project by supplying a command line argument such as debug. E.g.
make debug
which changes some of the compiler options set in CXXFLAGS in my top-level makefile.
However, I've not been able to get this to work.
Here is my makefile, which is included by the top-level makefile of boilermake.
INCDIRS := \
../component1/include \
../component2/include \
../component3/include
CXXFLAGS := O2 -pipe -std=c++11 -Wall -Wextra -Wold-style-cast -pedantic \
-isystem boost
SUBMAKEFILES := \
../component1/build/component1.mk \
../component2/build/component3.mk \
../component3/build/component4.mk
TARGET := my-project
TGT_LDFLAGS := -L. -L${TARGET_DIR}
TGT_LDLIBS := -lcomponent1 -lcomponent2 -lcomponent3 \
libboost_date_time-mt-sd.a \
libboost_filesystem-mt-sd.a
libcrypto.a \
libssl.a \
-lz \
-ldl \
-lpthread
TGT_PREREQS := \
libcomponent1.a \
libcomponent2.a \
libcomponent3.a
SOURCES := \
main.cpp
Here is the boilermake makefile.
# boilermake: A reusable, but flexible, boilerplate Makefile.
#
# Copyright 2008, 2009, 2010 Dan Moulding, Alan T. DeKok
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Caution: Don't edit this Makefile! Create your own main.mk and other
# submakefiles, which will be included by this Makefile.
# Only edit this if you need to modify boilermake's behavior (fix
# bugs, add features, etc).
# Note: Parameterized "functions" in this makefile that are marked with
# "USE WITH EVAL" are only useful in conjuction with eval. This is
# because those functions result in a block of Makefile syntax that must
# be evaluated after expansion. Since they must be used with eval, most
# instances of "$" within them need to be escaped with a second "$" to
# accomodate the double expansion that occurs when eval is invoked.
# ADD_CLEAN_RULE - Parameterized "function" that adds a new rule and phony
# target for cleaning the specified target (removing its build-generated
# files).
#
# USE WITH EVAL
#
define ADD_CLEAN_RULE
clean: clean_${1}
.PHONY: clean_${1}
clean_${1}:
$$(strip rm -f ${TARGET_DIR}/${1} $${${1}_OBJS:%.o=%.[do]})
$${${1}_POSTCLEAN}
endef
# ADD_OBJECT_RULE - Parameterized "function" that adds a pattern rule for
# building object files from source files with the filename extension
# specified in the second argument. The first argument must be the name of the
# base directory where the object files should reside (such that the portion
# of the path after the base directory will match the path to corresponding
# source files). The third argument must contain the rules used to compile the
# source files into object code form.
#
# USE WITH EVAL
#
define ADD_OBJECT_RULE
${1}/%.o: ${2}
${3}
endef
# ADD_TARGET_RULE - Parameterized "function" that adds a new target to the
# Makefile. The target may be an executable or a library. The two allowable
# types of targets are distinguished based on the name: library targets must
# end with the traditional ".a" extension.
#
# USE WITH EVAL
#
define ADD_TARGET_RULE
ifeq "$$(suffix ${1})" ".a"
# Add a target for creating a static library.
$${TARGET_DIR}/${1}: $${${1}_OBJS}
#mkdir -p $$(dir $$#)
$$(strip $${AR} $${ARFLAGS} $$# $${${1}_OBJS})
$${${1}_POSTMAKE}
else
# Add a target for linking an executable. First, attempt to select the
# appropriate front-end to use for linking. This might not choose the
# right one (e.g. if linking with a C++ static library, but all other
# sources are C sources), so the user makefile is allowed to specify a
# linker to be used for each target.
ifeq "$$(strip $${${1}_LINKER})" ""
# No linker was explicitly specified to be used for this target. If
# there are any C++ sources for this target, use the C++ compiler.
# For all other targets, default to using the C compiler.
ifneq "$$(strip $$(filter $${CXX_SRC_EXTS},$${${1}_SOURCES}))" ""
${1}_LINKER = $${CXX}
else
${1}_LINKER = $${CC}
endif
endif
$${TARGET_DIR}/${1}: $${${1}_OBJS} $${${1}_PREREQS}
#mkdir -p $$(dir $$#)
$$(strip $${${1}_LINKER} -o $$# $${LDFLAGS} $${${1}_LDFLAGS} \
$${${1}_OBJS} $${LDLIBS} $${${1}_LDLIBS})
$${${1}_POSTMAKE}
endif
endef
# CANONICAL_PATH - Given one or more paths, converts the paths to the canonical
# form. The canonical form is the path, relative to the project's top-level
# directory (the directory from which "make" is run), and without
# any "./" or "../" sequences. For paths that are not located below the
# top-level directory, the canonical form is the absolute path (i.e. from
# the root of the filesystem) also without "./" or "../" sequences.
define CANONICAL_PATH
$(patsubst ${CURDIR}/%,%,$(abspath ${1}))
endef
# COMPILE_C_CMDS - Commands for compiling C source code.
define COMPILE_C_CMDS
#mkdir -p $(dir $#)
$(strip ${CC} -o $# -c -MP -MD ${CFLAGS} ${SRC_CFLAGS} ${INCDIRS} \
${SRC_INCDIRS} ${SRC_DEFS} ${DEFS} $<)
endef
# COMPILE_CXX_CMDS - Commands for compiling C++ source code.
define COMPILE_CXX_CMDS
#mkdir -p $(dir $#)
$(strip ${CXX} -o $# -c -MP -MD ${CXXFLAGS} ${SRC_CXXFLAGS} ${INCDIRS} \
${SRC_INCDIRS} ${SRC_DEFS} ${DEFS} $<)
endef
# INCLUDE_SUBMAKEFILE - Parameterized "function" that includes a new
# "submakefile" fragment into the overall Makefile. It also recursively
# includes all submakefiles of the specified submakefile fragment.
#
# USE WITH EVAL
#
define INCLUDE_SUBMAKEFILE
# Initialize all variables that can be defined by a makefile fragment, then
# include the specified makefile fragment.
TARGET :=
TGT_CFLAGS :=
TGT_CXXFLAGS :=
TGT_DEFS :=
TGT_INCDIRS :=
TGT_LDFLAGS :=
TGT_LDLIBS :=
TGT_LINKER :=
TGT_POSTCLEAN :=
TGT_POSTMAKE :=
TGT_PREREQS :=
SOURCES :=
SRC_CFLAGS :=
SRC_CXXFLAGS :=
SRC_DEFS :=
SRC_INCDIRS :=
SUBMAKEFILES :=
# A directory stack is maintained so that the correct paths are used as we
# recursively include all submakefiles. Get the makefile's directory and
# push it onto the stack.
DIR := $(call CANONICAL_PATH,$(dir ${1}))
DIR_STACK := $$(call PUSH,$${DIR_STACK},$${DIR})
include ${1}
# Initialize internal local variables.
OBJS :=
# Ensure that valid values are set for BUILD_DIR and TARGET_DIR.
ifeq "$$(strip $${BUILD_DIR})" ""
BUILD_DIR := build
endif
ifeq "$$(strip $${TARGET_DIR})" ""
TARGET_DIR := .
endif
# Determine which target this makefile's variables apply to. A stack is
# used to keep track of which target is the "current" target as we
# recursively include other submakefiles.
ifneq "$$(strip $${TARGET})" ""
# This makefile defined a new target. Target variables defined by this
# makefile apply to this new target. Initialize the target's variables.
TGT := $$(strip $${TARGET})
ALL_TGTS += $${TGT}
$${TGT}_CFLAGS := $${TGT_CFLAGS}
$${TGT}_CXXFLAGS := $${TGT_CXXFLAGS}
$${TGT}_DEFS := $${TGT_DEFS}
$${TGT}_DEPS :=
TGT_INCDIRS := $$(call QUALIFY_PATH,$${DIR},$${TGT_INCDIRS})
TGT_INCDIRS := $$(call CANONICAL_PATH,$${TGT_INCDIRS})
$${TGT}_INCDIRS := $${TGT_INCDIRS}
$${TGT}_LDFLAGS := $${TGT_LDFLAGS}
$${TGT}_LDLIBS := $${TGT_LDLIBS}
$${TGT}_LINKER := $${TGT_LINKER}
$${TGT}_OBJS :=
$${TGT}_POSTCLEAN := $${TGT_POSTCLEAN}
$${TGT}_POSTMAKE := $${TGT_POSTMAKE}
$${TGT}_PREREQS := $$(addprefix $${TARGET_DIR}/,$${TGT_PREREQS})
$${TGT}_SOURCES :=
else
# The values defined by this makefile apply to the the "current" target
# as determined by which target is at the top of the stack.
TGT := $$(strip $$(call PEEK,$${TGT_STACK}))
$${TGT}_CFLAGS += $${TGT_CFLAGS}
$${TGT}_CXXFLAGS += $${TGT_CXXFLAGS}
$${TGT}_DEFS += $${TGT_DEFS}
TGT_INCDIRS := $$(call QUALIFY_PATH,$${DIR},$${TGT_INCDIRS})
TGT_INCDIRS := $$(call CANONICAL_PATH,$${TGT_INCDIRS})
$${TGT}_INCDIRS += $${TGT_INCDIRS}
$${TGT}_LDFLAGS += $${TGT_LDFLAGS}
$${TGT}_LDLIBS += $${TGT_LDLIBS}
$${TGT}_POSTCLEAN += $${TGT_POSTCLEAN}
$${TGT}_POSTMAKE += $${TGT_POSTMAKE}
$${TGT}_PREREQS += $${TGT_PREREQS}
endif
# Push the current target onto the target stack.
TGT_STACK := $$(call PUSH,$${TGT_STACK},$${TGT})
ifneq "$$(strip $${SOURCES})" ""
# This makefile builds one or more objects from source. Validate the
# specified sources against the supported source file types.
BAD_SRCS := $$(strip $$(filter-out $${ALL_SRC_EXTS},$${SOURCES}))
ifneq "$${BAD_SRCS}" ""
$$(error Unsupported source file(s) found in ${1} [$${BAD_SRCS}])
endif
# Qualify and canonicalize paths.
SOURCES := $$(call QUALIFY_PATH,$${DIR},$${SOURCES})
SOURCES := $$(call CANONICAL_PATH,$${SOURCES})
SRC_INCDIRS := $$(call QUALIFY_PATH,$${DIR},$${SRC_INCDIRS})
SRC_INCDIRS := $$(call CANONICAL_PATH,$${SRC_INCDIRS})
# Save the list of source files for this target.
$${TGT}_SOURCES += $${SOURCES}
# Convert the source file names to their corresponding object file
# names.
OBJS := $$(addprefix $${BUILD_DIR}/$$(call CANONICAL_PATH,$${TGT})/,\
$$(addsuffix .o,$$(basename $${SOURCES})))
# Add the objects to the current target's list of objects, and create
# target-specific variables for the objects based on any source
# variables that were defined.
$${TGT}_OBJS += $${OBJS}
$${TGT}_DEPS += $${OBJS:%.o=%.d}
$${OBJS}: SRC_CFLAGS := $${$${TGT}_CFLAGS} $${SRC_CFLAGS}
$${OBJS}: SRC_CXXFLAGS := $${$${TGT}_CXXFLAGS} $${SRC_CXXFLAGS}
$${OBJS}: SRC_DEFS := $$(addprefix -D,$${$${TGT}_DEFS} $${SRC_DEFS})
$${OBJS}: SRC_INCDIRS := $$(addprefix -I,\
$${$${TGT}_INCDIRS} $${SRC_INCDIRS})
endif
ifneq "$$(strip $${SUBMAKEFILES})" ""
# This makefile has submakefiles. Recursively include them.
$$(foreach MK,$${SUBMAKEFILES},\
$$(eval $$(call INCLUDE_SUBMAKEFILE,\
$$(call CANONICAL_PATH,\
$$(call QUALIFY_PATH,$${DIR},$${MK})))))
endif
# Reset the "current" target to it's previous value.
TGT_STACK := $$(call POP,$${TGT_STACK})
TGT := $$(call PEEK,$${TGT_STACK})
# Reset the "current" directory to it's previous value.
DIR_STACK := $$(call POP,$${DIR_STACK})
DIR := $$(call PEEK,$${DIR_STACK})
endef
# MIN - Parameterized "function" that results in the minimum lexical value of
# the two values given.
define MIN
$(firstword $(sort ${1} ${2}))
endef
# PEEK - Parameterized "function" that results in the value at the top of the
# specified colon-delimited stack.
define PEEK
$(lastword $(subst :, ,${1}))
endef
# POP - Parameterized "function" that pops the top value off of the specified
# colon-delimited stack, and results in the new value of the stack. Note that
# the popped value cannot be obtained using this function; use peek for that.
define POP
${1:%:$(lastword $(subst :, ,${1}))=%}
endef
# PUSH - Parameterized "function" that pushes a value onto the specified colon-
# delimited stack, and results in the new value of the stack.
define PUSH
${2:%=${1}:%}
endef
# QUALIFY_PATH - Given a "root" directory and one or more paths, qualifies the
# paths using the "root" directory (i.e. appends the root directory name to
# the paths) except for paths that are absolute.
define QUALIFY_PATH
$(addprefix ${1}/,$(filter-out /%,${2})) $(filter /%,${2})
endef
###############################################################################
#
# Start of Makefile Evaluation
#
###############################################################################
# Older versions of GNU Make lack capabilities needed by boilermake.
# With older versions, "make" may simply output "nothing to do", likely leading
# to confusion. To avoid this, check the version of GNU make up-front and
# inform the user if their version of make doesn't meet the minimum required.
MIN_MAKE_VERSION := 3.81
MIN_MAKE_VER_MSG := boilermake requires GNU Make ${MIN_MAKE_VERSION} or greater
ifeq "${MAKE_VERSION}" ""
$(info GNU Make not detected)
$(error ${MIN_MAKE_VER_MSG})
endif
ifneq "${MIN_MAKE_VERSION}" "$(call MIN,${MIN_MAKE_VERSION},${MAKE_VERSION})"
$(info This is GNU Make version ${MAKE_VERSION})
$(error ${MIN_MAKE_VER_MSG})
endif
# Define the source file extensions that we know how to handle.
C_SRC_EXTS := %.c
CXX_SRC_EXTS := %.C %.cc %.cp %.cpp %.CPP %.cxx %.c++
ALL_SRC_EXTS := ${C_SRC_EXTS} ${CXX_SRC_EXTS}
# Initialize global variables.
ALL_TGTS :=
DEFS :=
DIR_STACK :=
INCDIRS :=
TGT_STACK :=
# Include the main user-supplied submakefile. This also recursively includes
# all other user-supplied submakefiles.
$(eval $(call INCLUDE_SUBMAKEFILE,main.mk))
# Perform post-processing on global variables as needed.
DEFS := $(addprefix -D,${DEFS})
INCDIRS := $(addprefix -I,$(call CANONICAL_PATH,${INCDIRS}))
# Define the "all" target (which simply builds all user-defined targets) as the
# default goal.
.PHONY: all
all: $(addprefix ${TARGET_DIR}/,${ALL_TGTS})
# Add a new target rule for each user-defined target.
$(foreach TGT,${ALL_TGTS},\
$(eval $(call ADD_TARGET_RULE,${TGT})))
# Add pattern rule(s) for creating compiled object code from C source.
$(foreach TGT,${ALL_TGTS},\
$(foreach EXT,${C_SRC_EXTS},\
$(eval $(call ADD_OBJECT_RULE,${BUILD_DIR}/$(call CANONICAL_PATH,${TGT}),\
${EXT},$${COMPILE_C_CMDS}))))
# Add pattern rule(s) for creating compiled object code from C++ source.
$(foreach TGT,${ALL_TGTS},\
$(foreach EXT,${CXX_SRC_EXTS},\
$(eval $(call ADD_OBJECT_RULE,${BUILD_DIR}/$(call CANONICAL_PATH,${TGT}),\
${EXT},$${COMPILE_CXX_CMDS}))))
# Add "clean" rules to remove all build-generated files.
.PHONY: clean
$(foreach TGT,${ALL_TGTS},\
$(eval $(call ADD_CLEAN_RULE,${TGT})))
# Include generated rules that define additional (header) dependencies.
$(foreach TGT,${ALL_TGTS},\
$(eval -include ${${TGT}_DEPS}))
If you make the command as:
make debug=true
Then in one of your makefiles:
ifeq (debug,true)
CXXFLAGS += -DDEBUG
else
CXXFLAGS += -DNDEBUG
endif
this is the only line I changed on the make file, which is adding the "*.cpp" to the line:
SRC = $(wildcard *.c *.cpp)
and this is the clean target of the makefile:
# Target: clean project.
clean: begin clean_list end
clean_list :
#echo
#echo $(MSG_CLEANING)
$(REMOVE) $(OBJDIR)/$(TARGET).hex
$(REMOVE) $(OBJDIR)/$(TARGET).eep
$(REMOVE) $(OBJDIR)/$(TARGET).cof
$(REMOVE) $(OBJDIR)/$(TARGET).elf
$(REMOVE) $(OBJDIR)/$(TARGET).map
$(REMOVE) $(OBJDIR)/$(TARGET).sym
$(REMOVE) $(OBJDIR)/$(TARGET).lss
$(REMOVE) $(OBJ)
$(REMOVE) $(LST)
$(REMOVE) $(OBJDIR)/$(SRC:.c=.s)
$(REMOVE) $(OBJDIR)/$(SRC:.c=.d)
$(REMOVE) $(OBJDIR)/.dep/*
And when I run the makefile, it removes all my .cpp file. What did I do wrong?
Thanks in advance.
Jo
Here is the definition of OBJ and LST:
# Define all object files.
OBJ = $(addprefix $(OBJDIR)/,$(SRC:.c=.o)) $(addprefix $(OBJDIR)/,$(ASRC:.S=.o))
# Define all listing files.
LST = $(addprefix $(OBJDIR)/,$(SRC:.c=.lst)) $(addprefix $(OBJDIR)/,$(ASRC:.S=.lst))
This is the complete make file:
# Hey Emacs, this is a -*- makefile -*-
#----------------------------------------------------------------------------
# WinAVR Makefile Template written by Eric B. Weddington, Jörg Wunsch, et al.
#
# Released to the Public Domain
#
# Additional material for this makefile was written by:
# Peter Fleury
# Tim Henigan
# Colin O'Flynn
# Reiner Patommel
# Markus Pfaff
# Sander Pool
# Frederik Rouleau
#
#----------------------------------------------------------------------------
# On command line:
#
# make all = Make software.
#
# make clean = Clean out built project files.
#
# make coff = Convert ELF to AVR COFF.
#
# make extcoff = Convert ELF to AVR Extended COFF.
#
# make program = Download the hex file to the device, using avrdude.
# Please customize the avrdude settings below first!
#
# make debug = Start either simulavr or avarice as specified for debugging,
# with avr-gdb or avr-insight as the front end for debugging.
#
# make filename.s = Just compile filename.c into the assembler code only.
#
# make filename.i = Create a preprocessed source file for use in submitting
# bug reports to the GCC project.
#
# To rebuild project do "make clean" then "make all".
#----------------------------------------------------------------------------
#include conf.mk
# MCU name
MCU = atmega328p
# Processor frequency.
# This will define a symbol, F_CPU, in all source code files equal to the
# processor frequency. You can then use this symbol in your source code to
# calculate timings. Do NOT tack on a 'UL' at the end, this will be done
# automatically to create a 32-bit value in your source code.
F_CPU = 8000000
AVRDUDE_PROGRAMMER = stk500v1
# com1 = serial port. Use lpt1 to connect to parallel port.
AVRDUDE_PORT = /dev/cu.wchusbserial1420 # programmer connected to serial device
# Output format. (can be srec, ihex, binary)
FORMAT = ihex
# Target file name (without extension).
TARGET = main
# List C source files here. (C dependencies are automatically generated.)
SRC = $(wildcard *.c *.cpp)
OBJDIR = Builds
# List Assembler source files here.
# Make them always end in a capital .S. Files ending in a lowercase .s
# will not be considered source files but generated files (assembler
# output from the compiler), and will be deleted upon "make clean"!
# Even though the DOS/Win* filesystem matches both .s and .S the same,
# it will preserve the spelling of the filenames, and gcc itself does
# care about how the name is spelled on its command-line.
ASRC =
# Optimization level, can be [0, 1, 2, 3, s].
# 0 = turn off optimization. s = optimize for size.
# (Note: 3 is not always the best optimization level. See avr-libc FAQ.)
OPT = s
# Debugging format.
# Native formats for AVR-GCC's -g are dwarf-2 [default] or stabs.
# AVR Studio 4.10 requires dwarf-2.
# AVR [Extended] COFF format requires stabs, plus an avr-objcopy run.
DEBUG = stabs
# List any extra directories to look for include files here.
# Each directory must be seperated by a space.
# Use forward slashes for directory separators.
# For a directory that has spaces, enclose it in quotes.
EXTRAINCDIRS =
# Compiler flag to set the C Standard level.
# c89 = "ANSI" C
# gnu89 = c89 plus GCC extensions
# c99 = ISO C99 standard (not yet fully implemented)
# gnu99 = c99 plus GCC extensions
CSTANDARD = -std=c++11
# Place -D or -U options here
CDEFS = -DF_CPU=$(F_CPU)UL
# Place -I options here
CINCS =
#---------------- Compiler Options ----------------
# -g*: generate debugging information
# -O*: optimization level
# -f...: tuning, see GCC manual and avr-libc documentation
# -Wall...: warning level
# -Wa,...: tell GCC to pass this to the assembler.
# -adhlns...: create assembler listing
CFLAGS = -g$(DEBUG)
CFLAGS += $(CDEFS) $(CINCS)
CFLAGS += -O$(OPT)
CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
CFLAGS += -Wall -Wstrict-prototypes
CFLAGS += -Wa,-adhlns=$(addprefix $(OBJDIR)/,$(<:.c=.lst))
CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
CFLAGS += $(CSTANDARD)
CFLAGS += -gstabs
CFLAGS += -gstrict-dwarf
#---------------- Assembler Options ----------------
# -Wa,...: tell GCC to pass this to the assembler.
# -ahlms: create listing
# -gstabs: have the assembler create line number information; note that
# for use in COFF files, additional information about filenames
# and function names needs to be present in the assembler source
# files -- see avr-libc docs [FIXME: not yet described there]
# -listing-cont-lines: Sets the maximum number of continuation lines of hex
# dump that will be displayed for a given single line of source input.
ASFLAGS = -Wa,-adhlns=$(addprefix $(OBJDIR)/,$(<:.S=.lst)),-gstabs,--listing-cont-lines=100
#---------------- Library Options ----------------
# Minimalistic printf version
PRINTF_LIB_MIN = -Wl,-u,vfprintf -lprintf_min
# Floating point printf version (requires MATH_LIB = -lm below)
PRINTF_LIB_FLOAT = -Wl,-u,vfprintf -lprintf_flt
# If this is left blank, then it will use the Standard printf version.
PRINTF_LIB =
#PRINTF_LIB = $(PRINTF_LIB_MIN)
#PRINTF_LIB = $(PRINTF_LIB_FLOAT)
# Minimalistic scanf version
SCANF_LIB_MIN = -Wl,-u,vfscanf -lscanf_min
# Floating point + %[ scanf version (requires MATH_LIB = -lm below)
SCANF_LIB_FLOAT = -Wl,-u,vfscanf -lscanf_flt
# If this is left blank, then it will use the Standard scanf version.
SCANF_LIB =
#SCANF_LIB = $(SCANF_LIB_MIN)
#SCANF_LIB = $(SCANF_LIB_FLOAT)
MATH_LIB = -lm
#---------------- External Memory Options ----------------
# 64 KB of external RAM, starting after internal RAM (ATmega128!),
# used for variables (.data/.bss) and heap (malloc()).
#EXTMEMOPTS = -Wl,--section-start,.data=0x801100,--defsym=__heap_end=0x80ffff
# 64 KB of external RAM, starting after internal RAM (ATmega128!),
# only used for heap (malloc()).
#EXTMEMOPTS = -Wl,--defsym=__heap_start=0x801100,--defsym=__heap_end=0x80ffff
EXTMEMOPTS =
#---------------- Linker Options ----------------
# -Wl,...: tell GCC to pass this to linker.
# -Map: create map file
# --cref: add cross reference to map file
LDFLAGS = -Wl,-Map=$(OBJDIR)/$(TARGET).map,--cref
LDFLAGS += $(EXTMEMOPTS)
LDFLAGS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB)
#---------------- Programming Options (avrdude) ----------------
# Programming hardware: alf avr910 avrisp bascom bsd
# dt006 pavr picoweb pony-stk200 sp12 stk200 stk500
#
# Type: avrdude -c ?
# to get a full listing.
#
AVRDUDE_WRITE_FLASH = -U flash:w:$(OBJDIR)/$(TARGET).hex
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep
# Uncomment the following if you want avrdude's erase cycle counter.
# Note that this counter needs to be initialized first using -Yn,
# see avrdude manual.
#AVRDUDE_ERASE_COUNTER = -y
# Uncomment the following if you do /not/ wish a verification to be
# performed after programming the device.
#AVRDUDE_NO_VERIFY = -V
# Increase verbosity level. Please use this when submitting bug
# reports about avrdude. See <http://savannah.nongnu.org/projects/avrdude>
# to submit bug reports.
#AVRDUDE_VERBOSE = -v -v
AVRDUDE_FLAGS = -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER)
AVRDUDE_FLAGS += $(AVRDUDE_NO_VERIFY)
AVRDUDE_FLAGS += $(AVRDUDE_VERBOSE)
AVRDUDE_FLAGS += $(AVRDUDE_ERASE_COUNTER)
# --------------------------- EDITED BY WIJOYO UTOMO ---------------------------
# This is to modify the baud rate to 19200 when using my Arduino Nano v-3.0 as my ISP programmer
AVRDUDE_FLAGS += -C 19200
#---------------- Debugging Options ----------------
# For simulavr only - target MCU frequency.
DEBUG_MFREQ = $(F_CPU)
# Set the DEBUG_UI to either gdb or insight.
# DEBUG_UI = gdb
DEBUG_UI = insight
# Set the debugging back-end to either avarice, simulavr.
DEBUG_BACKEND = avarice
#DEBUG_BACKEND = simulavr
# GDB Init Filename.
GDBINIT_FILE = __avr_gdbinit
# When using avarice settings for the JTAG
JTAG_DEV = /dev/com1
# Debugging port used to communicate between GDB / avarice / simulavr.
DEBUG_PORT = 4242
# Debugging host used to communicate between GDB / avarice / simulavr, normally
# just set to localhost unless doing some sort of crazy debugging when
# avarice is running on a different computer.
DEBUG_HOST = localhost
#============================================================================
# Define programs and commands.
SHELL = sh
CC = /usr/local/CrossPack-AVR/bin/avr-g++
OBJCOPY = /usr/local/CrossPack-AVR/bin/avr-objcopy
OBJDUMP = /usr/local/CrossPack-AVR/bin/avr-objdump
SIZE = /usr/local/CrossPack-AVR/bin/avr-size
NM = /usr/local/CrossPack-AVR/bin/avr-nm
AVRDUDE = /usr/local/CrossPack-AVR/bin/avrdude
REMOVE = rm -f
COPY = cp
WINSHELL = cmd
# Define Messages
# English
MSG_ERRORS_NONE = Errors: none
MSG_BEGIN = -------- begin --------
MSG_END = -------- end --------
MSG_SIZE_BEFORE = Size before:
MSG_SIZE_AFTER = Size after:
MSG_COFF = Converting to AVR COFF:
MSG_EXTENDED_COFF = Converting to AVR Extended COFF:
MSG_FLASH = Creating load file for Flash:
MSG_EEPROM = Creating load file for EEPROM:
MSG_EXTENDED_LISTING = Creating Extended Listing:
MSG_SYMBOL_TABLE = Creating Symbol Table:
MSG_LINKING = Linking:
MSG_COMPILING = Compiling:
MSG_ASSEMBLING = Assembling:
MSG_CLEANING = Cleaning project:
# Define all object files.
OBJ = $(addprefix $(OBJDIR)/,$(SRC:.c=.o)) $(addprefix $(OBJDIR)/,$(ASRC:.S=.o))
# Define all listing files.
LST = $(addprefix $(OBJDIR)/,$(SRC:.c=.lst)) $(addprefix $(OBJDIR)/,$(ASRC:.S=.lst))
# Compiler flags to generate dependency files.
GENDEPFLAGS = -MD -MP -MF $(OBJDIR)/.dep/$(#F).d
# Combine all necessary flags and optional flags.
# Add target processor to flags.
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)
# Default target.
all: begin gccversion sizebefore clean build program sizeafter end
build: $(OBJDIR) elf hex eep lss sym
elf: $(OBJDIR)/$(TARGET).elf
hex: $(OBJDIR)/$(TARGET).hex
eep: $(OBJDIR)/$(TARGET).eep
lss: $(OBJDIR)/$(TARGET).lss
sym: $(OBJDIR)/$(TARGET).sym
$(OBJDIR):
#mkdir -p $#
# Eye candy.
# AVR Studio 3.x does not check make's exit code but relies on
# the following magic strings to be generated by the compile job.
begin:
#echo
#echo $(MSG_BEGIN)
end:
#echo $(MSG_END)
#echo
# Display size of file.
HEXSIZE = $(SIZE) --target=$(FORMAT) $(OBJDIR)/$(TARGET).hex
ELFSIZE = $(SIZE) --format=avr $(OBJDIR)/$(TARGET).elf
sizebefore:
#if test -f $(OBJDIR)/$(TARGET).elf; then echo; echo $(MSG_SIZE_BEFORE); $(ELFSIZE); \
2>/dev/null; echo; fi
sizeafter:
#if test -f $(OBJDIR)/$(TARGET).elf; then echo; echo $(MSG_SIZE_AFTER); $(ELFSIZE); \
2>/dev/null; echo; fi
# Display compiler version information.
gccversion :
#$(CC) --version
# Program the device.
program: $(OBJDIR)/$(TARGET).hex $(OBJDIR)/$(TARGET).eep
$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) $(AVRDUDE_WRITE_EEPROM)
# Generate avr-gdb config/init file which does the following:
# define the reset signal, load the target file, connect to target, and set
# a breakpoint at main().
gdb-config:
#$(REMOVE) $(GDBINIT_FILE)
#echo define reset >> $(GDBINIT_FILE)
#echo SIGNAL SIGHUP >> $(GDBINIT_FILE)
#echo end >> $(GDBINIT_FILE)
#echo file $(OBJDIR)/$(TARGET).elf >> $(GDBINIT_FILE)
#echo target remote $(DEBUG_HOST):$(DEBUG_PORT) >> $(GDBINIT_FILE)
ifeq ($(DEBUG_BACKEND),simulavr)
#echo load >> $(GDBINIT_FILE)
endif
#echo break main >> $(GDBINIT_FILE)
debug: gdb-config $(OBJDIR)/$(TARGET).elf
ifeq ($(DEBUG_BACKEND), avarice)
#echo Starting AVaRICE - Press enter when "waiting to connect" message displays.
#$(WINSHELL) /c start avarice --jtag $(JTAG_DEV) --erase --program --file \
$(OBJDIR)/$(TARGET).elf $(DEBUG_HOST):$(DEBUG_PORT)
#$(WINSHELL) /c pause
else
#$(WINSHELL) /c start simulavr --gdbserver --device $(MCU) --clock-freq \
$(DEBUG_MFREQ) --port $(DEBUG_PORT)
endif
#$(WINSHELL) /c start avr-$(DEBUG_UI) --command=$(GDBINIT_FILE)
# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB.
COFFCONVERT=$(OBJCOPY) --debugging \
--change-section-address .data-0x800000 \
--change-section-address .bss-0x800000 \
--change-section-address .noinit-0x800000 \
--change-section-address .eeprom-0x810000
coff: $(OBJDIR)/$(TARGET).elf
#echo
#echo $(MSG_COFF) $(OBJDIR)/$(TARGET).cof
$(COFFCONVERT) -O coff-avr $< $(OBJDIR)/$(TARGET).cof
extcoff: $(OBJDIR)/$(TARGET).elf
#echo
#echo $(MSG_EXTENDED_COFF) $(OBJDIR)/$(TARGET).cof
$(COFFCONVERT) -O coff-ext-avr $< $(OBJDIR)/$(TARGET).cof
# Create final output files (.hex, .eep) from ELF output file.
$(OBJDIR)/%.hex: $(OBJDIR)/%.elf
#echo
#echo $(MSG_FLASH) $#
$(OBJCOPY) -O $(FORMAT) -R .eeprom $< $#
$(OBJDIR)/%.eep: $(OBJDIR)/%.elf
#echo
#echo $(MSG_EEPROM) $#
-$(OBJCOPY) -j .eeprom --set-section-flags .eeprom=alloc,load \
--change-section-lma .eeprom=0 -O $(FORMAT) $< $#
# Create extended listing file from ELF output file.
$(OBJDIR)/%.lss: $(OBJDIR)/%.elf
#echo
#echo $(MSG_EXTENDED_LISTING) $#
$(OBJDUMP) -h -S $< > $#
# Create a symbol table from ELF output file.
$(OBJDIR)/%.sym: $(OBJDIR)/%.elf
#echo
#echo $(MSG_SYMBOL_TABLE) $#
$(NM) -n $< > $#
# Link: create ELF output file from object files.
.SECONDARY : $(OBJDIR)/$(TARGET).elf
.PRECIOUS : $(OBJ)
$(OBJDIR)/%.elf: $(OBJ)
#echo
#echo $(MSG_LINKING) $#
$(CC) $(ALL_CFLAGS) $^ --output $# $(LDFLAGS)
# Compile: create object files from C source files.
$(OBJDIR)/%.o : %.c
#echo
#echo $(MSG_COMPILING) $<
$(CC) -c $(ALL_CFLAGS) $(abspath $<) -o $#
# Compile: create assembler files from C source files.
$(OBJDIR)/%.s : %.c
$(CC) -S $(ALL_CFLAGS) $< -o $#
# Assemble: create object files from assembler source files.
$(OBJDIR)/%.o : %.S
#echo
#echo $(MSG_ASSEMBLING) $<
$(CC) -c $(ALL_ASFLAGS) $< -o $#
# Create preprocessed source for use in sending a bug report.
$(OBJDIR)/%.i : %.c
$(CC) -E -mmcu=$(MCU) -I. $(CFLAGS) $< -o $#
# Target: clean project.
clean: begin clean_list end
clean_list :
#echo
#echo $(MSG_CLEANING)
$(REMOVE) $(OBJDIR)/$(TARGET).hex
$(REMOVE) $(OBJDIR)/$(TARGET).eep
$(REMOVE) $(OBJDIR)/$(TARGET).cof
$(REMOVE) $(OBJDIR)/$(TARGET).elf
$(REMOVE) $(OBJDIR)/$(TARGET).map
$(REMOVE) $(OBJDIR)/$(TARGET).sym
$(REMOVE) $(OBJDIR)/$(TARGET).lss
$(REMOVE) $(OBJ)
$(REMOVE) $(LST)
$(REMOVE) $(OBJDIR)/$(SRC:.c=.s)
$(REMOVE) $(OBJDIR)/$(SRC:.c=.d)
$(REMOVE) $(OBJDIR)/.dep/*
# Include the dependency files.
-include $(shell mkdir $(OBJDIR)/.dep 2>/dev/null) $(wildcard $(OBJDIR)/.dep/*)
# Listing of phony targets.
.PHONY : all begin finish end sizebefore sizeafter gccversion \
build elf hex eep lss sym coff extcoff \
clean clean_list program debug gdb-config
Since I can't post image, I will explain my directory structure. There is a Builds sub directory that holds the .hex .eep .o .elf .lst of the build (when successful). My source files and header files are not in the Builds sub directory, but in the project directory which contains the Builds directory.
Alan Stokes is correct, except it doesn't matter what value OBJDIR has, because your clean rule is badly written. Consider this line in the clean rule:
$(REMOVE) $(OBJDIR)/$(SRC:.c=.s)
It's a common mistake to think that just by prefixing a variable with another variable, every word in the second will be prefixed by the first, but that's just not true. That's what addprefix is for (or patsubst if you prefer). The above line expands to this (assuming REMOVE is rm -f, OBJDIR is obj, and SRC is foo.c bar.c biz.c baz.c):
rm -f obj/foo.s bar.s biz.s baz.s
You can immediately see this is not what you wanted.
Now consider what happens when you add all the .cpp files, so SRC is now foo.c bar.c biz.c baz.c one.cpp two.cpp three.cpp. The substitution $(SRC:.c=.s) will replace the .c suffixes, BUT it will not touch any words that don't match the pattern. So, the command you run becomes:
rm -f obj/foo.s bar.s biz.s baz.s one.cpp two.cpp three.cpp
So you have two problems: the first is you should be using addprefix (or patsubst) to prefix all the words with the directory, and the second is you should be using the basename function to remove the suffix for all the words. There are multiple ways to do it but something like this will work:
$(REMOVE) $(patsubst %,$(OBJDIR)/%.s,$(basename $(SRC)))
And of course, you have to do the same with your rule for .d files. I strongly recommend you try it first with make -n before you let it rip for real.
I am trying to have a command executed depending on the current target from a list of targets (currently only one entry in that list) before another makefile is executed.
i have this:
$(LIBS):
ifeq ($#,libname)
my command here
endif
$(MAKE) -C ./lib/$#
the problem is, that the ifeq does not get executed even if the target name is correct. Using an $(info $#) shows exactly the libname but the expression is not evaluated as true.
I thought maybe there is a problem with expansion of the automatic variable in a conditional so i tried using an eval like this:
$(LIBS):
$(eval CURRENT_LIB := $#)
ifeq ($(CURRENT_LIB),libname)
my command here
endif
$(MAKE) -C ./lib/$#
info shows that the variable now equals exactly the libname but the ifeq does not get excuted.
When i enter something like ifeq (libname,libname) it works so the statement is working, but the comparison between variable and text does not evaluate to true even if the two are equal and it should work.
GNU make version is 4.1
What am i missing here?
Complete Makefile:
CC := g++
CFLAGS := -v -std=c++0x -pthread -Wall -g -O3
OBJ := mycode.o
OBJ += moreobjects.o
#more objects in here
LIBS = libname
.PHONY: libs $(LIBS)
SRC = $(OBJ:%.o=%.cpp)
DEPFILE := .depend
mytarget: libs $(OBJ)
$(CC) $(CFLAGS) -o $# $(OBJ)
-include $(DEPFILE)
%.o: %.cpp
$(CC) $(CFLAGS) -c $<
$(CC) -MM -std=c++11 $(SRC) > $(DEPFILE)
libs: $(LIBS)
$(LIBS):
$(eval CURRENT_LIB := $#)
ifeq ($(CURRENT_LIB),libname)
./lib/$(CURRENT_LIB)/configure
endif
$(MAKE) -C ./lib/$#
.PHONY: clean_all
clean_all: clean
$(foreach dir,$(LIBS),$(MAKE) clean -C ./lib/$(dir))
.PHONY: clean
clean:
rm -rf mytarget $(OBJ) $(DEPFILE)
Thank You very much!
What you are trying to do cannot work. From the documentation:
Conditionals control what 'make' actually "sees" in the makefile, so they cannot be used to control recipes at the time of execution.
The way I put it is that conditionals are evaluated at the time the Makefile is read. So make reads your Makefile, your conditional is false and it is removed.
"Normal" conditionals, don't work in a recipe with GNU make. Unfortunately that's true.
Workaround:
But you can use Functions for Conditionals and The eval Function instead. A small example for error detection from my build system. Here I use the eval and if function.
Snippet from Makefile.xu, which use eval and if:
xubuild_check_objects:
$(eval RESULT = $(call xu-obj_func-valid,$(xu-src-c-y)))
#echo 'echo nothing (dummy)' >/dev/null
$(if $(filter n,$(RESULT)), \
$(error XUbuild: error: xubuild_check_objects: xu-obj->xu-src-c-y override detected! Cannot build target! Aborting),)
NOTE:
#echo 'echo nothing (dummy)' >/dev/null
This here is required. When no command will be executed after eval, you get a make: Nothing to be done for 'xubuild_check_objects message.
Project Makefile (root directory) with error:
# XUbuild system (MUST included first)
include Makefile.xu
# This produces an error and aborts the build
xu-src-c-y = test.c
all: xubuild_check_objects
Error output:
$ make
Makefile.xu:104: *** XUbuild: error: xubuild_check_objects: xu-obj->xu-src-c-y override detected! Cannot build target! Aborting. Stop.
$
Project Makefile (root directory) without error:
# XUbuild system (MUST included first)
include Makefile.xu
# My build system is happy with this:
xu-src-c-y += test.c
all: xubuild_check_objects
Output (no error):
$ make
$
The full error detection from my build system simplified. I removed the most Makefile.xu "code" and only post the error detection here. (I don't explain my whole build system in this answer):
# XUbuild generic object
xu-obj := :xu-obj
xu-obj_del := :
# XUbuild object types
xu-obj_nop := n
xu-obj_yes := y
xu-obj_module := m
xu-obj_src := s
xu-obj_flag := f
# XUbuild source priority
xu-obj_src_head := 0
xu-obj_src_normal := 1
# XUbuild source types
xu-obj_src_a := 0
xu-obj_src_c := 1
xu-obj_src_cpp := 2
# XUbuild object functions
xu-obj_func-get = $(filter $(xu-obj)%,$(1))
xu-obj_func-remove = $(filter-out $(xu-obj)%,$(1))
xu-obj_func-valid = $(if $(call xu-obj_func-get,$(1)),y,n)
# XUbuild source objects
xu-src := $(xu-obj)$(xu-obj_del)
# XUbuild C source
xu-src-c := $(xu-src)$(xu-obj_src_normal)$(xu-obj_del)$(xu-obj_src_c)$(xu-obj_del)
xu-src-c- := $(xu-src-c)$(xu-obj_nop)
xu-src-c-y := $(xu-src-c)$(xu-obj_yes)
XUBUILD_PHONY += xubuild_check_objects
xubuild_check_objects:
$(eval RESULT = $(call xu-obj_func-valid,$(xu-src-c-y)))
#echo 'echo nothing (dummy)' >/dev/null
$(if $(filter n,$(RESULT)), \
$(error XUbuild: error: xubuild_check_objects: xu-obj->xu-src-c-y override detected! Cannot build target! Aborting),)
.PHONY: $(XUBUILD_PHONY)
When I try to run the following make file (updated with suggestions below):
# Build Directories
src_dir=src
obj_dir=obj
bin_dir=bin
cc=cl
cc_flags=
configs = dbg rel
# create the directory for the current target.
dir_guard=#mkdir -p $(#D)
# source files
src = MainTest.cpp
define build_template =
# object files - replace .cpp on source files with .o and add the temp directory prefix
obj_$(1) = $$(addprefix $$(obj_dir)/$(1)/, $$(addsuffix .obj, $$(basename $$(src))))
testVar = "wooo"
# build TwoDee.exe from all of the object files.
$$(bin_dir)/$(1)/MainTest.exe : $$(obj_$(1))
$$(dir_guard)
$$(cc) -out:$$# $$(obj_$(1)) -link
# build all of the object files in the temp directory from their corresponding cpp files.
$$(obj): $$(obj_dir)/$(1)/%.obj : $$(src_dir)/%.cpp
$$(dir_guard)
$$(cc) $$(cc_flags) -Fo$$(obj_dir)/$$(1) -c $$<
endef
$(foreach cfg_dir,$(configs),$(eval $(call build_template,$(cfg_dir))))
release: $(bin_dir)/rel/MainTest.exe
debug: cc_flags += -Yd -ZI
debug: $(bin_dir)/dbg/MainTest.exe
$(warning testVar $(testVar))
All I get is:
$ make
Makefile:41: testVar
make: *** No rule to make target `bin/rel/MainTest.exe', needed by `release'. Stop.
You can see from the output that the testVar variable is never set. I made these changes based on my last question: Why doesn't my makefile target-specific variable work?
There are some spaces which confuse Make. Try this:
$(foreach cfg_dir,$(configs),$(eval $(call build_template,$(cfg_dir))))
Also make sure that you specify the right objects to link:
$$(cc) -out:$$# $$(obj_$(1)) -link
And as #Beta pointed out in the comments below, the = in the template definition syntax requires GNU make 3.82 or later. So better omit it from the line:
define build_template