Setting variable in if statement not working - makefile

In this very simple makefile, I try to set a variable inside a condition, but outside the change is not noticed.
all:
STUFF="nothing"
ifeq (true, true)
echo "setting"
STUFF="hi"
endif
echo $(STUFF)
When I run this, I expect the last command to print "hi", but it doesn't.
chris#cranberry ~/git % make
STUFF="nothing"
echo "setting"
setting
STUFF="hi"
echo
What am I doing wrong here?
UPDATE:
run: build/aura.bin
ifdef DEBUGGING
$(eval DFLAGS=-s -S)
endif
qemu-system-i386 -serial stdio $(DFLAGS) -kernel build/aura.bin

You can do this without eval
ifdef DEBUGGING
run: private DFLAGS += -s -S
endif
run: build/aura.bin
qemu-system-i386 -serial stdio $(DFLAGS) -kernel $<
Another alternative is
run: private DFLAGS += $(if $(DEBUGGING),-s -S)
run: build/aura.bin
qemu-system-i386 -serial stdio $(DFLAGS) -kernel $<
Delete private to make the value of DFLAGS available in the prerequisites of run.

I fixed it by using $(eval <statement>) to set the values.
all:
STUFF="nothing"
ifeq (true, true)
echo "setting"
$(eval STUFF="hi")
endif
echo $(STUFF)

Related

make error from Makefile generated from premake

I'm getting the following output from the console when trying to run make.
==== Building Project (debug) ====
process_begin: CreateProcess(NULL, echo Creating obj/Debug, ...) failed.
make (e=2): The system cannot find the file specified.
make[1]: *** [Project.make:90: obj/Debug] Error 2
make: *** [Makefile:30: Project] Error 2
The makefile is generated using premake and I have no experience with makefiles. the make file looks like this:
# Alternative GNU Make workspace makefile autogenerated by Premake
ifndef config
config=debug
endif
ifndef verbose
SILENT = #
endif
ifeq ($(config),debug)
Project_config = debug
else ifeq ($(config),release)
Project_config = release
else
$(error "invalid configuration $(config)")
endif
PROJECTS := Project
.PHONY: all clean help $(PROJECTS)
all: $(PROJECTS)
Project:
ifneq (,$(Project_config))
#echo "==== Building Project ($(Project_config)) ===="
#${MAKE} --no-print-directory -C . -f Project.make config=$(Project_config)
endif
clean:
#${MAKE} --no-print-directory -C . -f Project.make clean
help:
#echo "Usage: make [config=name] [target]"
#echo ""
#echo "CONFIGURATIONS:"
#echo " debug"
#echo " release"
#echo ""
#echo "TARGETS:"
#echo " all (default)"
#echo " clean"
#echo " Project"
#echo ""
#echo "For more information, see https://github.com/premake/premake-core/wiki"
and the premake5.lua file is this:
workspace "Workspace"
configurations { "Debug", "Release" }
project "Project"
kind "ConsoleApp"
language "C++"
targetdir "bin/%{cfg.buildcfg}"
files { "**.h", "**.cpp" }
filter "configurations:Debug"
defines { "DEBUG" }
symbols "On"
filter "configurations:Release"
defines { "NDEBUG" }
optimize "On"
Any ideas on what might be wrong?
Thanks in advance.
Edit: the Project.make file looks like this:
# Alternative GNU Make project makefile autogenerated by Premake
ifndef config
config=debug
endif
ifndef verbose
SILENT = #
endif
.PHONY: clean prebuild
SHELLTYPE := posix
ifeq (.exe,$(findstring .exe,$(ComSpec)))
SHELLTYPE := msdos
endif
# Configurations
# #############################################
RESCOMP = windres
INCLUDES +=
FORCE_INCLUDE +=
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
LIBS +=
LDDEPS +=
LINKCMD = $(CXX) -o "$#" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
define PREBUILDCMDS
endef
define PRELINKCMDS
endef
define POSTBUILDCMDS
endef
ifeq ($(config),debug)
TARGETDIR = bin/Debug
TARGET = $(TARGETDIR)/Project.exe
OBJDIR = obj/Debug
DEFINES += -DDEBUG
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -g
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -g
ALL_LDFLAGS += $(LDFLAGS)
else ifeq ($(config),release)
TARGETDIR = bin/Release
TARGET = $(TARGETDIR)/Project.exe
OBJDIR = obj/Release
DEFINES += -DNDEBUG
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -O2
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -O2
ALL_LDFLAGS += $(LDFLAGS) -s
endif
# Per File Configurations
# #############################################
# File sets
# #############################################
GENERATED :=
OBJECTS :=
GENERATED += $(OBJDIR)/main.o
OBJECTS += $(OBJDIR)/main.o
# Rules
# #############################################
all: $(TARGET)
#:
$(TARGET): $(GENERATED) $(OBJECTS) $(LDDEPS) | $(TARGETDIR)
$(PRELINKCMDS)
#echo Linking Project
$(SILENT) $(LINKCMD)
$(POSTBUILDCMDS)
$(TARGETDIR):
#echo Creating $(TARGETDIR)
ifeq (posix,$(SHELLTYPE))
$(SILENT) mkdir -p $(TARGETDIR)
else
$(SILENT) mkdir $(subst /,\\,$(TARGETDIR))
endif
$(OBJDIR):
#echo Creating $(OBJDIR)
ifeq (posix,$(SHELLTYPE))
$(SILENT) mkdir -p $(OBJDIR)
else
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
endif
clean:
#echo Cleaning Project
ifeq (posix,$(SHELLTYPE))
$(SILENT) rm -f $(TARGET)
$(SILENT) rm -rf $(GENERATED)
$(SILENT) rm -rf $(OBJDIR)
else
$(SILENT) if exist $(subst /,\\,$(TARGET)) del $(subst /,\\,$(TARGET))
$(SILENT) if exist $(subst /,\\,$(GENERATED)) rmdir /s /q $(subst /,\\,$(GENERATED))
$(SILENT) if exist $(subst /,\\,$(OBJDIR)) rmdir /s /q $(subst /,\\,$(OBJDIR))
endif
prebuild: | $(OBJDIR)
$(PREBUILDCMDS)
ifneq (,$(PCH))
$(OBJECTS): $(GCH) | $(PCH_PLACEHOLDER)
$(GCH): $(PCH) | prebuild
#echo $(notdir $<)
$(SILENT) $(CXX) -x c++-header $(ALL_CXXFLAGS) -o "$#" -MF "$(#:%.gch=%.d)" -c "$<"
$(PCH_PLACEHOLDER): $(GCH) | $(OBJDIR)
ifeq (posix,$(SHELLTYPE))
$(SILENT) touch "$#"
else
$(SILENT) echo $null >> "$#"
endif
else
$(OBJECTS): | prebuild
endif
# File Rules
# #############################################
$(OBJDIR)/main.o: main.cpp
#echo $(notdir $<)
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$#" -MF "$(#:%.o=%.d)" -c "$<"
-include $(OBJECTS:%.o=%.d)
ifneq (,$(PCH))
-include $(PCH_PLACEHOLDER).d
endif
The error message appears to be associated with this rule from Project.make ...
$(OBJDIR):
#echo Creating $(OBJDIR)
ifeq (posix,$(SHELLTYPE))
$(SILENT) mkdir -p $(OBJDIR)
else
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
endif
... and I infer from the appearance of CreateProcess in the diagnbostic message that you are trying to perform this build on Windows.
The issue is a failure to create a subdirectory obj/Debug of the working directory. Sometimes such diagnostics are misleading as to the specific nature of the problem, but here are some possibilities:
the Windows branch of the implicated rule appears to be relying on "command extensions" to be enabled in order to create two directories with one mkdir command. They are enabled by default, but they can be disabled either globally or on a per-processes basis. If they are disabled at least for the cmd.exe process in which you are performing your build then the failure you describe is likely. In that case, you could probably work around that particular issue by manually creating the obj directory, but you might face other, similar ones.
Alternatively, you may simply not be authorized to write in the project folder.

make fclean && make all works, make re doesn't

I'm working on an advanced makefile that I found in a book. I've got some simple rules inside:
clean to delete binaries
fclean to delete some extra files too (links to binaries generated by ln)
all to make all
re to make fclean then make all
when I do make fclean then make all, it works perfectly. When i do make re, an error occurs:
error: unable to open output file '/Users/malberte/work/libft/bin/libft/common/ft_atoi.o':
'No such file or directory'
1 error generated.
So here is my code:
$(_MODULE_NAME)_OBJS := $(addsuffix $(_OBJEXT),$(addprefix $($(_MODULE_NAME)_OUTPUT)/,$(basename $(SRCS)))) $(DEPS)
$(_MODULE_NAME)_BINARY := $($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(BINARY_EXT)
$(_MODULE_NAME)_EXPOSE_BINARY := $(_ROOT)/$(BINARY)$(BINARY_EXT)
ifneq ($(_NO_RULES),T)
ifneq ($($(_MODULE_NAME)_DEFINED), T)
_CLEAN := clean-$(_MODULE_NAME)
_FCLEAN := fclean-$(_MODULE_NAME)
_ALL := all-$(_MODULE_NAME)
_RE := re-$(_MODULE_NAME)
_IGNORE := $(shell mkdir -p $($(_MODULE_NAME)_OUTPUT))
.PHONY: all re $(_ALL) $(_RE)
re: fclean all
# re: $(_RE)
# $(_RE): $(_FCLEAN) $(_ALL)
all: $(_ALL)
$(_ALL): $($(_MODULE_NAME)_BINARY)
.PHONY: $(_MODULE_NAME)
$(_MODULE_NAME): $($(_MODULE_NAME)_BINARY)
.PHONY: fclean clean $(_CLEAN)
fclean: $(_FCLEAN)
$(_FCLEAN): $(_CLEAN)
rm -rf $($(patsubst fclean-%,%,$#)_EXPOSE_BINARY)
clean: $(_CLEAN)
$(_CLEAN):
rm -rf $($(patsubst clean-%,%,$#)_OUTPUT)
$($(_MODULE_NAME)_OUTPUT)/%.o: $(_MODULE_PATH)/%.c
#$(COMPILE.c) -o '$#' '$<'
$($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(_LIBEXT): $($(_MODULE_NAME)_OBJS)
#if [ "$(LIBMERGE)" = "F" ]; \
then \
$(AR) r '$#' $^; \
ranlib '$#'; \
else \
libtool -static -o '$#' $^; \
fi
$($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(_EXEEXT): $($(_MODULE_NAME)_OBJS)
$(LINK.c) $^ -o '$#'
$(_MODULE_NAME)_DEFINED := T
endif
endif
I've tried lot of things, I really don't understand what is happening when I use make re and it throws the error above.
Someone has an idea please ?
You have this line in your makefile:
_IGNORE := $(shell mkdir -p $($(_MODULE_NAME)_OUTPUT))
which creates the output directory, as the makefile is being parsed. Then you run your clean target which invokes this recipe:
rm -rf $($(patsubst clean-%,%,$#)_OUTPUT)
which causes the output directory to be deleted. Then you run your all target which invokes the compiler and asks it to write the output file to $($(_MODULE_NAME)_OUTPUT)/%.o but that directory no longer exists.
So the compiler gives you the error:
error: unable to open output file '...': No such file or directory
If you run make twice, then the first time you clean and delete the directory, then when you run make all it will run the _IGNORE shell command and create the directory again so it will exist.
If you run make re one time, then the makefile is only parsed one time and the output directory is only created one time (before it's deleted).
Okay thank you so much. It drove me to think about how a makefile really works, so here is my basic solution, thanks to you:
$(_MODULE_NAME)_OBJS := $(addsuffix $(_OBJEXT),$(addprefix $($(_MODULE_NAME)_OUTPUT)/,$(basename $(SRCS)))) $(DEPS)
$(_MODULE_NAME)_BINARY := $($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(BINARY_EXT)
$(_MODULE_NAME)_EXPOSE_BINARY := $(_ROOT)/$(BINARY)$(BINARY_EXT)
ifneq ($(_NO_RULES),T)
ifneq ($($(_MODULE_NAME)_DEFINED), T)
_OUTPUT_TREE := output-tree-$(_MODULE_NAME)
_CLEAN := clean-$(_MODULE_NAME)
_FCLEAN := fclean-$(_MODULE_NAME)
_ALL := all-$(_MODULE_NAME)
_RE := re-$(_MODULE_NAME)
# _IGNORE := $(shell mkdir -p $($(_MODULE_NAME)_OUTPUT))
.PHONY: all re $(_ALL) $(_RE)
re: fclean all
# re: $(_RE)
# $(_RE): $(_FCLEAN) $(_ALL)
all: $(_ALL)
$(_ALL): $($(_MODULE_NAME)_BINARY)
.PHONY: $(_MODULE_NAME)
$(_MODULE_NAME): $($(_MODULE_NAME)_BINARY)
.PHONY: fclean clean $(_CLEAN)
fclean: $(_FCLEAN)
$(_FCLEAN): $(_CLEAN)
rm -rf $($(patsubst fclean-%,%,$#)_EXPOSE_BINARY)
clean: $(_CLEAN)
$(_CLEAN):
rm -rf $($(patsubst clean-%,%,$#)_OUTPUT)
$($(_MODULE_NAME)_OUTPUT)/%.o: $(_MODULE_PATH)/%.c | $(_OUTPUT_TREE)
#$(COMPILE.c) -o '$#' '$<'
$($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(_LIBEXT): $($(_MODULE_NAME)_OBJS)
#if [ "$(LIBMERGE)" = "F" ]; \
then \
$(AR) r '$#' $^; \
ranlib '$#'; \
else \
libtool -static -o '$#' $^; \
fi
$($(_MODULE_NAME)_OUTPUT)/$(BINARY)$(_EXEEXT): $($(_MODULE_NAME)_OBJS)
$(LINK.c) $^ -o '$#'
.PHONY: output-tree $(_OUTPUT_TREE)
output-tree: $(_OUTPUT_TREE)
$(_OUTPUT_TREE):
mkdir -p $($(_MODULE_NAME)_OUTPUT)
$(_MODULE_NAME)_DEFINED := T
endif
endif
I added a prerequisite order to the atomic target
$($(_MODULE_NAME)_OUTPUT)/%.o: $(_MODULE_PATH)/%.c | $(_OUTPUT_TREE)
Here is the rule:
.PHONY: output-tree $(_OUTPUT_TREE)
output-tree: $(_OUTPUT_TREE)
$(_OUTPUT_TREE):
mkdir -p $($(_MODULE_NAME)_OUTPUT)
I'll see if I need more adjustments but it seems to be the right way !

.PRECIOUS target not getting compiled and says nothing to be done for all

I want the following Target .PRECIOUS in Makefile to be executed so that appropriate gRPC header source files are generated based on .proto file. The files are not generated and it says nothing to do
Makefile
HOST_SYSTEM = $(shell uname | cut -f 1 -d_)
SYSTEM ?= $(HOST_SYSTEM)
CXX = g++
CPPFLAGS += `pkg-config --cflags protobuf grpc`
CXXFLAGS += -std=c++11
ifeq ($(SYSTEM),Darwin)
LDFLAGS += -L/usr/local/lib `pkg-config --libs protobuf grpc++ grpc`\
-lgrpc++_reflection\
-ldl
else
LDFLAGS += -L/usr/local/lib `pkg-config --libs protobuf grpc++ grpc`\
-Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed\
-ldl
endif
PROTOC = protoc
GRPC_CPP_PLUGIN = grpc_cpp_plugin
GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)`
PROTOS_PATH = protos
vpath %.proto $(PROTOS_PATH)
all: system-check
.PRECIOUS: %.grpc.pb.cc
%.grpc.pb.cc: %.proto
$(PROTOC) -I $(PROTOS_PATH) --grpc_out=. --plugin=protoc-gen-grpc=$(GRPC_CPP_PLUGIN_PATH) $<
.PRECIOUS: %.pb.cc
%.pb.cc: %.proto
$(PROTOC) -I $(PROTOS_PATH) --cpp_out=. $<
clean:
rm -f *.o *.pb.cc *.pb.h
# The following is to test your system and ensure a smoother experience.
# They are by no means necessary to actually compile a grpc-enabled software.
PROTOC_CMD = which $(PROTOC)
PROTOC_CHECK_CMD = $(PROTOC) --version | grep -q libprotoc.3
PLUGIN_CHECK_CMD = which $(GRPC_CPP_PLUGIN)
HAS_PROTOC = $(shell $(PROTOC_CMD) > /dev/null && echo true || echo false)
ifeq ($(HAS_PROTOC),true)
HAS_VALID_PROTOC = $(shell $(PROTOC_CHECK_CMD) 2> /dev/null && echo true || echo false)
endif
HAS_PLUGIN = $(shell $(PLUGIN_CHECK_CMD) > /dev/null && echo true || echo false)
SYSTEM_OK = false
ifeq ($(HAS_VALID_PROTOC),true)
ifeq ($(HAS_PLUGIN),true)
SYSTEM_OK = true
endif
endif
system-check:
ifneq ($(HAS_VALID_PROTOC),true)
#echo " DEPENDENCY ERROR"
#echo
#echo "You don't have protoc 3.0.0 installed in your path."
#echo "Please install Google protocol buffers 3.0.0 and its compiler."
#echo "You can find it here:"
#echo
#echo " https://github.com/google/protobuf/releases/tag/v3.0.0"
#echo
#echo "Here is what I get when trying to evaluate your version of protoc:"
#echo
-$(PROTOC) --version
#echo
#echo
endif
ifneq ($(HAS_PLUGIN),true)
#echo " DEPENDENCY ERROR"
#echo
#echo "You don't have the grpc c++ protobuf plugin installed in your path."
#echo "Please install grpc. You can find it here:"
#echo
#echo " https://github.com/grpc/grpc"
#echo
#echo "Here is what I get when trying to detect if you have the plugin:"
#echo
-which $(GRPC_CPP_PLUGIN)
#echo
#echo
endif
ifneq ($(SYSTEM_OK),true)
#false
endif
On Running make
Bob#hub:~/Raju/demo_grpc$ make
make: Nothing to be done for 'all'.
.PRECIOUS has no impact on the issue you're seeing as far as I can tell. Your makefile, from make's point of view, boils down to to two rules:
all: system-check
system-check:
...some echo lines or exit 1...
and that's it. I don't see any other prerequisites for all so that's all it will do.
In order to get that message from make, either (a) there is a file on your filesystem named all, or (b) there is a file on your filesystem named system-check.
You can find out which it is, and/or what else make is doing, by running make -d and checking the output.
You can avoid problems with files existing on your filesystem for these special targets by making them .PHONY:
.PHONY: all system-check

Makefile rule always been processed

My recipe $(HDAIMG) is always been processed, even when already there is a $(HDAIMG) file in the folder. What am I doing wrong?
HDAIMG := $(TESTDIR)/$(PROJECT)-hda.img
HDAIMG value, actually, is test/project-hda.img
PHONY: $(PROJECT)
all: $(PROJECT) $(HDAIMG)
$(PROJECT): check-env
$(call v_exec, 1, $(MAKE) -C $(SRCDIR) $#)
$(HDAIMG): $(PROJECT) check-env
$(call print_white_init, HDAIMG)
$(call print, Creating $#)
$(call v_exec, 2, dd if=/dev/zero of=$# count=0 bs=1 seek=$(HDAIMGSIZE) &> /dev/null)
$(call print, Partitioning $#)
$(call v_exec, 2, parted --script $# mklabel msdos mkpart primary ext4 1 100%)
$(call print, Creating $# device maps)
$(call v_exec, 2, sudo kpartx -a $# -s)
$(call v_exec, 2, sudo mkfs.ext4 /dev/mapper/loop0p1 -q)
$(call v_exec, 2, sudo mount /dev/mapper/loop0p1 $(TESTDIR)/mnt)
$(call v_exec, 2, sudo umount $(TESTDIR)/mnt)
$(call v_exec, 2, sudo kpartx -d $#)
$(call print_white_done, HDAIMG)
check-env:
ifneq ($(ERROR),)
$(call print_error, $(ERROR))
exit 1
endif
That called functions are used to print with color or to execute with choosed verbose; there are in my Makeconfig.mk already included. Some:
v_exec = $(V$(strip $(1)))$(strip $(2))
print = #echo -e '$(LEAD_SUB_STR) $(strip $(1))'
print_white_init= #echo -e '$(subst PATTERN,$(strip $(1)),$(WHITE_INIT)) $(strip $(2))'
print_white_done= #echo -e '$(subst PATTERN,$(strip $(1)),$(WHITE_DONE)) $(strip $(2))'
$(HDAIMG) has check-env as a prerequisite, and Make always thinks that check-env must be rebuilt, because check-env is not actually a file that exists. Therefore Make decides that $(HDAIMG) must be rebuilt.
It would make more sense to perform the check as the first command in the rule, rather than as a prerequisite.

Eliminating duplication in a Makefile

This is the same Makefile as in Makefile command substitution problem, but a different question.
REBAR=./rebar
REBAR_DEBUG=$(REBAR) -C rebar.debug.config
REBAR_COMPILE=$(REBAR) get-deps compile
LAST_CONFIG:=$(shell cat config.tmp)
PLT=dialyzer/sqlite3.plt
all: config_normal compile
compile:
$(REBAR_COMPILE)
test:
$(REBAR_COMPILE) eunit
clean:
-rm -rf deps ebin priv doc/* .eunit c_src/*.o
docs:
$(REBAR_COMPILE) doc
static: config_debug
$(REBAR_DEBUG) get-deps compile
ifeq ($(wildcard $(PLT)),)
dialyzer --build_plt --apps kernel stdlib erts --output_plt $(PLT)
else
dialyzer --plt $(PLT) -r ebin
endif
cross_compile: config_cross
$(REBAR_COMPILE) -C rebar.cross_compile.config
valgrind: clean
$(REBAR_DEBUG) get-deps compile
valgrind --tool=memcheck --leak-check=yes --num-callers=20 ./test.sh
ifeq ($(LAST_CONFIG),normal)
config_normal:
echo "$(LAST_CONFIG) == normal"
else
config_normal: clean
echo "$(LAST_CONFIG) != normal"
rm -f config.tmp
echo "normal" > config.tmp
endif
ifeq ($(LAST_CONFIG),debug)
config_debug: ;
else
config_debug: clean
rm -f config.tmp
echo "debug" > config.tmp
endif
ifeq ($(LAST_CONFIG),cross)
config_cross: ;
else
config_cross: clean
rm -f config.tmp
echo "cross" > config.tmp
endif
.PHONY: all compile test clean docs static valgrind config_normal config_debug config_cross
How can I eliminate (or significantly decrease) the duplication between config_normal, config_debug, and config_cross?
If you are using at least version 3.80 of GNU make, you can use $(eval) to create the rules from a template, like this:
define templ
ifeq ($$(LAST_CONFIG),$(config))
config_$(config):
echo "$$(LAST_CONFIG) == $(config)"
else
config_$(config):
echo "$$(LAST_CONFIG) != $(config)"
rm -f config.tmp
echo "$(config)" > config.tmp
endif
endef
$(foreach config,normal debug cross,$(eval $(templ)))

Resources