bj_dir = $(addprefix $(OBJDIR),$(subst $(ROOTDIR),,$(CURDIR)))/
target = $(obj_dir)libnovds_delivery.a
sources = \
novds_my_delivery_service_timer.c \
novds_my_delivery.c \
novds_dcmd.c \
novds_my_delivery_reply_service.c \
novds_my_delivery_reply.c \
novds_serial.c
objects = $(addprefix $(obj_dir),$(patsubst %.c,%.o,$(sources)))
deps = $(addprefix $(obj_dir),$(patsubst %.c,%.d,$(sources)))
i know the functionality of subst in makefile but in this piece code after ROOTDIR two commas are used i don't know what it will do.
Just to be clear, subst is not short for subset, it's short for substitute.
$(subst $(ROOTDIR),,$(CURDIR))) replaces every occurrence of whatever value $(ROOTDIR) expands to, with nothing (no text), in the value that $(CURDIR) expands to. The commas separate arguments so the first comma separates $(ROOTDIR) from the next argument and the second comma separates the second argument (which is empty) from $(CURDIR).
This is kind of a bad use of subst, though, because it will replace $(ROOTDIR) every time it appears (even multiple times) anywhere in the value (even in the middle or the end).
For things like this, where you want to remove a prefix only, it's best to use patsubst, as in $(patsubst $(ROOTDIR)%,%,$(CURDIR)).
Related
Assume I have the following directory/file structure
dirA/1fileAA.zsh
dirA/99fileAB.zsh
dirB/2fileBA.zsh
dirB/50fileBB.zsh
dirB/subdirA/20fileBAA.zsh
which I want to have ordered by the numbers the filenames begin with, ignoring any directories, so I get
dirA/1fileAA.zsh
dirB/2fileBA.zsh
dirB/subdirA/20fileBAA.zsh
dirA/99fileAB.zsh
dirB/50fileBB.zsh
using just built-in zsh functionality.
What would be the best way to achieve this?
I could think of rewriting strings sort and write them back?
Or better try to create an associated array and sort by keys?
I'm still a zsh and want to avoid digging into the wrong direction, too much.
Here is one way to accomplish this using only zsh builtins. The function prepends the filename to the front of each path for sorting and then removes it:
function sortByFilename {
local -a ary
printf -v ary '%s/%s' ${${argv:t}:^argv}
print -l ${${(n)ary}#*/}
}
With your example directory setup, it can be invoked from the parent directory of dirA and dirB with:
sortByFilename **/*.zsh
Testing it:
sortByFilename \
dirA/1fileAA.zsh \
dirA/99fileAB.zsh \
dirB/2fileBA.zsh \
dirB/50fileBB.zsh \
'/leadslash/42 and spaces' \
dirB/subdirA/20fileBAA.zsh
Result:
dirA/1fileAA.zsh
dirB/2fileBA.zsh
dirB/subdirA/20fileBAA.zsh
/leadslash/42 and spaces
dirB/50fileBB.zsh
dirA/99fileAB.zsh
The pieces:
printf -v ary <fmt> ...: runs printf with the format string, and assign the results to the ary array. Each iteration of the format string will become another element in the array.
%s/%s: the format string. This will concatenate two strings with a slash separator.
If there are more values than in the input than specifiers in the format string, printf will repeat the format pattern. So here, it will pull pairs (of filename/pathname) from the input array.
${${argv:t}:^argv}: this will produce an array alternating with filenames and full paths, i.e. (file1 path1 file2 path2 ...)
${ :^ }: zsh parameter expansion that will zip two arrays to create the alternating filenames and paths.
${argv:t}: array of filenames. Built using the function positional parameters in argv, and the :t modifier, which returns the filename component for each element in the array.
argv: array of full paths.
print -l: print each element of the input on a separate line.
${${(n)ary}#*/}: the final sorted list of paths.
${(n)ary}: Returns the array sorted numerically, using the n parameter expansion flag. At this point, each element in ary is the concatenation of the filename, a slash, and the input path.
The n flag works here because of the filename pattern; it will sort by decimal value instead of lexically within a common / empty prefix, e.g. foo1 foo3 foo12.
${ #*/}: Removes the pattern */ from the front of each element in the array. This deletes the prefix that was being used for sorting, leaving the original path.
local -a ary: declares an array variable. This is used as an indicator to printf -v to split its output.
It's possible to eliminate this line and make the function shorter and a bit more cryptic by (re-/mis-/ab)using the pre-declared array argv.
function sortByFilename {
printf -v argv %s/%s ${${argv:t}:^argv}
print -l ${${(n)argv}#*/}
}
Edit - a single-line version:
(){print -l ${"${(n0)$(printf '%s/%s\0' ${${argv:t}:^argv})}"#*/}} **/*.zsh
Including this simply because one-liners are fun to create, not because it's recommended. With the anonymous function, process substitution, and additional parameter expansion flags, this is less readable and possibly less efficient than the function above.
Our current main Makefile imports lots of sub makes. Along with auto dependency, the general format of each of these looks like this:
OBJS += \
$(OBJS_DIR)/<filename>.o \
...
C_DEPS += \
$(OBJS_DIR)/<filename>.d \
$(OBJS_DIR)/%.o: $(MODULES_PATH)/%.c $(OBJS_DIR)/%.d
$(GCC) ...
Where OBJS and C_DEPS are defined in the main makefile and accumulated across all included makefiles. BTW we are using -j in the call to make for parallel execution.
The first thing I tried was to change the fact that in each makefile each entry is listed twice, once in the OBJS and once again in the C_DEPS. The change I had in mind is the following:
OBJ := \
$(OBJS_DIR)/<filename>.o \
...
OBJS += $(OBJ)
C_DEPS += $(OBJ:.o=.d)
kind of making a local variable once and using it to add to the objects and dependency lists. But this doesn't work, there are at least two issues:
The first one is that each make include will overwrite the previous definition. The fact that the make files are included and not called results in all my attempts to use keywords like unexport and private prove useless and the variable OBJ is overwritten in each makefile.
I am concerned about make concurrency (make -j is used) - should I be?
I was thinking of making the variable name dynamic but that also looks ugly and there is no guarantee that there won't be two makefiles with the same name from different paths included.
Is there any way around this?
Thanks to #Vroomfondel for the observations that lead to the simple answer.
Saving the contents of the varibale in between include calls is actually what is performed by the concatination into OBJS and C_DEPS, the problem was that the variable was not expanded at the time it was read and after the accumelation was done it only held the value from the last makefile that set the value of OBJ.
As it turns out it didnt realy matter what falver variable OBJ was, the problem lay in the fact that I had both OBJS and C_DEPS set as recursivly expanded variable. Once I set them to simply expanded varibles the issue was resolved, OBJ was expanded into them at reading time.
That is from
OBJS =
C_DEPS =
To
OBJS :=
C_DEPS :=
I have a data processing job that I would like to automate with Make. Hundreds of files need to be processed, in several steps.
Unfortunately, the base name will change for at least one of the steps, but it would be easy to write these dependencies into a separate file that then is included.
However, I'd like to avoid also writing the build instructions (which are quite complicated) for all these files separately.
I envisage something along these lines:
# automatically generated rules, included into make file
dir1/test.bb: dir2/test_other_name.aa
# (many more rules like the above, linking xxx.bb to yyy.aa)
# pattern rule
%.bb: %.aa
# build step using $# $>
What I would like is the pattern rule to provide the rules, and the explicit rule defining the dependencies. Can something like this be achieved?
When make's noddy patterns don't cut the mustard,
just write out the rules explicitly.
(This has the happy side effect of not using pattern rules.)
Let's say you have a function src-to-target which will generate the target filename (i.e., $(call src-to-target,dir2/test_other_name.aa) expands to dir1/test.bb.
Also, you have a list of sources in ${srcs}, and ${recipe} is a list of shell commands using $#, $< etc.
define src-to-target = ... # $1:source
define recipe =
echo Building $# from $<
⋮
endef
define generate-rule = # $1:source
target := $(call src-to-taget,$1)
targets += $${target}
$${target}: $1 ; $${recipe}
endef
$(foreach _,${srcs},$(eval $(call generate-rule,$_)))
.PHONY: all
all: ${targets} ; : $# Success
The $(foreach ...) does all the work here.
So, looking at that in painful detail,
First expand ${srcs}
Set $_ to the first in the list (dir2/test_other_name.aa say)
Expand $(call generate-rule,$_)
Expand $(call generate-rule,dir2/test_other_name.aa)
$1 is set to dir2/test_other_name.aa, and the expansion of $(generate-rule) follows, leading to this block of text
target := dir1/test.bb
targets += ${target}
${target}: dir2/test_other_name.aa ; ${recipe}
As a side effect, $(eval) swallows the above text. The expansion of the $(eval) though is empty.
$_ is set to the next source file.
Wash, lather, rinse, repeat
Once the $(foreach) is complete,
${targets} contains the complete list of targets.
Parallel safe too.
What's not to like?
I'm trying to figure out how to get file completion to work at any word position on the command line after a set of characters. As listed in a shell these characters would be [ =+-\'\"()] (the whitespace is tab and space). Zsh will do this, but only after the backtick character, '`', or $(. mksh does this except not after the characters [+-].
By word position on the command line, I'm talking about each set of characters you type out which are delimited by space and a few other characters. For example,
print Hello World,
has three words at positions 1-3. At position 1, when you're first typing stuff in, completion is pretty much perfect. File completion works after all of the characters I mentioned. After the first word, the completion system gets more limited since it's smart. This is useful for commands, but limiting where you can do file completion isn't particularly helpful.
Here are some examples of where file completion doesn't work for me but should in my opinion:
: ${a:=/...}
echo "${a:-/...}"
make LDFLAGS+='-nostdlib /.../crt1.o /.../crti.o ...'
env a=/... b=/... ...
I've looked at rebinding '^I' (tab) with the handful of different completion widgets Zsh comes with and changing my zstyle ':completion:*' lines. Nothing has worked so far to change this default Zsh behaviour. I'm thinking I need to create a completion function that I can add to the end of my zstyle ':completion:*' completer ... line as a last resort completion.
In the completion function, one route would be to cut out the current word I want to complete, complete it, and then re-insert the completion back into the line if that's possible. It could also be more like _precommand which shifts the second word to the first word so that normal command completion works.
I was able to modify _precommand so that you can complete commands at any word position. This is the new file, I named it _commando and added its directory to my fpath:
#compdef -
# precommands is made local in _main_complete
precommands+=($words[1,$(( CURRENT -1 ))])
shift words
CURRENT=1
_normal
To use it I added it to the end of my ':completion:*' completer ... line in my zshrc so it works with every program in $path. Basically whatever word you're typing in is considered the first word, so command completion works at every word position on the command line.
I'm trying to figure out a way to do the same thing for file completion, but it looks a little more complicated at first glace. I'm not really sure where to go with this, so I'm looking to get some help on this.
I took a closer look at some of Zsh's builtin functions and noticed a few that have special completion behaviour. They belong to the typeset group, which has a function _typeset in the default fpath. I only needed to extract a few lines for what I wanted to do. These are the lines I extracted:
...
elif [[ "$PREFIX" = *\=* ]]; then
compstate[parameter]="${PREFIX%%\=*}"
compset -P 1 '*='
_value
...
These few lines allow typeset completion after each slash in a command like this:
typeset file1=/... file2=~/... file3=/...
I extrapolated from this to create the following function. You can modify it to put in your fpath. I just defined it in my zshrc like this:
_reallyforcefilecompletion() {
local prefix_char
for prefix_char in ' ' $'\t' '=' '+' '-' "'" '"' ')' ':'; do
if [[ "$PREFIX" = *${prefix_char}* ]]; then
if [[ "$PREFIX" = *[\'\"]* ]]; then
compset -q -P "*${prefix_char}"
else
compset -P "*${prefix_char}"
fi
_value
break
fi
done
}
You can use this by adding it to a zstyle line like this:
zstyle ':completion:*' completer _complete _reallyforcefilecompletion
This way, it's only used as a last resort so that smarter completions can try before it. Here's a little explanation of the function starting with the few variables and the command involved:
prefix_char: This gets set to each prefix character we want to complete after. For example, env a=123 has the prefix character =.
PREFIX: Initially this will be set to the part of the current word from the beginning of the word up to the position of the cursor; it may be altered to give a common prefix for all matches.
IPREFIX (not shown in code): compset moves string matches from PREFIX to IPREFIX so that the rest of PREFIX can be completed.
compset: This command simplifies modification of the special parameters, while its return status allows tests on them to be carried out.
_value: Not really sure about this one. The documentation states it plays some sort of role in completion.
Documentation for the completion system
The function: In the second line, we declare prefix_char local to avoid variable pollution. In line three, we start a for loop selecting each prefix_char we want to complete after. In the next if block, we check if the variable PREFIX ends with one of the prefix_chars we want to complete after and if PREFIX contains any quotes. Since PREFIX contains quotes, we use compset -q to basically allow quotes to be ignored so we can complete in them. compset -P strips PREFIX and moves it to IPREFIX, basically so it gets ignored and completion can work.
The next elif statement is for a PREFIX ending with prefix_char but not containing quotes, so we only use compset -P. I added the return 0 to break the loop. A more correct way to make this function would be in a case statement, but we're not using the compset return value, so this works. You don't see anything about file completion besides _value. For the most part we just told the system to ignore part of the word.
Basically this is what the function does. We have a line that looks like:
env TERM=linux PATH=/<---cursor here
The cursor is at the end of that slash. This function allows PREFIX, which is PATH=, to be ignored, so we have:
env TERM=linux /<---cursor here
You can complete a file there with PATH= removed. The function doesn't actually remove the PATH= though, it just recategorizes it as something to ignore.
With this function, you can now complete in all of the examples I listed in the question and a lot more.
One last thing to mention, adding this force-list line in your zshrc cripples this function somehow. It still works but seems to choke. This new force-list function is way better anyway.
zstyle ':completion:*' force-list always
EDIT: There were a couple lines I forgot to copy into the function. Probably should have checked before posting. I think it's good now.
My zshenv file has a bunch of lines like
if [[ -d "$HOME/bin" ]]; then
path=($HOME/bin $path)
fi
I thought I’d try to factor this pattern out into a function. I replaced it with
function prepend_to_path_if_exists() {
if [[ -d $1 ]]; then
path=($1 $path)
fi
}
prepend_to_path_if_exists("$HOME/bin")
but this gives the error
/Users/bdesham/.zshenv:8: missing end of string
where line 8 is the one where I’m calling prepend_to_path_if_exists. What exactly is causing this error, and how can I make this function work? I’m using zsh 5.0.5 on OS X 10.10.1.
You could call functions as with usual command executions like this (without ()):
prepend_to_path_if_exists "$HOME/bin"
It seems that zsh try to expand the glob prepend_to_path_if_exists(…) rather than to call the function.
TL;DR: Prepending emelemnts to $path would be accomplished by a little cryptic way:
(I'm not quite sure that the below form is preferable for anyone though.)
# `typeset -U` uniqify the elements of array.
# It could be good for $path.
typeset -U path
# prepending some paths unconditionally,
path[1,0]=(\
$HOME/bin \
$HOME/sbin \
)
# then filtering out unnecessary entries afterward.
path=(${^path}(-/N))
The $path[x,0]=… is prepending(splicing) element(s) to array taken from the below:
So that's the same as VAR[1,0]=(...) ? It doesn't really "look" very
much like prepend to me.
-- Greg Klanderman (http://www.zsh.org/mla/workers/2013/msg00031.html)
The ${^path}(-/N) expands the glob qualifires -/N on the each $path elements.
(Without ^ in the parameter expansion, the last elements of array will be evaluated, so it is mandatory in this case.)
The glob qualifires -/N means that "symbolic links and the files they point to"(-) the "directory"(/). And when it does not match anything do not raise errors (N).
In short, it would keep exsisting directories only for $path.