Makefile with multiple values for a parameter - makefile

Currently, I am working on a makefile that takes a parameter "CLASS=xxx" and then compiles and does stuff with that value.
In the end, it runs an application ($APP) on a bunch of files.
I enter this command:
make default CLASS=Test_UART
and the makefile processes it thusly:
pc: $(APP)
make -C BUILDENV CLASS=$(CLASS) BUILD=just_filelist OUTPUT=filelist.txt SKIPSELF=yes
../classCvt/classCvt <./Applications/$(CLASS).class> ./Applications/$(CLASS).ujc
time -p ./$(APP) ./Applications/$(CLASS).ujc `cat filelist.txt`
Hence it calls a makefile in my BUILDENV folder which does the following:
#USAGE: make -C <PATH_TO_THIS_FILES_PARENT_DIR> CLASS=<MY_JAVA_FILE_NAME_WITHOUT_JAVA_EXTENSION> OUT=<OUTPUT_FILE_NAME>
SELF := $(dir $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))
CLASS ?= PLEASE_SPECIFY_CLASS_PARAM
DIR := $(PWD)#print working directory
CCVT ?= $(SELF)/../../classCvt/classCvt
TOBIN ?= $(SELF)/../../classCvt/tobin
OUTPUT ?=
### Comment: Defining CMD.
ifeq ($(BUILD), just_filelist)
CMD = echo
else
ifeq ($(BUILD), PC)
CMD = echo
else
ifeq ($(BUILD), unopt)
CMD = $(TOBIN)
else
### Comment: Optimized CMD = tobin -c ccvt
CMD = $(TOBIN) -c $(CCVT)
endif
endif
endif
ifeq ($(OUTPUT), )
OUT = &1
else
OUT = $(DIR)/$(OUTPUT)
endif
ifeq ($(SKIPSELF), yes)
MYCLASS =
else
MYCLASS = $(DIR)/Applications/$(CLASS).class
endif
all:
CLASSPATH=$(SELF)/RT/real:$(SELF)/RT/fake:$(DIR) javac $(DIR)/Applications/$(CLASS).java
find $(SELF)/RT/real -iname "*.class" -type f > $(SELF)/files
ls $(DIR)/Applications/*.class | grep -v "$(CLASS)\.class" >> $(SELF)/files || true
cat $(SELF)/files | xargs $(CMD) $(MYCLASS) >$(OUT)
rm -f $(SELF)/files
What I would like to do is give a command like:
make default CLASS=Test1,Test2,Test3
and the makefile to process it for the 3 classes and put the given classes in a .txt and the default classes in a different .txt, something like this like this:
pc: $(APP)
make -C BUILDENV default_classes BUILD=list_default_classes OUTPUT=list_default_classes.txt
# make -C BUILDENV given_classes BUILD=list_given_classes OUTPUT=list_given_classes.txt CLASS=$(CLASS) SKIPSELF=yes
../classCvt/classCvt `cat list_given_classes.txt`./Applications/$(CLASS).ujc
#here the list_given_classes should now contain the .ujc files
time -p ./$(APP) `cat list_given_classes.txt` `cat list_default_classes.txt`
and for the makefile in the BUILDENV, I expect something like:
default_classes:
CLASSPATH=$(SELF)/RT/real:$(SELF)/RT/fake:$(DIR)
find $(SELF)/RT/real -iname "*.class" -type f > $(SELF)/files
ls $(DIR)/Applications/*.class | grep -v "$(CLASS1)\.class" "$(CLASS2)\.class">> $(SELF)/files || true
cat $(SELF)/files | xargs $(CMD) >$(OUT)
rm -f $(SELF)/files
given_classes:
javac $(DIR)/Applications/$(CLASS).java
find $(SELF)/RT/real -iname "*.class" -type f > $(SELF)/files
ls $(DIR)/Applications/*.class | grep -v "$(CLASS)\.class" >> $(SELF)/files || true
cat $(SELF)/files | xargs $(CMD) $(MYCLASS) >$(OUT)
rm -f $(SELF)/files
However, I'm not sure how to do this for a CLASS parameter containing multiple classes.
I'm thinking to try and parse the Test1,Test2,Test3 value into a list of 1,2,3 and then iterating over it. But no clue if this is a good way and even on how to do it.
What do you guys suggest?

Pretty way:
pc: $(APP)
define BUILD_CLASS
pc: pc-$(CLASS_SPLIT)
pc-$(CLASS_SPLIT):
make -C BUILDENV CLASS=$(CLASS_SPLIT) BUILD=just_filelist OUTPUT=filelist.txt SKIPSELF=yes
../classCvt/classCvt <./Applications/$(CLASS_SPLIT).class> ./Applications/$(CLASS_SPLIT).ujc
time -p ./$(APP) ./Applications/$(CLASS_SPLIT).ujc `cat filelist.txt`
endef
CLASSES := $(shell echo $(CLASS) | tr ',' ' ')
$(foreach CLASS_SPLIT, $(CLASSES), $(eval $(BUILD_CLASS)))

Simple way:
pc: $(APP)
$(foreach C, $(shell echo $(CLASS) | tr ',' ' '), \
make -C BUILDENV CLASS=$(C) BUILD=just_filelist OUTPUT=filelist.txt SKIPSELF=yes && \
../classCvt/classCvt <./Applications/$(C).class> ./Applications/$(C).ujc && \
time -p ./$(APP) ./Applications/$(C).ujc `cat filelist.txt` &&) true

Related

How to add a specific folder tree to the fzf search list?

I use fzf in bash on Gentoo Linux and configured it in the ~/.bashrc with
if [ -x "$(command -v fzf)" ]
then
source /usr/share/fzf/key-bindings.bash
fi
export FZF_DEFAULT_OPTS="
--layout=reverse
--info=inline
--height=80%
--multi
--preview-window=:hidden
--preview '([[ -f {} ]] && (bat --style=numbers --color=always {} || cat {})) || ([[ -d {} ]] && (tree -C {} | less)) || echo {} 2> /dev/null | head -200'
--color='hl:148,hl+:154,pointer:032,marker:010,bg+:237,gutter:008'
--prompt='∼ ' --pointer='▶' --marker='✓'
--bind 'ctrl-p:toggle-preview'
--bind 'ctrl-a:select-all'
--bind 'ctrl-y:execute-silent(echo {+} | pbcopy)'
--bind 'ctrl-e:execute(echo {+} | xargs -o vim)'
--bind 'ctrl-v:execute(code {+})'
"
CTRL-T should now provide a fuzzy search in ~/my/.. and in /usr/local/.
What is the best way to teach fzf about these two directory trees?
Short Answer:
You should use the variable FZF_CTRL_T_COMMAND and set your custom command.
In this case you should use something like this:
export FZF_CTRL_T_COMMAND="find ~/my/.. /usr/local"
And now, when you press Ctrl-T you will be able to search only in those directories.
Long Answer:
I realized that you should use FZF_CTRL_T_COMMAND variable by inspecting into the key-bindings file, in my case, this one is located in: /usr/share/bash-completion/completions/fzf-key-bindings in your case might be /usr/share/fzf/key-bindings.bash.
If you check in that file you should see a function called __fzf_select__ and inside this one you might have something like this:
__fzf_select__() {
local cmd opts
cmd="${FZF_CTRL_T_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
-o -type f -print \
-o -type d -print \
-o -type l -print 2> /dev/null | cut -b3-"}"
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS -m"
eval "$cmd" |
FZF_DEFAULT_OPTS="$opts" $(__fzfcmd) "$#" |
while read -r item; do
printf '%q ' "$item" # escape special chars
done
}
As you can see, the assignation in the third line: cmd="${FZF_CTRL_T_COMMAND:-"command find -L . ..."}" means that the command to execute will be what FZF_CTRL_T_COMMAND variable has, but if this is empty then it should execute the function defined there: command find -L . ..
Maybe you should take a look at the assignment command find -L ... and set your FZF_CTRL_T_COMMAND similarly or you can simply set it as I specified in the short answer and it will work: export FZF_CTRL_T_COMMAND="find ~/my/.. /usr/local"

Makefile eval function causing unexpected end of file

I'm trying to increment a Makefile variable with eval function inside a rule (to do a fancy loading bar), by doing
$(eval COUNTER=$(shell echo $$(($(COUNTER) + 1))))
I found on internet that this is working for some people but this line causes me a Syntax Error: unexpected end of file, what should I do to increment my counter? Are there some non-horrific alternatives to this function, or a way to avoid this unexpected end of file? thanks for your help
Here is the entire Makefile I'm working on
SHELL = /bin/sh
.SUFFIXES = .c .h .o
NAME = Necklace
SRCD = src
INCD = inc
OBJD = .obj
SRC = $(addsuffix $(word 1, $(.SUFFIXES)),\
main\
necklace\
utils)
INC = $(addsuffix $(word 2, $(.SUFFIXES)),\
necklace)
OBJ = $(SRC:$(word 1, $(.SUFFIXES))=$(word 3, $(.SUFFIXES)))
CC = gcc
CFLAGS = -Wall\
-Werror\
-Wextra\
-I $(INCD)
COUNT = $(shell cat file.count 2>/dev/null)
COMPILED = $(shell echo $$(($(shell ls src/*$(word 1, $(.SUFFIXES)) | wc -l | tr -d ' ') - $(COUNT))))
define set
set = $(eval $1 := $2)
ifeq ($(COUNT),)
all:
echo $(words $(SRC)) > file.count && $(MAKE) -n | grep $(CC) | wc -l | tr -d ' ' > tmp.txt && rm -f file.count && echo $$(($$(cat tmp.txt) - 1)) > file.count && make
else
all: $(NAME)
endif
$(NAME): $(addprefix $(OBJD)/, $(OBJ)) | $(addprefix $(INCD)/, $(INC))
$(CC) $(CFLAGS) $^ -o $#
$(OBJD):
mkdir $#
$(OBJD)/%.o: $(SRCD)/%.c | $(OBJD)
#printf '\r\033[2KCompiling [' && sh prc.sh $(COMPILED) $(COUNT) && printf ']' && $(CC) $(CFLAGS) -c $< -o $# && $(eval COMPILED:=$(shell echo $$(($(COMPILED) + 1)))) #Syntax Error is here
clean:
rm -rf $(OBJD)
fclean: clean
rm -rf $(NAME)
re: fclean all
I managed to do it by writing and reading the variable into a file instead of using Make variables

Makefile: make text file and append strings in it

I'm having difficulties with makefiles.
So in a recipe, I'm making a file (with a name and a .ujc extension) in a for loop and would like to have a text file at the end which contains all the created files. Purpose is to feed it to an application.
For example, in a semi high-level example,
List= [Class1,Class2,Class3]
foreach(Class C in List) {
#do operations on C > outputs a ClassX.ujc file
# add name of file to a text file named "list_of_files"
}
At the end I should have a text file, list_of_files.txt, which contains the following string:
Class1.ujc Class2.ujc Class3.ujc
As a reference, the code I have at the moment (and which does a bit of the stuff above but does not work is) is:
pc: $(APP)
$(foreach C, $(shell echo $(CLASS) | tr ',' ' '), \
make -C BUILDENV CLASS=$(C) BUILD=just_filelist OUTPUT=filelist.txt SKIPSELF=yes && \
../classCvt/classCvt <./Applications/$(C).class> ./Applications/$(C).ujc && \
cat app_file_list.txt | xargs echo ./Applications/$(C).ujc >app_file_list.txt && \
) true
time -p ./$(APP) `cat app_file_list.txt` `cat filelist.txt`
The internal make does make a filelist which is fed to the app, but I'd also like to feed the app_file_list but its construction goes totally wrong.
Probably simple, but I'm not getting there.
Edit:
The code below does what I want:
pc: $(APP)
rm -f cat app_file_list.txt
$(foreach C, $(shell echo $(CLASS) | tr ',' ' '), \
make -C BUILDENV CLASS=$(C) BUILD=just_filelist OUTPUT=filelist.txt SKIPSELF=yes && \
../classCvt/classCvt <./Applications/$(C).class> ./Applications/$(C).ujc && \
cat app_file_list.txt | echo ./Applications/$(C).ujc >>app_file_list.txt && \
) true
time -p ./$(APP) `cat app_file_list.txt` `cat filelist.txt`
Notable mistake I made was the xargs.
(Also in the post)
The solution turned out to be not-so-difficult. I needed to remove the xargs command and do the correct operation (i.e., >> instead of >) in the 'cat app_file_list.txt | etc...' line.
The code below does what I want:
pc: $(APP)
rm -f cat app_file_list.txt
$(foreach C, $(shell echo $(CLASS) | tr ',' ' '), \
make -C BUILDENV CLASS=$(C) BUILD=just_filelist OUTPUT=filelist.txt SKIPSELF=yes && \
../classCvt/classCvt <./Applications/$(C).class> ./Applications/$(C).ujc && \
cat app_file_list.txt | echo ./Applications/$(C).ujc >>app_file_list.txt && \
) true
time -p ./$(APP) `cat app_file_list.txt` `cat filelist.txt`
Notable mistake I made was the xargs which caused strings to repeat into the .txt file.

string replacement using xargs

I am trying to collect file path and timestamp for each file under a certain directory(which is passed as an argument) in makefile
So, it goes like this.
TIMESTAMP_LOG := timestamp.log
TARGET_ROOT := ../../out/root
define collect-timestamp
$(shell find $(1) | xargs -lfn sh -c 'echo -n fn"," >> $(TIMESTAMP_LOG); stat -c %Y fn >> $(TIMESTAMP_LOG)')
endef
all:
$(call collect-timestamp, $(TARGET_ROOT))
If I run this, i would the get whole file path and timestamp as below
ex) ../../out/root/bin/ls,133030303
but I want to get rid of "../../out/root" in file path.(passing as an argument if possible)
I thought I could do this using sed or shell script(see below) but apparently I am stuck. I tried:
$(shell find $(1) | xargs -Ifn sh -c 'echo -n ${fn##$(1)}"," >> $(TIMESTAMP_LOG); stat -c %Y fn >> $(TIMESTAMP_LOG)')
$(shell find $(1) | xargs -Ifn sh -c 'sed 's/fn//g' >> $(TIMESTAMP_LOG); stat -c %Y fn >> $(TIMESTAMP_LOG)')
If you use find, you don't need xargs in most cases.
The following should work:
find $(1) -exec stat -c "%n,%Y" {} \; | sed 's#$(1)\/##'
Note that I use $(1) as both parameter to find and in sed substitution command.
You may try
find $(1) | xargs stat -c %Y >> $(TIMESTAMP_LOG)

Problem in Makefile

I am executing the following command in Makefile:-
#ls export_mojave/marker_*.tcl > export_mojave.list
#for file in `cat export_mojave_tcl_files.list`; do \
diff $$file bk_marker > $$file.diff ; \
if ! [ -s $$file.diff ]; then\
rm -f $$file.diff ; \
else \
echo $$file >> marker.fill.tcl.diff; \
fi \
done ;
If there exists some file related to the above expression in the mentioned directory,
it will run fine but if there does not exits any files matching to above expression, It is marking an error. Is there anything exists like "catch" in Makefile?
If you need to skip error in makefiles' rule, then prefix command with '-' sign:
-#ls .... > some_file || echo Error: no file;
if [ -e some_file ] ....
Or modify to be more in make-style:
FILES := $(shell ls ...)
ifeq ($(FILES),)
$(error no files)
endif
....
target:
$(foreach file,$(FILES), ...)

Resources