Incrementing Makefile Variable When Executing Rule - makefile

My dilemma is the following: I need to create a symbolic link to a different serial port for each of the items in the Makefile variable 'LINKS'. I have the following code.
LINK_PATH = ~/some/path/
LINKS = $(LINK_PATH)/SomeLinkName $(LINK_PATH)/AnotherLinkName $(LINK_PATH)/TheseLinkNamesUnchangeable
COUNT = 0
install: $(LINKS)
#Do other stuff
$(LINKS): $(LINK_PATH)
ln -s /dev/ttyS$(COUNT) $#
$(LINK_PATH):
mkdir -p $#
I know that, as it is now, it will just create a bunch of links pointing to /dev/ttyS0. But I need them to be different, preferably sequential. Any ideas? Thanks.

if all of the serial ports are defined ahead of time, you can enumerate them and store that list in another variable then use that variable as a target dependency:
LINKS=/path/to/bar /path/to/baz /path/to/woz
COUNTS=$(shell v=`echo $(LINKS) | wc -w`; echo `seq 0 $$(expr $$v - 1)`)
install: $(COUNTS)
$(COUNTS):
#echo ln -s /dev/ttyS$# $(shell v=($(LINKS)); echo $${v[$#]})
then, when run:
[user#host: ~]$ make install
ln -s /dev/ttyS0 /path/to/bar
ln -s /dev/ttyS1 /path/to/baz
ln -s /dev/ttyS2 /path/to/woz

Related

Symbolic link in makefile

Consider the following makefile:
SHELL = /bin/sh
MY_DIR := $(realpath ./)
BASE_DIR := $(realpath ../..)
BASE_SRC_DIR = $(BASE_DIR)/src
BASE_INC_DIR = $(BASE_DIR)/include
HUL_DIR = $(MY_DIR)/hul
JNI_DIR = $(HUL_DIR)/jni
JNI_SRC_DIR = $(JNI_DIR)/src
JNI_INC_DIR = $(JNI_DIR)/include
dirs: $(JNI_SRC_DIR) $(JNI_INC_DIR)
$(JNI_SRC_DIR): $(JNI_DIR)
ln -s $(BASE_SRC_DIR) $#
$(JNI_INC_DIR): $(JNI_DIR)
ln -s $(BASE_INC_DIR) $#
$(JNI_DIR):
mkdir -p $(JNI_DIR)
This makefile creates two symbolic links (JNI_SRC_DIR and JNI_INC_DIR) and sets a JNI_DIR as a dependency for those. All is fine except one thing: calling make dirs twice creates the links and then links inside those folders. I know this is the standard ln behaviour when symlinking folders that already exist, I just don't know of any ln option flag to prevent it without an error (-n does it but with an error). Anyway, what I'd like to change is make running the rules for the second time. Apparently it also follows the symlinks, but I just want it to check whether they are there:
Here's a sample output, with three calls:
$ make dirs
mkdir -p /Users/fratelli/Documents/hul/platform/android/hul/jni
ln -s /Users/fratelli/Documents/hul/src /Users/fratelli/Documents/hul/platform/android/hul/jni/src
ln -s /Users/fratelli/Documents/hul/include /Users/fratelli/Documents/hul/platform/android/hul/jni/include
$ make dirs
ln -s /Users/fratelli/Documents/hul/src /Users/fratelli/Documents/hul/platform/android/hul/jni/src
ln -s /Users/fratelli/Documents/hul/include /Users/fratelli/Documents/hul/platform/android/hul/jni/include
$ make dirs
make: Nothing to be done for `dirs'.
I'd like the second time to behave as the third, as the symlinks are already there.
What's happening is that when the symlinks are created by the first make dirs invocation, the modification time of the directory gets updated. Because you have a dependency on the directory, that means the next time you run make dirs, make decides the targets are out of date.
You can change the dependency on $(JNI_DIR) to be an order-only prerequisite instead, like this:
$(JNI_SRC_DIR): | $(JNI_DIR)
ln -s $(BASE_SRC_DIR) $#
$(JNI_INC_DIR): | $(JNI_DIR)
ln -s $(BASE_INC_DIR) $#
This tells make to create $(JNI_DIR) if it doesn't exist, but it won't recreate the links if the directory has been updated.

Understanding a docker entrypoint script

The script is located here: https://github.com/docker-library/ghost/blob/master/docker-entrypoint.sh
#!/bin/bash
set -e
if [[ "$*" == npm*start* ]]; then
baseDir="$GHOST_SOURCE/content"
for dir in "$baseDir"/*/ "$baseDir"/themes/*/; do
targetDir="$GHOST_CONTENT/${dir#$baseDir/}"
mkdir -p "$targetDir"
if [ -z "$(ls -A "$targetDir")" ]; then
tar -c --one-file-system -C "$dir" . | tar xC "$targetDir"
fi
done
if [ ! -e "$GHOST_CONTENT/config.js" ]; then
sed -r '
s/127\.0\.0\.1/0.0.0.0/g;
s!path.join\(__dirname, (.)/content!path.join(process.env.GHOST_CONTENT, \1!g;
' "$GHOST_SOURCE/config.example.js" > "$GHOST_CONTENT/config.js"
fi
ln -sf "$GHOST_CONTENT/config.js" "$GHOST_SOURCE/config.js"
chown -R user "$GHOST_CONTENT"
set -- gosu user "$#"
fi
exec "$#"
From what I know, it says that if you use some variation of npm start to move some files around from $GHOST_SOURCE to $GHOST_CONTENT, do something to the config.js file, link the config file, set ownership of the content files, and then execute npm start as the user user. Otherwise, it just runs your commands normally.
The specifics are what are hard for me to understand because there are a lot of things from bash that I've never seen before. So I have a lot of questions.
for dir in "$baseDir"/*/ "$baseDir"/themes/*/; do
In the above, why do they specify both /*/ and /themes/*/? Shouldn't /*/ contain themes? Is * not a wildcard for some reason?
targetDir="$GHOST_CONTENT/${dir#$baseDir/}"
In the above, what is the point of # in the variable expansion?
tar -c --one-file-system -C "$dir" . | tar xC "$targetDir"
In the above, does this somehow save time? Why not use something like rsync? I understand the point of -C, but why -c and --one-file-system?
sed -r '
s/127\.0\.0\.1/0.0.0.0/g;
s!path.join\(__dirname, (.)/content!path.join(process.env.GHOST_CONTENT, \1!g;
' "$GHOST_SOURCE/config.example.js" > "$GHOST_CONTENT/config.js"
What does this sed command do? I know it's a replacement, but why the "$GHOST_SOURCE/config.example.js" > "$GHOST_CONTENT/config.js" as the end?
ln -sf "$GHOST_CONTENT/config.js" "$GHOST_SOURCE/config.js"
In the above, what is the point of this symlink? Why try to link them to each other if both files already exist?
set -- gosu user "$#"
In the above what does calling set with no args do?
I hope that's not too much. I felt making a separate question for each of these would be too much especially since it's all related to each other.
for dir in "$baseDir"/*/ "$baseDir"/themes/*/; do
In the above, why do they specify both /*/ and /themes/*/? Shouldn't
/*/ contain themes? Is * not a wildcard for some reason?
themes/ is in the first match, but themes/*/ is not, so you need the second entry to include the contents of themes.
targetDir="$GHOST_CONTENT/${dir#$baseDir/}"
In the above, what is the point of # in the variable expansion?
It removes the $baseDir prefix from $dir. So for example:
bash$ dir=/home/bmitch/data/docker
bash$ echo $dir
/home/bmitch/data/docker
bash$ echo ${dir#/home/bmitch}
/data/docker
tar -c --one-file-system -C "$dir" . | tar xC "$targetDir"
In the above, does this somehow save time? Why not use something like
rsync? I understand the point of -C, but why -c and --one-file-system?
rsync may not be installed on every machine by default, tar is fairly universal. The -c is to create, vs extract, and --one-file-system avoids tar continuing to an outside mount point (nfs, symlink to root, etc).
sed -r '
s/127\.0\.0\.1/0.0.0.0/g;
s!path.join\(__dirname, (.)/content!path.join(process.env.GHOST_CONTENT, \1!g;
' "$GHOST_SOURCE/config.example.js" > "$GHOST_CONTENT/config.js"
What does this sed command do? I know it's a replacement, but why the
"$GHOST_SOURCE/config.example.js" > "$GHOST_CONTENT/config.js" as the
end?
config.example.js is the input (last arg to the sed), config.js is the output (after the >). So it takes the config.example.js, change the ip address from 127.0.0.1 to 0.0.0.0, effectively listening on all interfaces/ip's instead of just internally on the loopback. The second half of the sed is changing the path.join arguments from __dirname to process.env.GHOST_CONTENT.
ln -sf "$GHOST_CONTENT/config.js" "$GHOST_SOURCE/config.js"
In the above, what is the point of this symlink? Why try to link them
to each other if both files already exist?
The $GHOST_SOURCE/config.js is replaced (-f) with a link to $GHOST_CONTENT/config.js. Symbolic links give a file name reference to another actual file, so there will be two names, but one copy of the data, which means you will only have a single configuration in this situation.
set -- gosu user "$#"
In the above what does calling set with no args do?
This changes the values of $1, $2, ... $n to be $1=gosu, $2=user, $3=the old $1, $4=the old $2..., essentially adding the gosu and user to the beginning of the passed parameters to the script. The -- makes sure that set doesn't interpret any values from $# as a flag for itself.

Multiple value in variable and name parse

I'm writing code to make some wget and get the backup file in some routers.
My first problem is with the variable RT. If I declare only IP on this variable (and add the /config.dat or anything else in wget line) this script will work, otherwise it will say that the "directory was not found."
How can I declare it as it is in the script below?
My second question: I want to say that the output file -O is the IP with the extension .dat for the 2 first IP and for the third it will not have extension just the IP.
Is it possible to do that as I said?
#!/bin/sh
RT="10.0.0.59/config.dat 10.0.0.60/cgi-bin/export_settings.cgi 10.0.0.66/rom-0"
MT="10.0.0.57 10.0.0.58"
L_RT="LOGIN"
P_RT="PASSWORD"
#FUTURE USE WITH TAR
#tmp=$"(mktemp -d)"
#trap -- 'rm -frv -- "$tmp"' EXIT
#cd -- "$tmp"
for bkp_rt in $RT; do
wget --auth-no-challenge --user=$L_RT --password=P_RT $bkp_rt -O $bkp_rt
done
After updating my script.
#!/bin/bash
RT="10.0.0.59/config.dat 10.0.0.60/cgi-bin/export_settings.cgi 10.0.0.66/rom-0"
MT="10.0.0.57 10.0.0.58"
L_RT="admin"
P_RT="PASSWORD"
L=MT="backup"
#tmp=$"(mktemp -d)"
#trap -- 'rm -frv -- "$tmp"' EXIT
#cd -- "$tmp"
for bkp_rt in $RT; do
wget --auth-no-challenge --user=$L_RT --password=$P_RT \
"$bkp_rt" \
-O "$bkp_rt"
done
You're missing the $ for --password=P_RT.
If you want to use 10.0.0.60/cgi-bin/export_settings.cgi as a filename, you'll have to make sure the 10.0.0.60 and 10.0.0.60/cgi-bin directories exist first.
To fetch the URL with wget, you will probably need to specify the scheme:
for bkp_rt in $RT; do
mkdir -p "$(dirname "$bkp_rt")"
wget --auth-no-challenge --user="$L_RT" --password="$P_RT" "$bkp_rt" \
-O "$bkp_rt"
done

ln command creates broken links

I'm having trouble using the ln command in a makefile.
This is a part of my makefile for creating a dynamic library:
NAME := Net
DDIR := dynamic_library
DLIB := $(DDIR)/lib$(NAME).so
MAJOR := 1
MINOR := 0
VERSION := $(MAJOR).$(MINOR)
$(DLIB).$(VERSION): $(OBJD)
g++ -shared -Wl,-soname,$(DLIB).$(MAJOR) $^ -o $#
$(DLIB): $(DLIB).$(VERSION)
ln -sf $(DLIB).$(VERSION) $(DLIB)
ln -sf $(DLIB).$(VERSION) $(DLIB).$(MAJOR)
OBJD are my .o files.
I'm trying to create the links next to libNet.so.1.0 which is in dynamic_library/ .
The ln commands create broken links with incomplete target addresses but in the correct destination.
I've tried adding ./ and / before the sources but they don't work.
Any help would be appreciated
EDIT
found the answer through trial and error
apparently we're supposed to add ../ before the sources. I have no idea why though.
if anybody has a better way, please answer.
Some of the variations of ln command are:
ln -s abs_path_to_link_target rel_path_from_current_dir_to_link_source
ln -s rel_path_from_link_src_to_target rel_path_from_current_dir_to_link_source
But the following, which you were trying to use, is not one of them:
ln -s rel_path_from_current_dir_to_link_target ...
Your makefile has another subtle error, namely, the link source, does not depend on the changes to the link target, it only depends on the existence of the link target.
And another problem, is that you have a "side effect", when you are making $(DLIB) target. I am guessing you are a software eng, so you know that side effects are bad for parallelism, cause race conditions, and make code hard to read.
Also, one should always use automatic variables such as $#, and depend everything on the Makefile.
Finally, I am hoping that you know why you are using -f. Some of the responses above, including mine :), do not use it. It is very important in the Makefile context, don't drop it.
Bearing these points in mind, the cleanest and correct way to do this would be:
$(DLIB) $(DLIB).$(MAJOR): Makefile | $(DLIB).$(VERSION)
ln -sf $(abspath $|) $#
g++ -shared -Wl,-soname,$(DLIB).$(MAJOR) $^ -o $#
Isn't it supposed to be -Wl,-soname,$(DLIB).$(VERSION)?
ln -s creates a link at the destination location with a link target of your source path (not the full path the path you give the ln command). If your link destination is a directory below your source directory and you aren't using full paths to the source then you need the ../ so that the link target is ../source.file instead of source.file as that would make the link point to a file in the same directory.
Compare:
$ ln -s bar foo
$ readlink foo
bar
$ readlink -f foo
/tmp/bar
$ ln -s ../bar foo
$ readlink foo
../bar
$ readlink -f foo
/foo
$ ln -s /tmp/bar foo
$ readlink foo
/tmp/bar
$ readlink -f foo
/tmp/bar

Suppress "Clock skew" warning for future-times in Makefile

I have a Makefile that does performs a task if it hasn't happened in the last hour. It does so like this:
HOUR_FROM_NOW = $(shell perl -e '($$s,$$m,$$h,$$d,$$M)=localtime(time()+3600); printf("%02d%02d%02d%02d\n",$$M+1,$$d,$$h,$$m);')
NOW_FILE = $(shell mkdir -p .make; touch .make/now; echo .make/now )
.PHONY: externals
externals: $(PROJECTS:%=.make/proj_%)
.make/proj_%: $(NOW_FILE)
$(MAKE) -s $(*F)
touch -t $(HOUR_FROM_NOW) $#
.PHONY: $(PROJECTS)
$(PROJECTS):
# do stuff, specifically, clone git-repo if not exists, else pull latest
That part works great, except that I now get warnings:
make: Warning: File `.make/proj' has modification time 3.5e+03 s in the future
make: Nothing to be done for `externals'.
make: warning: Clock skew detected. Your build may be incomplete.
Anyone know how to suppress those warnings? (Or to do a periodic task in a makefile)
Most versions of touch I have come across can do some date time maths which allows for setting the timestamp of a file directly via the --date option.
That and the fact that variables assigned with := are only "evaluated once" makes this a bit easier to read.
HOUR_AGO := .make/hour_ago
__UGLY := $(shell mkdir -p .make && touch --date='1hour ago' $(HOUR_AGO))
# The preceding line will be executed once
.make/proj_%: .make/hour_ago | .make
$(MAKE) -s $(*F)
#touch $#
.make:
mkdir -p $#
I'm using something very similar to this to periodically refresh login tokens.
Never would have thought of it if it wasn't for Dave's answer though.
The directory is created by specifying it as a order-only-prerequisite
I suspect that the + 3600 is at fault. What happens if you remove it?
I thought and thought, and then the stupid-obvious solution hit me ...
Instead of setting timestamps in the future with HOUR_FROM_NOW, I use the real time and compare with HOUR_AGO_FILE ...
HOUR_AGO = $(shell perl -e '($$s,$$m,$$h,$$d,$$M)=localtime(time()-3600); printf("%02d%02d%02d%02d\n",$$M+1,$$d,$$h,$$m);')
HOUR_AGO_FILE = $(shell mkdir -p .make; touch -t $(HOUR_AGO) .make/hour_ago; echo .make/hour_ago )
.PHONY: externals
externals: $(PROJECTS:%=.make/proj_%)
.make/proj_%: $(HOUR_AGO_FILE)
$(MAKE) -s $(*F)
#touch $#

Resources