extra links from foreach in Makefile - makefile

I'm on a mac using GNU Make to manage my dotfiles. There's a directory with my emacs config files and a corresponding target in the Makefile:
all: _emacs
.PHONY: all list $(MAKECMDGOALS)
....
EMACS_SOURCE_DIR := $(abspath ./emacs)
EMACS_TARGET_DIR := $(abspath $(HOME)/.emacs.d)
EMACS_CONFIG_FILES := $(wildcard $(EMACS_SOURCE_DIR)/*.el)
_emacs: | $(EMACS_TARGET_DIR)
#echo $(call message,"Setting up config files for emacs")
$(foreach file, \
$(EMACS_CONFIG_FILES), \
ln -sf $(file) $(addsuffix /, $(EMACS_TARGET_DIR)))
$(EMACS_TARGET_DIR):
#echo "Creating directory $(EMACS_TARGET_DIR)"
#mkdir -p $#
When I run make _emacs it creates some extra links:
.emacs.d -> /Users/xxx/.emacs.d/
-sf -> -sf
early-init.el -> /Users/xxx/Projects/bootstrap/emacs/early-init.el
init-org.el -> /Users/xxx/Projects/bootstrap/emacs/init-org.el
init-pkgs.el -> /Users/xxx/Projects/bootstrap/emacs/init-pkgs.el
init.el -> /Users/xxx/Projects/bootstrap/emacs/init.el
ln -> ln
I'm struggling to understand what exactly is happening and how to avoid having the first two and the last soft links created.

In general it's a bad idea to try to construct a complex shell command using make functions inside a recipe. You should simply use shell constructs: for example use the shell for loop, not the make foreach loop.
Let's see what your recipe does:
_emacs: | $(EMACS_TARGET_DIR)
$(foreach file, \
$(EMACS_CONFIG_FILES), \
ln -sf $(file) $(addsuffix /, $(EMACS_TARGET_DIR)))
Make functions manipulate text. They don't know anything about commands, shell syntax, etc. Before make invokes the shell it first expands the script. What will this result in? This:
ln -sf .../emacs/early-init.el /Users/xxx/.emacs.d/ ln -sf .../emacs/init-org.el /Users/xxx/.emacs.d/ ln -sf ...
Maybe now you can see the problem.
If you wanted to do this using make's foreach you have to add a shell delimiter so the shell knows where one command ends and the next begins; something like:
$(foreach file, \
$(EMACS_CONFIG_FILES), \
ln -sf $(file) $(addsuffix /, $(EMACS_TARGET_DIR)) ; )
(note the ; at the end). Now when make expands it will look like this:
ln -sf .../emacs/early-init.el /Users/xxx/.emacs.d/ ; ln -sf .../emacs/init-org.el /Users/xxx/.emacs.d/ ; ln -sf ...
If I were you, I'd instead use a shell loop. It's just much simpler to understand:
for file in $(EMACS_CONFIG_FILES); do \
ln -sf $$file $(addsuffix /, $(EMACS_TARGET_DIR)) ; \
done
But, for what you want to do you don't need a loop at all; you can link multiple files into the same directory with one command:
ln -sf $(EMACS_CONFIG_FILES) $(EMACS_TARGET_DIR)
(I'm not sure what the addsuffix was needed for, or why you couldn't just write it as $(EMACS_TARGET_DIR)/ without the addsuffix)

Related

Make: Can we have directories as a target in GNUmakefile

I am working on GNUmake to create symlink to specific file by parsing whole directory which has a folder which needs to be linked.
Here is my makefile snippet.
TGT_LINK = /lan/test/workspace/build/tools
all_target: release_buid complete_test $(TGT_LINK)
$(TGT_LINK):
if [ ! -d $# ]; then mkdir -p $#; fi
cd $#; \
tar_ln=`\ls -d synopsystcl* | sed 's/synopsys//'`; \
sour_dir=`\ls -d synopsystcl*`; \
if ! [ -e $tar_ln ]; then \
ln -s $sour_dir $tar_ln; \
fi
Directory : /lan/test/workspace/build/tools Contains following content in it
polaris.so link.a dynamic.so kbuild.so README.txt license.txt synopsystcl5.5 build.json
Here i am trying to create symlink with name tcl5.5 pointing to synopsystcl5.5 with my above target $(TGT_LINK) code.
tcl5.5 -> synopsystcl5.5
After successful completion of two targets : release_buid complete_test , build is not proceeding to go for next target $(TGT_LINK) to create symlink. Could you please help whats wrong in code?
I would let make itself to check and recreate symlink as needed, i.e.:
TGT_LINK = /lan/test/workspace/build/tools
all_target: release_buid complete_test symlink
.PHONY: symlink
symlink: $(patsubst $(TGT_LINK)/tcl%, $(TGT_LINK)/synopsystcl%, $(wildcard $(TGT_LINK)/tcl*))
$(TGT_LINK)/synopsystcl%: $(TGT_LINK)/tcl%
set -e; \
cd $(#D); \
rm -f $(#F); \
ln -s $(<F) $(#F)
I would also consider reordering targets if there are dependencies indeed. One should never assume that dependency list is processed left to right; it will also lead to errors when parallel build (-j) is involved. If you have dependencies that something should happen before something else, it should be explicitly stated, e.g.:
all_target: symlink
symlink: complete_test
complete_test: release_buid

rpmbuild - /usr/sbin Symlink not installing

I'm setting up a Redis RPM for a local, unnetworked box. I'm trying to create a symlink: /usr/sbin/redis-server -> /opt/redis/redis-server
However, when I do an rpm -Uvh redis-3.2.7-1.rpm, it installed fine to /opt/redis/redis-server but never creates the symlink. Here's the relevant part of my spec file:
%build
# Empty section.
%install
rm -rf %{buildroot}
rm -f /usr/sbin/redis-server
mkdir -p %{buildroot}
# in builddir
cp -a * %{buildroot}
ln -sf /opt/redis/redis-server /usr/sbin/redis-server
%clean
rm -rf %{buildroot}
%files
/opt/redis/*
/etc/init.d/redis
ln -sf /opt/redis/redis-server /usr/sbin/redis-server needs to be ln -sf /opt/redis/redis-server %{buildroot}/usr/sbin/redis-server and then /usr/sbin/redis-server needs to be added to the %files section. Also remove that rm in %install.
The fact that the ln did not fail tells me you really made the symlink, and you're building RPMs as root which is a spectacularly bad idea.
I'm assuming that the tarball expands with opt at the top level; if not your cp is incorrect as well.

How to execute a make target on file change automatically?

How do I write a make target that will watch for any file changes in specific folders and execute some other make target to compile files? I am looking for a way that can do this with minimal dependency on tools in addition to make itself to keep things simple.
For the watching you can use fswatch. (There's also a go version of this program which may be easier to install: fswatch) For example:
fswatch -ext cpp,c,h make -f Makefile
Anytime you change a cpp, c or h file it will run make again.
Make can be a bit slow for this, so I tend to use ninja instead, but that really depends on the size of your project.
Another option is tup, which has watching built-in:
tup monitor
But, sadly, only for linux.
You can use entr and adjust your Makefile similar to this one
.DEFAULT_GOAL := run
SHELL := /bin/bash
run:
clear && \
cp one.txt two.txt && \
rm -f _* *.l2m *.o2m && \
Ganlib < testgan2.x2m
watch:
while sleep 1 ; do find . -name '*.x2m' -o -name '*.c2m' \
| entr -d make -f ./Makefile ; done
.PHONY: run watch
followed by
$ make watch

Make `shell` command returns outdated symlink target

If I create a symlink pointing to a (non-existant) target, old.
ln -sf "old" /tmp/symlink
Then run the following Makefile:
SHELL := /bin/bash
all:
#ln -sf "new" /tmp/symlink
#ls -l /tmp/symlink
#echo "$(shell ls -l /tmp/symlink)"
It outputs two different targets for the symlink:
lrwxrwxrwx ... /tmp/symlink -> new
lrwxrwxrwx ... /tmp/symlink -> old
Why does the shell command return an outdated target for the symlink?

Makefile error:127

I compiled the source code with the Makefile, I got Makefile Error:
/bin/sh: line 8: : command not found
make: *** [lib_build] Error 127
My lib_build target is as below:
lib_build:
#echo "--------------------------------------------------------------------";
#echo "VZW Compiling DM Agent 3rd party and native Libraries..." ;
#echo "--------------------------------------------------------------------";
mkdir -p $(VZW_LIB_DIR) ; \
mkdir -p $(VZW_BIN_DIR) ; \
mkdir -p $(VZW_SCR_DIR) ; \
mkdir -p $(VZW_CFG_DIR) ; \
mkdir -p $(VZW_OBJ_DIR) ; \
mkdir -p $(VZW_LOG_DIR) ; \
cd $(VZW_BASE_DIR)/lib/ ; \
make all ; \
cd $(VZW_SYNCML_DIR)/src/bld/linux ; \
make all;
cp -r $(VZW_SYNCML_DIR)/bin/linux/libsml.so $(VZW_LIB_DIR) ;
cp -r $(VZW_SYNCML_DIR)/bin/linux/libxpt.so $(VZW_LIB_DIR) ;
cp -r $(VZW_BASE_DIR)/3rd_party/iksemel-1.4/src/.libs/* $(VZW_LIB_DIR) ;
It was working fine then I try to modify some line in the above source code and later reverted everything. Then something went wrong and I got the error. I am not able to understand what went wrong.
One more thing to add here. Is there any way to know in which line exactly in the Makefile this kind of problem is happening. If there is no way then it is very difficult to spot these kind of problems.
I would suggest restructuring the commands. As the rule is written, it ignores all errors because this is how shell works. At least prepend set -e to this sequence of shell commands.
A lot more mind-tweaking to find if any syntactical mistake is there, I found the solution that there was a space after a line in Makefile ie. after the "/"
cd $(VZW_SYNCML_DIR)/src/bld/linux ; \
This was very difficult to spot though as spaces are not visible.

Resources