I'd like to remove some duplication from my Makefile. It takes files with lists of hosts and concatenates them into larger groups. For example, this would generate hosts/west.txt from the content of hosts/oregon.txt hosts/washington.txt hosts/idaho.txt and so on.
ALLFILE = hosts/all.txt
WESTFILE = hosts/west.txt
EASTFILE = hosts/east.txt
GENERATEDFILES = $(ALLFILE) $(WESTFILE) $(EASTFILE)
ALLFILES = $(filter-out $(GENERATEDFILES), $(wildcard hosts/*.txt))
WESTFILES = hosts/oregon.txt hosts/washington.txt hosts/idaho.txt
EASTFILES = hosts/new_york.txt hosts/virginia.txt
$(ALLFILE) : $(ALLFILES)
#echo '# DO NOT EDIT' > $#
#echo '# Edit individual host/ files instead.' >> $#
#echo '# Regenerate with `make`.' >> $#
#echo '' >> $#
cat $^ >> $#
$(WESTFILE) : $(WESTFILES)
#echo '# DO NOT EDIT' > $#
#echo '# Edit individual host/ files instead.' >> $#
#echo '# Regenerate with `make`.' >> $#
#echo '' >> $#
cat $^ >> $#
$(EASTFILE) : $(EASTFILES)
#echo '# DO NOT EDIT' > $#
#echo '# Edit individual host/ files instead.' >> $#
#echo '# Regenerate with `make`.' >> $#
#echo '' >> $#
cat $^ >> $#
I'd like to remove that redundant code. Either by making all the target/dependency combos share one rule, or by some other means.
$(ALLFILE) : $(ALLFILES)
$(WESTFILE) : $(WESTFILES)
$(EASTFILE) : $(EASTFILES)
$(ALLFILE) $(WESTFILE) $(EASTFILE):
#echo '# DO NOT EDIT' > $#
#echo '# Edit individual host/ files instead.' >> $#
#echo '# Regenerate with `make`.' >> $#
#echo '' >> $#
cat $^ >> $#
Related
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.
I am new in makefle, I had need array in makefile then I found that I can achieve that having variable that their items separated with spaces, and then iterating over it.
Now I want something like map(key,value) pair to store values with keys.
Question: can I have that in makefile ?
Thanks in advance..
You can use token pasting for that:
VAR_FOO_KEY := FOO_VAL
VAR_BAR_KEY := BAR_VAL
#example lookup:
KEY := FOO_KEY
LOOKUP_VAL := $(VAR_$(KEY))
Make a file named KV.mk with the following function definitions:
# KV(3 args): in a dictionary directory record a key value pair in a file
# KV(2 args): in a dictionary directory recover value associated with a key
# KV(1 arg ): remove a dictionary directory
# KV(0 arg ): remove all dictionary directories
define KV
$(if $4,$(error K0123.mk: args>3 forbidden), \
$(if $3,mkdir -p $1.key; echo "$3" > $1.key/$2, \
$(if $2,`cat $1.key/$2`, \
$(if $1,rm -fr $1.key/*, \
rm -fr *.key \
))))
endef
Then make a client Makefile with the following contents:
include KV.mk
all: hello.cpp hello
.PHONY: hello.cpp
hello.cpp:
#echo '#include <iostream>' > $#
#echo 'int main(int argc, char** argv)' >> $#
#echo '{ std::cout << "hello" << std::endl; return 0; }' >> $#
hello: hello.cpp
#$(call KV,$#)
#$(call KV,$#,compiler,g++)
#$(call KV,$#,compiler) -o hello hello.cpp
#./$#
clean:
#rm -f hello.cpp hello
#$(call KV)
Copy these two files into an empty directory and run "make".
This method offers much liberty in having multiple dictionaries
with multiple key/value pairs per dictionary.
One dictionary may be used for the entire Makefile.
Another dictionary may be used for all rules having common needs.
Individual dictionaries may be used for each rule.
Simplifying one more step, here is a shell script encapsulating the KV idea.
#!/bin/bash
usage() {
cat <<USAGE
KV(1) -- syre(tm) user command
NAME
KV - key value store management for Makefile
SYNOPSIS
(1) KV [--help]
(2) KV [--clear]
(3) KV [OPTION]... {key}
(4) KV [OPTION]... {key}={value}
1: display usage (this text)
2: clear all dictionaries (make clean)
3: print value associated with key
4: store key value pair in the dictionary and key file
USAGE
if [ "" != "$1" ] ; then echo "KV error: $1"; fi
exit 0
}
if [ $1 = "--help" ] ; then usage;
elif [ $1 = "--clear" ] ; then rm -fr *.key;
else
case $# in
3) mkdir -p $1.key; echo "$3" > $1.key/$2;;
2) cat $1.key/$2;;
1) rm -fr $1.key;;
*) usage "KV error: 1, 2, or 3 args required";;
esac
fi
and here is the same Makefile showing that simplification.
all: hello.cpp hello
.PHONY: hello.cpp
hello.cpp:
#echo '#include <iostream>' > $#
#echo 'int main(int argc, char** argv)' >> $#
#echo '{ std::cout << "hello" << std::endl; return 0; }' >> $#
hello: hello.cpp
#./KV $#
#./KV $# compiler g++
#`./KV $# compiler` -o hello hello.cpp
#./$#
clean:
#rm -f hello.cpp hello
#./KV --clear
I want to write this into a file:
-MF"$(#:%.o=%.d)" -MT"$(#)" -o "$#" "$<"
but when I echo this into a file, with "", or '', the variables will expand, how can I prevent the expansion and write it as is?
P.S. echo '-MF"$(#:%.o=%.d)" -MT"$(#)" -o "$#" "$<"' is called in a makefile.
in my makefile, I let's say I have:
all:
echo '-MF"$(#:%.o=%.d)" -MT"$(#)" -o "$#" "$<"' > $file
what I see in the file is
-MF"all" -MT"all" -o "all" ""
Use $$ to put a dollar sign into a Makefile recipe
all:
echo '-MF"$$(#:%.o=%.d)" -MT"$$(#)" -o "$$#" "$$<"' > $file
I have a template file, and the configuration script transforms it into the final Makefile. What shoud be the extension of the template?
Example template:
EXEC = $var1
PATH = $var2
CC = $var3
STRIP = $var4
MKDIR = $var5
COPY = $var6
.PHONY: all
all: $(EXEC)
$(EXEC): source.c
$(CC) $< -o $#
$(STRIP) $#
$(MKDIR) $(PATH)
$(COPY) $# $(PATH)/
The variables are replaced by the configuration script.
How do I change variable value dynamically in makefile?
I want to call specific target depending on value of macro.
For eg.
STATIC_LIB = TRUE
all: makelib $(var)
makelib:
ifeq (${STATIC_LIB}, TRUE)
var=staticlib
else
var=sharedlib
endif
Here i want to call either staticlib target or sharedlib target depending on the value of target.
Code:
.SUFFIXES: .cpp .hpp
# Programs
SHELL = bash
CC = g++
LD = ld
RM = rm
ECHO = /bin/echo
CAT = cat
PRINTF = printf
SED = sed
DOXYGEN = doxygen
STATIC_LIB = TRUE
######################################
# Project Name (generate executable with this name)
TARGET = cs296_exe_28
# Project Paths
PROJECT_ROOT=$(HOME)/Desktop/cs296/cs296_base_code
EXTERNAL_ROOT=$(PROJECT_ROOT)/external
SRCDIR = $(PROJECT_ROOT)/src
OBJDIR = $(PROJECT_ROOT)/myobjs
BINDIR = $(PROJECT_ROOT)/mybins
LIBDIR = $(PROJECT_ROOT)/mylibs
DOCDIR = $(PROJECT_ROOT)/doc
# Library Paths
BOX2D_ROOT=$(EXTERNAL_ROOT)
GLUI_ROOT=/usr
GL_ROOT=/usr
#Libraries
LIBS = -lBox2D -lglui -lglut -lGLU -lGL
# Compiler and Linker flags
CPPFLAGS =-g -O3 -Wall
CPPFLAGS+=-I $(BOX2D_ROOT)/include -I $(GLUI_ROOT)/include
LDFLAGS+=-L $(BOX2D_ROOT)/lib -L $(GLUI_ROOT)/lib
######################################
NO_COLOR=\e[0m
OK_COLOR=\e[1;32m
ERR_COLOR=\e[1;31m
WARN_COLOR=\e[1;33m
MESG_COLOR=\e[1;34m
FILE_COLOR=\e[1;37m
OK_STRING="[OK]"
ERR_STRING="[ERRORS]"
WARN_STRING="[WARNINGS]"
OK_FMT="${OK_COLOR}%30s\n${NO_COLOR}"
ERR_FMT="${ERR_COLOR}%30s\n${NO_COLOR}"
WARN_FMT="${WARN_COLOR}%30s\n${NO_COLOR}"
######################################
SRCS := $(wildcard $(SRCDIR)/*.cpp)
INCS := $(wildcard $(SRCDIR)/*.hpp)
OBJS := $(SRCS:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o)
OBJSNMAIN := $(filter-out $(OBJDIR)/main.o,$(OBJS))
.PHONY: all setup doc clean distclean
ifndef STATIC_LIB
makelib : sharedlib # line 5
else ifeq ( ${STATIC_LIB}, TRUE )
makelib : staticlib # line 7
else
makelib : sharedlib # line 9
endif
all: setup makelib exelib
setup:
#$(ECHO) "Setting up compilation..."
#mkdir -p myobjs
#mkdir -p mybins
#mkdir -p mylibs
$(BINDIR)/$(TARGET): $(OBJS)
#$(PRINTF) "$(MESG_COLOR)Building executable:$(NO_COLOR) $(FILE_COLOR) %16s$(NO_COLOR)" "$(notdir $#)"
#$(CC) -o $# $(LDFLAGS) $(OBJS) $(LIBS) 2> temp.log || touch temp.err
#if test -e temp.err; \
then $(PRINTF) $(ERR_FMT) $(ERR_STRING) && $(CAT) temp.log; \
elif test -s temp.log; \
then $(PRINTF) $(WARN_FMT) $(WARN_STRING) && $(CAT) temp.log; \
else $(PRINTF) $(OK_FMT) $(OK_STRING); \
fi;
#$(RM) -f temp.log temp.err
-include $(OBJS:.o=.d)
$(OBJS): $(OBJDIR)/%.o : $(SRCDIR)/%.cpp
#$(PRINTF) "$(MESG_COLOR)Compiling: $(NO_COLOR) $(FILE_COLOR) %25s$(NO_COLOR)" "$(notdir $<)"
#$(CC) $(CPPFLAGS) -c $< -o $# -MD 2> temp.log || touch temp.err
#if test -e temp.err; \
then $(PRINTF) $(ERR_FMT) $(ERR_STRING) && $(CAT) temp.log; \
elif test -s temp.log; \
then $(PRINTF) $(WARN_FMT) $(WARN_STRING) && $(CAT) temp.log; \
else printf "${OK_COLOR}%30s\n${NO_COLOR}" "[OK]"; \
fi;
#$(RM) -f temp.log temp.err
exe: $(BINDIR)/$(TARGET)
makelib:
ifeq (${STATIC_LIB}, TRUE)
LIB_TYPE = staticlib
else
LIB_TYPE = sharedlib
endif
staticlib:
#$(ECHO) "static"
#ar rcs $(LIBDIR)/libCS296.a $(OBJSNMAIN)
sharedlib:
#$(ECHO) "shared"
#$(CC) -shared -o $(LIBDIR)/libCS296.so $(OBJSNMAIN)
exelib:
$(CC) -L $(LIBDIR) $(LDFLAGS) -o cs296_exelib_28 $(OBJDIR)/main.o -lCS296 $(LIBS)
doc:
#$(ECHO) -n "Generating Doxygen Documentation ... "
#$(RM) -rf doc/html
#$(DOXYGEN) $(DOCDIR)/Doxyfile 2 > /dev/null
#$(ECHO) "Done"
clean:
#$(ECHO) -n "Cleaning up..."
#$(RM) -rf $(OBJDIR) *~ $(DEPS) $(SRCDIR)/*~ $(LIBDIR)
#$(ECHO) "Done"
distclean: clean
#$(RM) -rf $(BINDIR) $(DOCDIR)/html
The solution that you came up with would not work since $(var) would be evaluated before makelib is executed.
What you could do instead is to define makelib conditionally:
STATIC_LIB = TRUE
all : makelib
ifndef STATIC_LIB
makelib : sharedlib # line 5
else ifeq (${STATIC_LIB}, TRUE)
makelib : staticlib # line 7
else
makelib : sharedlib # line 9
endif
An ifndef conditional directive saves from an error that would occur in case STATIC_LIB is not defined.
You can set var value before rule definition:
Edited, Makefile:
STATIC_LIB = TRUE
ifeq (${STATIC_LIB}, TRUE)
var=staticlib
else
var=sharedlib
endif
all: $(var)
staticlib:
echo "staticlib"
sharedlib:
echo "sharedlib"
Test:
make -f Makefile
Output:
echo "staticlib"
staticlib