Looking for ways to assign a variable in a Makefile based on the existence of a directory.
Eg: (pseudo code)
if dir "src/$(project)" exists
SOURCE_DIR := src/$(project)
else
SOURCE_DIR := src/default
test.o: $SOURCE_DIR/test.c
gcc -c -o $# $<
What's the best way to achieve the above
As your source directory will contain files and a non-existing directory will not, you can utilize the GNU make internal function $(wildcard):
project := test
# returns all entries under src/$(project) directory or empty string
ifneq ($(wildcard src/$(project)/*),)
SOURCE_DIR := src/$(project)
else
SOURCE_DIR := src/default
endif
.PHONY: all
all:
#echo $(SOURCE_DIR)
Test run
$ ls src/test/
a.c
$ make
src/test
$ rm -rf src/test
$ make
src/default
BONUS: it might be more readable to rewrite the conditional like this:
SOURCE_DIR := src/$(project)
ifeq ($(wildcard $(SOURCE_DIR)/*),)
# fall back to default directory
SOURCE_DIR := src/default
endif
UPDATE 2: if you don't want to rely on the existence of any files in the directory, you can also test the directory name directly. A directory always has an entry ., because it points to itself:
SOURCE_DIR := src/$(project)
ifeq ($(wildcard $(SOURCE_DIR)/.),)
# fall back to default directory
SOURCE_DIR := src/default
endif
UPDATE 3: adding a check that $(project) is set:
SOURCE_DIR := src/$(project)
ifeq ($(strip $(project)),)
# fall back to default directory
SOURCE_DIR := src/default
else ifeq ($(wildcard $(SOURCE_DIR)/.),)
# fall back to default directory
SOURCE_DIR := src/default
endif
or if you prefer makefile Golfing (thanks #MadScientist for the suggestion)
SOURCE_DIR := $(or $(and $(project),$(wildcard src/$(project)/.)),src/default)
$(and) result
if $(project) is an empty string: empty string
if src/$(project) is not a directory: empty string
otherwise: src/$(project)/., which is equivalent to src/$(project)
$(or) result
if $(and) returns empty string: src/default
otherwise: the string returned by $(and)
CAVEAT: the above listed tests will fail if $(project) contains white space.
Try this.
SOURCE_DIR := src/$(shell test -d src/"$(project)" && echo "$(project)" || echo default)
Related
For my Makefile, my prerequisite is of the form
$(Root)a/b/c/d/File.source
and I'd like to target
a/c.zip
knowing that there are multiple folders for a and c, that b is fixed, that d can vary, but that the source file is always called File.source.
Taking inspiration from this answer, I've tried
As:= $(notdir $(shell find $(Root) -mindepth 1 -maxdepth 1 -type d))
define RULE
$1/%.zip: $1/b/%/*/File.source
echo "Test"
endef
$(foreach a, $(As), $(eval $(call RULE, $(a))))
But it does not seem to work.
I think this could be relatively simple, given the fact how patterns match. Having a rule
%.zip: b/%.foo
will cause make a/c.zip to search for a/b/c.foo file. With this in mind we can write a quite simple Makefile:
$ cat Makefile
# Extract directories that contain File.source files
sourcedirs := $(dir $(wildcard $(ROOT)*/b/*/*/File.source))
# Generate archive names: drop the /b/ part and add .zip extension
archives := $(patsubst %/,%.zip,$(subst /b/,/,$(dir $(sourcedirs:/=))))
%.zip: b/%/*/File.source
echo Making $# from $<
all: $(archives)
The output:
$ find . -name File.source
./a/b/c/d/File.source
./foo/b/bar/baz/File.source
./foo/b/cfg/d/File.source
$ make -s
Making foo/bar.zip from foo/b/bar/baz/File.source
Making foo/cfg.zip from foo/b/cfg/d/File.source
Making a/c.zip from a/b/c/d/File.source
Let's first find all File.source files that match the $(Root)*/b/*/*/File.source pattern:
FILES := $(shell find $(Root)*/b/* -maxdepth 2 -mindepth 2 -type f -name File.source)
Then, let's create a macro for each of them (I over-exaggerated the number of intermediate variables for easier understanding, you can simplify a bit, if you wish):
# $(1): File.source path
define MY_RULE
$(1)-dirs := $$(subst /, ,$(1))
$(1)-Roota := $$(word 1,$$($(1)-dirs))
$(1)-a := $$(patsubst $$(Root)%,%,$$($(1)-Roota))
$(1)-c := $$(word 3,$$($(1)-dirs))
$(1)-target := $$($(1)-a)/$$($(1)-c).zip
$$($(1)-target): $(1)
#echo "Test for $$< (target $$#)"
endef
And finally, let's call the macro on each File.source file:
$(foreach f,$(FILES),$(eval $(call MY_RULE,$(f))))
All in all, plus a test phony default goal for easier testing:
FILES := $(shell find $(Root)*/b/* -maxdepth 2 -mindepth 2 -type f -name File.source)
.PHONY: test
.DEFAULTGOAL := test
# $(1): File.source path
define MY_RULE
$(1)-dirs := $$(subst /, ,$(1))
$(1)-Roota := $$(word 1,$$($(1)-dirs))
$(1)-a := $$(patsubst $$(Root)%,%,$$($(1)-Roota))
$(1)-c := $$(word 3,$$($(1)-dirs))
$(1)-target := $$($(1)-a)/$$($(1)-c).zip
$$($(1)-target): $(1)
#echo "Test for $$< (target $$#)"
test: $$($(1)-target)
endef
$(foreach f,$(FILES),$(eval $(call MY_RULE,$(f))))
Note: there is a possibility that you have more than one File.source file with the same corresponding target, because of the d part of your pattern. If this happens GNU make will issue warnings and use only the last encountered rule for each such target. There are ways to raise an error, if you prefer, but as all this is already quite complicated, let's leave it as an exercise on make conditionals.
I have a makefile that gives me the source files in a hierarchy.
SRCS := $(wildcard $(SRC_DIR)/*.c $(SRC_DIR)/*/*.c)
gives me
./src/main.c ./src/add/add.c ./src/sub/sub.c
I want to flatten the object files into a single "obj" directory.
Of course
OBJS := $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.obj)
gives me
./obj/main.obj ./obj/add/add.obj ./obj/sub/sub.obj
instead of desired
./obj/main.obj ./obj/add.obj ./obj/sub.obj
Question: How do I get rid of additional source directory levels?
Never had to use complex substitution so far. My intuitive try with additional "/%" in pattern
# won't work as expected:
OBJS := $(SRCS:$(SRC_DIR)/%/%.c=$(OBJ_DIR)/%.obj)
produces no meaningful result (${OBJS} becomes same as ${SRCS}).
All examples I found so far only have single occurance of "%" in match pattern.
MAK_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
SRC_DIR = $(MAK_DIR)./src
OBJ_DIR = $(MAK_DIR)./obj
# gives: ./src/main.c ./src/add/add.c ./src/sub/sub.c
SRCS := $(wildcard $(SRC_DIR)/*.c $(SRC_DIR)/*/*.c)
# gives: ./obj/main.obj ./obj/add/add.obj ./obj/sub/sub.obj
OBJS := $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.obj)
.PHONY : all
all :
#echo $(SRCS)
#echo $(OBJS)
Just use the notdir function, and also patsubst, more powerful (and that I find easier to understand than the shorthand):
OBJS := $(patsubst %.c,$(OBJ_DIR)/%.obj,$(notdir $(SRCS)))
But it is not the whole story because later on you will probably want to do something like:
$(OBJ_DIR)/%.obj: $(SRC_DIR)/%.c
which will not work any more. Not mentioning the fact that you could have several source files with the same name in different directories. Assuming you do not have such names conflicts you can generate all your dependencies using foreach-eval-call:
MAK_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
SRC_DIR := $(MAK_DIR)./src
OBJ_DIR := $(MAK_DIR)./obj
SRCS := $(shell find $(MAK_DIR) -type f -name '*.c')
OBJS := $(patsubst %.c,$(OBJ_DIR)/%.obj,$(notdir $(SRCS)))
.PHONY: objs
objs: $(OBJS)
# $(1): source file
define DEP_rule
$(1)-obj := $$(patsubst %.c,$$(OBJ_DIR)/%.obj,$$(notdir $(1)))
$(1)-dep := $$(patsubst %.c,$$(OBJ_DIR)/%.d,$$(notdir $(1)))
$$($(1)-obj): $(1) $$($(1)-dep)
endef
$(foreach src,$(SRCS),$(eval $(call DEP_rule,$(src))))
Just remember that the DEP_rule macro gets expanded twice, thus the $$.
Target_2 is unable to get value of eval set in target_1,
The destination eval turns out empty handed, i.e no value is assigned to it
.PHONY:target_1
target_1:
ifeq ($(RQM_SETUP),ci)
$(eval efs-mount-path := $(shell mktemp -d))
else
#echo "$(VAR)"
endif
.PHONY:target_2
target_2:
$(MAKE) target_1 VAR=dags
$(eval destination := $(efs-mount-path))
I am trying to build a dummy C project using GNU Make. I use the following rules to build the object files from the c files and the output from the object files :
AT := #
#Initial tools to get the absolute paths.
TOOLSDIR := ./tools/bin
SHELL := $(TOOLSDIR)/sh
CYGPATH := $(TOOLSDIR)/cygpath
# Defining used tools
TOOLSDIR := $(shell $(CYGPATH) -am "$(TOOLSDIR)")
SCRIPTDIR := $(shell $(CYGPATH) -am "$(SCRIPTDIR)")
LIBDIR := $(shell $(CYGPATH) -am "$(LIBDIR)")
#Defining Env Variable responsible for Removing non-ansi characters in error output
LC_ALL=C
SHELL := $(shell $(CYGPATH) -am "$(SHELL)")
CYGPATH := $(shell $(CYGPATH) -am "$(CYGPATH)")
TEST := $(shell $(CYGPATH) -am "$(TOOLSDIR)/test")
RM := $(shell $(CYGPATH) -am "$(TOOLSDIR)/rm") -f
PERL := $(shell $(CYGPATH) -am "$(TOOLSDIR)/perl")
FIND := $(shell $(CYGPATH) -am "$(TOOLSDIR)/find")
MKDIR := $(shell $(CYGPATH) -am "$(TOOLSDIR)/mkdir")
PERL5LIB := $(LIBDIR)/perl5/site_perl/5.14/x86_64-cygwin-threads;$(LIBDIR)/perl5/site_perl/5.14;$(LIBDIR)/perl5/5.14/x86_64-cygwin-threads;$(LIBDIR)/perl5/5.14
# Defining perl script to convert dependencies genereted by the compiler to unix path
FIXDEPS_SCRIPT := "$(SCRIPTDIR)/fixdeps.pl"
ZAPEXEC_SCRIPT := "$(SCRIPTDIR)/ZAP_Command.pl"
DEPFIX := $(PERL) $(FIXDEPS_SCRIPT)
ZAPEXE := $(PERL) $(ZAPEXEC_SCRIPT)
CREATE_IAR_PROJ := $(PERL) $(IARWKSP_SCRIPT)
COMP_DIR :=C:\ghs\comp_201355
OBJDIR :=.
CC := "$(COMP_DIR)/ccrh850.exe"
AS := "$(COMP_DIR)/ccrh850.exe"
LD := "$(COMP_DIR)/ccrh580.exe"
GSREC := "$(COMP_DIR)/gsrec.exe"
GMEM := "$(COMP_DIR)/gmemfile.exe"
CEXT :=c
CCEXT :=C
ASMEXTC := S
ASMEXTS := s
ASMEXT := 850
ASMEXT_DEFAULT := asm
DBGEXT := dla
DBGEXT_DEFAULT := dnm
OBJEXT := o
EXEEXT := elf
LSTEXT := lst
DEPEXT := dd
INCOPT := -I
EXEMAP := ./exe/PROJECT_NAME.map
CFLAGS :=-c -G -Onone -no_callt -sda=all -large_sda -reserve_r2 -Wundef --short_enum -dual_debug \
-delete -preprocess_assembly_files --no_wrap_diagnostics -registermode=32 -keeptempfiles -prepare_dispose -ansi \
-full_debug_info -noobj -Wimplicit-int -Wshadow -Wtrigraphs -inline_prologue -DOS_MEMMAP=0 -DOS_MULTICORE=OS_SINGLE \
-DOS_USE_CLZ_QUEUE_ALGORITHM=1 -DOS_DEVELOPMENT_SANITY_CHECKS=0 -DOS_KERNEL_TYPE=OS_FUNCTION_CALL -DOS_USE_TRACE=0 -MD \
-DAUTOSAR_USED -DRENESAS -DF1K -DR7F701583xAFP --long_long -cpu=rh850 -DTS_ARCH_FAMILY=TS_RH850 \
-DTS_ARCH_DERIVATE=TS_RH850F1L -DRH850F1L=1 -DOS_CPU=OS_RH850F1L -DOS_TOOL=OS_ghs -DOS_ARCH=OS_RH850
VPATH :=./ ./obj ./src ./exe
INCLUDES :=-I"./" -I"./obj" -I"./src" -I"./exe"
OBJECTS :=./obj/main.o
.PHONY: all release debug
EXEC := ./exe/PROJECT_NAME.hex
EXEOUT := ./exe/PROJECT_NAME.out
all: debug release
release: $(EXEC)
debug: release
$(EXEC): $(EXEOUT)
#echo '************************************'
#echo 'Using GSREC to Convert .out to .hex'
$(AT)$(GSREC) -o$(EXEC) $(EXEOUT)
#echo 'Generation Done'
#echo '************************************'
$(EXEOUT): $(OBJECTS)
#echo '************************************'
#echo 'Building .out file'
#echo $(EXEOUT)
$(AT)$(CC) $(LDFLAGS) -o $(EXEOUT) $(OBJECTS)
#echo '************************************'
$(OBJDIR)/%.$(OBJEXT): %.$(CEXT)
#echo 'Compiling: $<'
$(AT)$(CC) $(CFLAGS) $(INCLUDES) -filetype.c $(shell $(CYGPATH) -am "$<") -o $#
The output when I execute the target all is as follows :
make: *** No rule to make target 'obj/main.o', needed by 'exe/PROJECT_NAME.out'. Stop.
The main.c source file has the following code :
#include <stdio.h>
#include <stdlib.h>
#include "alpha.c"
void second(void);
int main()
{
printf("Second module, I send you greetings!n");
second();
return 0;
}
The alpha.c source file has the following code :
#include <stdio.h>
void second(void)
{
puts("Glad to be here!");
}
Where $(OBJECTS) is the relative path of all the object files that are supposed to be generated and $(EXEC) and $(EXEOUT) are the outputs. The VPATH contains relative paths. All the needed prerequisites (.c and.h files) can be found among the paths in VPATH and in INCLUDES in the compilation command. That is the exact code I use to build this dummy project which contains only two .c files main.c and alpha.c and main.c uses alpha.c. When the source files are in the same folder as the makefile this dummy project builds, but when I put the source files in the src folder and create a obj folder for the object files to be generated in and an exe folder for the final output to be generated in previously mentioned error appears. What could be the reason for the previously mentioned error?
#Beta is right that it's generally a bad idea to dump a huge makefile into StackOverflow and ask someone to fix it. You should instead try to reduce the problem as much as possible.
However in this case, your problem is here:
OBJDIR :=.
OBJECTS :=./obj/main.o
$(EXEOUT): $(OBJECTS)
...
$(OBJDIR)/%.$(OBJEXT): %.$(CEXT)
...
So after all the variables have been expanded, you get these rules:
./exe/PROJECT_NAME.out: ./obj/main.o
...
./%.o: %.c
...
Well, when make tries to match the pattern ./%.o with the target to be built ./obj/main.o, the stem % will match obj/main. Then it will try to find %.c which resolves to obj/main.c, which doesn't exist.
You should be using the OBJDIR variable in your OBJECTS variable assignment, like:
OBJECTS :=$(OBJDIR)/main.o
then it will work.
CFG_DATA_FILES := $(wildcard ../src/config/cfg_file*.csv)
CFG_DATA_MEM_FILES := $(addprefix bin/,$(notdir $(CFG_DATA_FILES:.csv=.mem)))
CFG_DATA_BIN_FILES := $(addprefix bin/,$(notdir $(CFG_DATA_MEM_FILES:.mem=.bin)))
COMBINED_BIN_FILES := $(subst cfg_file-,fw_,$(CFG_DATA_BIN_FILES))
COMBINED_BIN_FILES := $(subst .bin,_combined.bin,$(COMBINED_BIN_FILES))
ifdef CUSTOM_CONFIG
rom: build_images create_custom_binaries
else
rom: build_images create_binaries
endif
chk_custom_cfg:
ifdef CUSTOM_CONFIG
export CUSTOM_CONFIG
$(eval CUST_CFG_DATA_FILES=$(CUSTOM_CONFIG))
export CUST_CFG_DATA_FILES
$(eval CUST_CFG_DATA_MEM_FILES=$(addprefix bin/,$(notdir $(CUST_CFG_DATA_FILES:.csv=.mem))))
export CUST_CFG_DATA_MEM_FILES
$(eval CUST_CFG_DATA_BIN_FILES=$(addprefix bin/,$(notdir $(CUST_CFG_DATA_MEM_FILES:.mem=.bin))))
export CUST_CFG_DATA_BIN_FILES
$(eval CUST_COMBINED_BIN_FILES=$(subst .bin,_combined.bin,$(CUST_CFG_DATA_BIN_FILES)))
export CUST_COMBINED_BIN_FILES
#echo existing cfg files $(CUST_CFG_DATA_FILES)
#echo existing mem files $(CUST_CFG_DATA_MEM_FILES)
#echo existing bin files $(CUST_CFG_DATA_BIN_FILES)
#echo existing combined files $(CUST_COMBINED_BIN_FILES)
endif
build_images: copy_ops_imgs
create_binaries: config $(COMBINED_BIN_FILES)
create_custom_binaries: chk_custom_cfg cust_config
#echo custom config bin files $(CUST_CFG_DATA_BIN_FILES)
config: boot_partn_map_cfg $(CFG_DATA_BIN_FILES)
#echo Removing temporary files
#(cd bin; rm -rf *.mem)
cust_config: boot_partn_map_cfg $(CUST_CFG_DATA_BIN_FILES)
#echo Removing temporary files
#(cd bin; rm -rf *.mem)
$(CFG_DATA_BIN_FILES): $(CFG_DATA_FILES)
#echo Generating config data image $(subst /bin,,$#)
$(CUST_CFG_DATA_BIN_FILES):$(CUST_CFG_DATA_FILES)
#echo Generating config data image $(subst /bin,,$#)
In the above make file :
calling "make CUSTOM_CONFIG=../src/config/custom.config rom" expect $(CUST_CFG_DATA_BIN_FILES) build for ../src/config/custom.config in target cust_config , but I see the environment variable update properly but the target cust_config will not build $(CUST_CFG_DATA_BIN_FILES).
Am I missing anything here, My goal is build different binaries based on the input CUSTOM_CONFIG