Makefile - iterate over list of files - first file missed - cmd

I've got a file containing a list of source files that looks like this:
SRCS = src/main.c \
src/main.h
Then in my makefile I have a target like so to create a .TCL file that references the SRCS files:
create_app.tcl: .platform.done
echo setws $(WORKSPACE) > $#
echo platform active {$(PROJ_NAME)_platform} >> $#
echo app create -name $(PROJ_NAME)_app -template {Empty Application(C)} >> $#
echo app config -name $(PROJ_NAME)_app -add libraries m >> $#
$(foreach file, $(SRCS), echo importsources -name $(PROJ_NAME)_app -path {$(realpath $(file))} -soft-link & >> $#) true
The 'foreach' line misses off the first source file from my list and i'm struggling to see why. So my TCL ends up like this:
setws ws
platform active {test_platform}
app create -name test_app -template {Empty Application(C)}
app config -name test_app -add libraries m
importsources -name test_app -path {C:/git/dev_aph/software/test/src/main.h} -soft-link
When it should end up like this:
setws ws
platform active {test_platform}
app create -name test_app -template {Empty Application(C)}
app config -name test_app -add libraries m
importsources -name test_app -path {C:/git/dev_aph/software/test/src/main.c} -soft-link
importsources -name test_app -path {C:/git/dev_aph/software/test/src/main.h} -soft-link
Can anyone point out what i'm doing incorrectly here? Sure I could just add the first file twice but i'd rather understand what i've got wrong and fix it.
Thanks.
Update:
Trying Renaud's suggestion:
make create_app.tcl
echo setws ws > create_app.tcl
echo platform active {test_platform} >> create_app.tcl
echo app create -name test_app -template {Empty Application(C)} >> create_app.tcl
echo app config -name test_app -add libraries m >> create_app.tcl
for p in C:/git/dev_aph/software/test/src/main.c C:/git/dev_aph/software/test/src/main.h; do \
echo "importsources -name test_app -path {$p} -soft-link &" >> create_app.tcl; \
done
p was unexpected at this time.
make: *** [../../scripts/vitis.mk:38: create_app.tcl] Error 255
If I change the loop variable to be %%p:
make create_app.tcl
echo setws ws > create_app.tcl
echo platform active {test_platform} >> create_app.tcl
echo app create -name test_app -template {Empty Application(C)} >> create_app.tcl
echo app config -name test_app -add libraries m >> create_app.tcl
for %%p in C:/git/dev_aph/software/test/src/main.c C:/git/dev_aph/software/test/src/main.h; do \
echo "importsources -name test_app -path {$p} -soft-link &" >> create_app.tcl; \
done
C:/git/dev_aph/software/test/src/main.c was unexpected at this time.
make: *** [../../scripts/vitis.mk:38: create_app.tcl] Error 255
Another update:
Got it working as intended by modifying the 'foreach':
$(foreach file, $(SRCS), echo importsources -name $(PROJ_NAME)_app -path {$(realpath $(file))} -soft-link >> $# &) true
I've moved the & the other side of the $# as I noticed the first source file in the list was getting echo'd to the command line rather than to the target.

You can't use foreach as a replacement for a for-loop inside a recipe. The rule how make expands and parses a recipe inside a rule is to first substitute all variables and functions and then to pass the resulting string to single shell invocations, line by line as written in the makefile. What you need to recognize though, is that foreach DOES NOT produce anything else but a space-separated list of strings, so your depiction of singled out lines per iteration is the wrong mental picture of this function. Its just a string, really. In your case the result of the foreach will look like this:
echo importsources -name test_app -path {src/main.c} -soft-link & >> create_app.tcl echo importsources -name test_app -path {src/main.h} -soft-link & >> create_app.tcl true
Notice the double echo in the line - I am too weak in shell programming to really judge the validity of the syntax of this statement but I think its funny that it works at all. A possible workaround is the definition of newline and tabulator characters which you could prepend to the generator string in the foreach, but I think the more common and easier to understand approach is to write a true forloop in shell syntax.
In case you nevertheless want to try the make based solution (and learn a bit about dynamically generating makefile portions):
empty := #
define newline :=
$(empty)
$(empty)
endef
SRCS = src/main.c \
src/main.h
create_app.tcl: .platform.done
echo setws $(WORKSPACE) > $#
echo platform active {$(PROJ_NAME)_platform} >> $#
echo app create -name $(PROJ_NAME)_app -template {Empty Application(C)} >> $#
echo app config -name $(PROJ_NAME)_app -add libraries m >> $#
$(foreach file, $(SRCS), echo importsources -name $(PROJ_NAME)_app -path {$(realpath $(file))} -soft-link & >> $#$(newline))

As explained in comments you are mixing make functions and shell functions and this is wrong. If your file paths don't have spaces, try:
PATHS := $(realpath $(SRCS))
create_app.tcl: .platform.done
...
for p in $(PATHS); do \
echo "importsources -name $(PROJ_NAME)_app -path {$$p} -soft-link &" >> $#; \
done
Do not forget the trailing \ characters (line continuation) and the $$p.

Thanks for everyone's suggestions. I'm going to post my solution to the original problem. I know it has been mentioned that this is not the best approach but it has achieved the result I was looking for.
Got it working as intended by modifying the 'foreach':
$(foreach file, $(SRCS), echo importsources -name $(PROJ_NAME)_app -path {$(realpath $(file))} -soft-link >> $# &) true
I've moved the & the other side of the $# as I noticed the first source file in the list was getting echo'd to the command line rather than to the target.

Related

Bash script deletes first row in csv during editing with 'cut'

I have little problem with my bash script. The script is cloning repositories to my local folder and then search for files with specified extensions in those repositories (results are CSVtemporary2/ASSETS-LIST-"$dir".csv) , then edits results (cuts first 4 elements of paths, results are:/CSVtoGD2/"$dir".csv)
In the CSV there are paths to files in every single row ex. /users/krzysztofpaszta/temporaryprojects/gamex/car1.png
/users/krzysztofpaszta/temporaryprojects/gamex/sound1.mp3
(...)
Every part of the script is working fine but after cutting the first 4 elements of paths the first row is deleted.. I do not know why this is happening.
So results should be:
/car1.png
/sound1.mp3
(...)
But results are:
/sound1.mp3
(...)
I do not know why this is happening.
So in other words, files CSVtemporary2/ASSETS-LIST-"$dir".csv are fine but then, files
/CSVtoGD2/"$dir".csv have first row deleted.. Someone has idea why this is happening?
#!/bin/bash
rm -vrf /Users/krzysztofpaszta/CSVtoGD2/*
rm -vrf /Users/krzysztofpaszta/CSVtemporary2/*
cd /Users/krzysztofpaszta/temporaryprojects
for repo in $(cat /users/krzysztofpaszta/repolinks.csv); do
git clone "$repo"
dir=${repo##*/}
find /users/krzysztofpaszta/temporaryprojects/"$dir" -name "*.fnt" -o -name "*.png" -o -name "*.ttf" -o -name "*.asset" -o -name "*.jpeg" -o -name "*.tga" -o -name "*.tif" -o -name "*.bmp" -o -name "*.jpg" -o -name "*.fbx" -o -name "*.prefab" -o -name "*.flare" -o -name "*.ogg" -o -name "*.wav" -o -name "*.anim" -o -name "*.mp3" -o -name "*.tiff" -o -name "*.otf" -o -name "*.hdr" >> /users/krzysztofpaszta/CSVtemporary2/ASSETS-LIST-"$dir".csv
while read in ; do
cut -d'/' -f6- >> /users/krzysztofpaszta/CSVtoGD2/"$dir".csv #| awk 'BEGIN{print"//"}1' - adding first empty row is not the solution, first row with text is still deleted
done < /users/krzysztofpaszta/CSVtemporary2/ASSETS-LIST-"$dir".csv
done
#rm -vrf /Users/krzysztofpaszta/temporaryprojects/*
#echo Repo deleted
Your syntax
while read in ; do
cut -d'/' ^f6- >> /users/krzysztofpaszta/CSVtoGD2/"$dir".csv
done < /users/krzysztofpaszta/CSVtemporary2/ASSETS-LIST-"$dir".csv
will not work as you expect.
The read command reads the first line of the csv file
assigning a variable in to it.
Then the remaining lines are fed to cut command via stdin.
Instead you can say;
while IFS= read -r line; do
cut -d'/' ^f6- <<< "$line" >> /users/krzysztofpaszta/CSVtoGD2/"$dir".csv
done < /users/krzysztofpaszta/CSVtemporary2/ASSETS-LIST-"$dir".csv
Actually you don't even need to use the while loop:
cut -d'/' ^f6- < /users/krzysztofpaszta/CSVtemporary2/ASSETS-LIST-"$dir".csv > /users/krzysztofpaszta/CSVtoGD2/"$dir".csv
Besides there are many points to be improved:
There may be a typo in either of the pathnames /Users or /users.
You can use the while .. read .. loop instead of
for repo in $(cat path/to.csv)
You don't need to create a temporary file for the output of find.
You can feed the output directly to cut command via a pipeline.

How to use find to append a string to every file in a directory

I'm trying to append a fixed string to each file in a folder (and its subfolders) whilst skipping the .git directory.
I expected that something like this would work
find . -type f ! -path "./.git/*" -exec "echo \"hello world\" >> {}" \;
and if I run it with an additional echo it does generate commands that look right
bash> find . -type f ! -path "./.git/*" -exec echo "echo \"hello world\" >> {}" \;
echo "hello world" >> ./foo.txt
echo "hello world" >> ./bar.txt
...
and those commands do what I want when I run them directly from the shell but when I run it from find I get this:
bash> find . -type f ! -path "./.git/*" -exec "echo \"hello world\" >> {}" \;
find: echo "hello world" >> ./foo.txt: No such file or directory
find: echo "hello world" >> ./bar.txt: No such file or directory
...
but those files do exist because when I list the directory I get this:
bash> ls
bar.txt baz.txt foo.txt subfolder/
I guess that I'm not quoting something I need to but I've tried everything I can think of (double and single quote, escaping and not escaping the inner quotes etc...).
Can someone explain what is wrong with my command and how to achieve the the addition of the fixed string to the files?
You need to instruct the shell to do it:
find . -type f ! -path "./.git/*" -exec sh -c "echo hello world >> {}" \;

Xcode check for license header

I'm not even sure this is possible, but I'll give it a shot.
Is it somehow possible (i.e. with a plugin) for Xcode to check if every File contains a valid license header, and if not stops compiling?
Thanks in advance.
Here is the complete Setup! Note that this will produce Xcode erros too. :)
#!/bin/bash
searchString=`cat license.txt`
ERROR_COUNT=0
while read file
do
grep -F "${searchString}" $file > /dev/null
if [ $? -ne 0 ]; then
echo -e $file:1: error : No valid License Header found! 1>&2
ERROR_COUNT=1
fi
done < <(find . -type d \( -name "TTTAttributedLabel" -o -name "minizip" -o -name "SSZipArchive" \) -prune -o -type f \( -name "*.m" -o -name "*.h" -o -name "*.pch" \) -print)
exit $ERROR_COUNT
And here is how to setup the Script:
Xcode: Running a script before every build that modifies source code directly
How to report Errors in Xcode:
http://shazronatadobe.wordpress.com/2010/12/04/xcode-shell-build-phase-reporting-of-errors/
Well im not sure if this is the correct answer, but you could add a shell script to your build phase which could check the files in your project for the valid license header (via grep+regex).

Processing list in Makefile

In bash, when I want to iterate in a recursive list of pdf files, without the extension, I could do the following:
for file in `find mypath -type f -name '*.pdf' -printf "%f\n"`
do
echo "${file%.*}"
done
This works perfectly, and I get a list of the pdf files without the extension.
But if I try to do the same in a Makefile, I get empty output:
my_test:
#for file in `find mypath -type f -name '*.pdf' -printf "%f\n"`; \
do \
echo "${file%.*}"; \
done; \
do you have an idea why this is happening?
thanks in advance
Just put in an extra $:
echo "$${file%.*}"; \
In your command Make expands the first $, interprets ${ as nothing, and things unravel fast. With $$, the first $ escapes the second and the ${...} gets passed to the shell.

Makefile: depend on every file of a directory

I'd like to do a Makefile that runs either with gnumake or makepp that packs all the files under given directiories:
DIRS:=$(shell find . -mindepth 2 -maxdepth 2 -not -name mp3 -not -name ".*" -type d)
PACKAGES = $(DIRS:%=%.npk)
all: packages
packages: $(PACKAGES)
%.npk: %/*
npack c $# #^
.PHONY: all packages
the problem is that there's no such thing as %/* in the dependencies.
I need the targets (X.npk) to depend on every file in directory X, but I don't know what the files are when I write the Makefile, 'cause they're generated later.
An example:
./dirA/x
./dirA/y
./dirB/e
./dirB/f
I'd like to create ./dirA.npk (depending on x,y), ./dirB.npk (e,f)
There's nothing I know about the dirs or the files in advance except that the find used in the 1st line finds all the dirs.
Try using the wildcard directive:
DEPS := $(foreach dir, $(DIRS), $(wildcard $(dir)/*))
%.npk: $(DEPS)
npack c $# $^
EDIT:
The above is just an example of using wildcard and makes each .npk file dependent on the files in all of the other folders. Your usage would be slightly different.
I think there may be an easier way to go about this. Why are you wanting to have a dependency on all of the files in the folder? Is it just to use the $^ operator? Or do you need to rebuild the .npk if any of the files changed?
One alternate (and possibly cleaner) solution would be to use the find utility in your recipe instead of $^ and use the .FORCE directive to always force the .npk file to be rebuilt. The downside is that .npk files may be rebuilt unnecessarily.
EDIT 2:
If there's not a way to do this cleanly with make commands, you can work around it by using .FORCE to ensure that the recipe is always run and move the "should I rebuild this file" check into the body of the recipe:
%.npk: .FORCE
check_for_rebuild.sh $# && npack c $# $^
where check_for_rebuild.sh is a shell script that does something like this:
#!/bin/bash
# Returns non-zero if the archive needs to be rebuilt
if [ -e $1 ]; then
folder_name=$(basename $1 .npk)
[ -z "$(find $folder_name -newer $1 -not -type d)" ] && return 0
fi
return 1
I don't really like that solution because it works around the problem instead of solving it directly, but it may be able to get you going in the meantime. If you are going to go that route, it's probably cleaner and easier to do everything in the shell script and either have the makefile simply invoke the script or get rid of the makefile entirely.
This is the solution I found:
it is based on the makedepend idea, with some "meta" scripting. Not very nice, but works.
PACKAGES :=
all: packages
-include Makefile.depend
packages: Makefile.depend $(PACKAGES)
depend: clean Makefile.depend
Makefile.depend:
#(PACKAGES= ; \
for DIR in `find . -mindepth 2 -maxdepth 2 -not -name mp3 -not -name ".*" -type d` ; \
do \
PACKAGE=`basename $${DIR}.npk` ; \
PACKAGES="$${PACKAGES} $${PACKAGE}" ; \
DEPS=`find $${DIR} -not -type d | sed -e 's#\([: ]\)#\\\\\1#' -e 's#^\./\(.*\)# \1#' | tr -d "\n"` ; \
SUBDIR=`echo $${DIR} | sed -e 's#^\./\([^/]\+\)/.*#\1#'` ; \
FILES=`echo \ $${DEPS} | sed -e "s# $${SUBDIR}/# #g"` ; \
echo "$${PACKAGE}:$${DEPS}" ; \
echo " #cd $${SUBDIR} ; \\" ; \
echo " npack c ../\$$# $${FILES} ; \\" ; \
echo ; \
done ; \
echo "PACKAGES = $${PACKAGES}" \
)>> Makefile.depend ; \
cleanall: clean
rm -f *.npk
clean:
#rm -f Makefile.depend
.PHONY: all packages depend clean
With makepp you can do this in 2 steps, via the :foreach rule modifier:
$(foreach).txt: $(foreach)/*: foreach */
&echo $(inputs) -o $(output)
This provides a rule for every subdirectory, which reexecutes whenever there is a change in the list of files therein.

Resources