Suppose I have made some non-conventional use of make. Suppose further that I've come up with a name scheme within this usage that is elegant and useful with tools other than make, and which is almost fitting perfectly into the make paradigm.
I can almost leverage make built-ins to accomplish this task, and am looking for a bit of magic to make it 'just work' within the semantics and grammar of the system.
Right now, I have a buildable process that depends as follows:
# "<-" = "depends on"
foo.bar.biz.baz.new <- foo.bar.biz.baz.extension bar.biz.baz.new
bar.biz.baz.new <- bar.biz.baz.extension biz.baz.new
biz.baz.new <- biz.baz.extension baz.new
baz.new <- baz.extension
# and the following rules should suffice, where $(magic ...) is a stand-in for what I'd like to do:]
SHELL:=/usr/bin/env bash
.SECONDEXPANSION
%.new: $$*.extension $(magic $$*)
...
build: foo.bar.biz.baz.new
What I am looking for is some magic swapper that:
a...b -> ...c if ... != ""
# so, it chops off the prefix (dot-delimited), a
# and swaps the suffix only if something is present other than the suffix
I think this could be:
$(filter-out .c, $(patsubst %,%.c $(not-prefix $$*)))
If not-prefix existed, and I am wondering whether there is some built-in that more or less accomplishes this task out of the box?
Alternatively, is there some really compact bashism that can accomplish this?
For example:
$(shell echo "$$*" | sed -E 's/[^.]*(.*)\.[^.]*$$/\1.new/' | grep -v -E '^\.new$$')
Is super windy and verbose, but maybe there is some compact way to do this?
The most readable I've come up with so far:
%.new: $$*.extension $(filter-out .new, $(shell echo "$$#" | cut -d '.' -f 1 --complement))
Update Minimal verifiable example of the tree-build recursion problem:
%.ext:
#touch $#
.SECONDEXPANSION:
%.new: $$*.ext $(filter-out new, $(shell echo "$$#" | cut -d '.' -f 1 --complement))
#echo "#: $#"
#echo "<: $<"
#echo "^: $^"
#echo $(filter-out new, $(shell echo "$#" | cut -d '.' -f 1 --complement))
$ make descendant.ancestor.new
#: descendant.ancestor.new
<: descendant.ancestor.ext
^: descendant.ancestor.ext
ancestor.new
rm descendant.ancestor.ext
Next, I add in the second "$" sign to the rule, so that it executes during the second expansion instead:
%.ext:
#touch $#
.SECONDEXPANSION:
%.new: $$*.ext $$(filter-out new, $$(shell echo "$$#" | cut -d '.' -f 1 --complement))
#echo "#: $#"
#echo "<: $<"
#echo "^: $^"
#echo $(filter-out new, $(shell echo "$#" | cut -d '.' -f 1 --complement))
Which yields:
$ make descendant.ancestor.new
make: *** No rule to make target 'descendant.ancestor.new'. Stop.
Ultimately, I think this should be something like:
.SECONDEXPANSION:
%.new: $$*.ext | $$(filter-out new, $$(shell echo "$$#" | cut -d '.' -f 1 --complement))
But instead what I have done to make it "just work" is:
%.ext:
#touch $#
.SECONDEXPANSION:
%.new: $$*.ext .FORCE
#echo "#: $#"
#echo "<: $<"
#echo "^: $^"
#$(eval prior=$(filter-out new, $(shell echo "$#" | cut -d '.' -f 1 --complement)))
#$(if $(prior),$(MAKE) $(prior))
#echo "ta-da: idempotent, depth first tree recursion #:$#"
.FORCE:
Which yields a more brittle behavior than the rule line:
$ make -s branch.root.new
#: branch.tree.new
<: branch.tree.ext
^: branch.tree.ext .FORCE
#: tree.new
<: tree.ext
^: tree.ext .FORCE
ta-da: idempotent, depth first tree recursion #:tree.new
ta-da: idempotent, depth first tree recursion #:branch.tree.new
OK thanks for clarifying what you're looking for. So, I don't have anything I would consider "dead simple" but you can do it wholly with GNU make using this:
e =
sp = $e $e
magic1 = $(if $(filter-out 1,$(words $(subst ., ,$1))),$1,)
magic = $(call magic1,$(subst $(sp),.,$(wordlist 2,1000,$(subst ., ,$1))))
Now wherever you want "magic" to happen, call the magic function:
.SECONDEXPANSION:
%.new: $$*.ext $$(call magic,$$#)
The magic1 macro expands to its argument if its argument has >1 ., else expands to the empty string. The magic macro strips off the first "prefix" and calls magic1 with that value.
There may be simpler ways to do this, I didn't come up with one off the top of my head.
Also, I didn't review your edited question so I'm not sure if something you added there makes a difference here.
The problem here is that most build systems depend on stable prefixes and suffixes, with only varying stems (middle parts).
Here, you have an arbitrarily varying prefix, as well as middle part. So for instance bar.biz.baz is the common stem among all the names in the first dependency situation. It is arbitrary. However, each one of the names involved also has an independently arbitrary prefix, like foo.
To avoid fighting the tool, I would try to encode foo and biz.bar.baz, and every other such pair, together in some variables that act as the primary source of this information.
Sample Makefile. In this solution we use nothing but call, firstword, secondword a one-character subst, foreach and eval:
UNITS := foo#biz.bar.baz bar#biz.baz biz#baz
# $(call FIRST, foo#bar) -> foo
FIRST = $(firstword $(subst #, ,$(1)))
# $(call REST, foo#bar) -> bar
REST = $(lastword $(subst #, ,$(1)))
TARGETS := $(foreach U,$(UNITS),$(call FIRST,$(U)).$(call REST,$(U)).new)
DEPS := $(foreach U,$(UNITS),$(call FIRST,$(U)).$(call REST,$(U)).extension) \
$(foreach U,$(UNITS),$(call REST,$(U)))
.PHONY: all $(TARGETS) $(DEPS)
all: $(TARGETS)
define RULE
$(1).$(2).new: $(1).$(2).extension $(2)
#echo BUILD $$# '<-' $$^
endef
$(foreach U,$(UNITS),\
$(eval $(call RULE,$(call FIRST,$(U)),$(call REST,$(U)))))
Output:
$ make
BUILD foo.biz.bar.baz.new <- foo.biz.bar.baz.extension biz.bar.baz
BUILD bar.biz.baz.new <- bar.biz.baz.extension biz.baz
BUILD biz.baz.new <- biz.baz.extension baz
Alternative, using computed variable names. Here, in addition to computed variables, which simulate data structuring, we are only using foreach, call and eval. No string processing at all, other than the nested variable expansion:
UNITS := mercury mars venus
mercury.head := foo
mercury.tail := biz.bar.baz
mars.head := bar
mars.tail = biz.baz
venus.head := biz
venus.tail := baz
TARGETS := $(foreach U,$(UNITS),$($(U).head).$($(U).tail).new)
DEPS := $(foreach U,$(UNITS),$($(U).head).$($(U).tail).extension) \
$(foreach U,$(UNITS),$($(U).tail))
.PHONY: all $(TARGETS) $(DEPS)
all: $(TARGETS)
define RULE
$(1).$(2).new: $(1).$(2).extension $(2)
#echo BUILD $$# '<-' $$^
endef
$(foreach U,$(UNITS),\
$(eval $(call RULE,$($(U).head),$($(U).tail))))
What I am looking for is some magic swapper that:
a...b -> ...c if ... != ""
# so, it chops off the prefix (dot-delimited), a
# and swaps the suffix only if something is present other than the suffix
I don't completely understand the operation you want to execute, but the GNUmake table toolkit has, despite its name, also some practical string operations to offer:
include gmtt.mk
TEST := $(call glob-match,aprefix.thensomething.somemore.thenapostfix,aprefix.*.*apostfix)
$(info $(TEST))
$(if $(TEST),$(info Yes, string looks like),$(info No, string doesn't look like))
$(info aprefix..apostfix)
$(if $(TEST),$(info The variable contents of the string is "$(word 2,$(TEST))" and "$(word 4,$(TEST))"))
Output:
aprefix. thensomething . somemore.then apostfix
Yes, string looks like
aprefix..apostfix
The variable contents of the string is "thensomething" and "somemore.then"
PS: I want to note that the .* sequence in the pattern does not constitute the regex [sequence of 0..n arbitrary characters] but a glob pattern, where the * alone stands for repetition of 0..n arbitrary characters. The . is literal here.
EDIT:
Here is a solution with dynamic rules (to really generate them, replace info with eval:
include gmtt/gmtt.mk
gen-rules = \
$(if $(call glob-match,$1,*.*.new),\
$(info $1 : $(firstword $(call glob-match,$1,*.new)).extension $(word 3,$(call glob-match,$1,*.*)))\
$(call gen-rules,$(word 3,$(call glob-match,$1,*.*))),\
$(info $1 : $(patsubst %.new,%.extension,$1)))
$(foreach cmdline-target,$(filter %.new,$(MAKECMDGOALS)),$(call gen-rules,$(cmdline-target)))
I am relatively new to Makefiles.
I have the following (simplified) code as part of my Makefile.
I wish to know if, target and the recipe can be declared inside
else statement. Is this valid to write recipe inside ifeq block.
If not can you suggest how to improve it.
CUR_PI_VERSION:= abc
CUR_GIT_VERSION:= cde
LAST_PI_VERSION:= bbc
LAST_GIT_VERSION:= cde
$(info $$CUR_GIT_VERSION is [${CUR_GIT_VERSION}])
$(info $$CUR_PI_VERSION is [${CUR_PI_VERSION}])
$(info $$LAST_GIT_VERSION is [${LAST_GIT_VERSION}])
$(info $$LAST_PI_VERSION is [${LAST_PI_VERSION}])
ABC_AG := ./abc_ag.py
ABC_KG := ./abc_kg.vhd
ABC_OC := ./abc_ef.txt
ifeq ($(CUR_PI_VERSION)#$(CUR_GIT_VERSION),$(LAST_PI_VERSION)#$(LAST_GIT_VERSION))
| #echo "Everything matched, so don't need the make top"
else
# is this valid !!!
.PHONY: $(ABC_KG)
$(ABC_AG) $(ABC_KG) $(ABC_OC):
printf "\t TEST \n"
endif
clean: clean_fpi
clean_fpi:
#rm -f $(ABC_KG); \
| rm -f $(ABC_OC); \
| rm -f $(ABC_AG);
Thanks. Any feedback is appreciated.
I have two variables in the makefile they can be empty or having empty string OR valid string
I need to error exit in case either of the variable is empty or empty string
Here is simple makefile I am using
ABC := ""
XYZ := hello
all:
ifeq ($(and $(ABC),$(XYZ)),)
$(error "Either of var is null")
endif
#echo "Done"
With this I get output as Done While I want it to fail.
If I change ifeq condition as follows,
ifeq ($(and $(ABC),$(XYZ)),"") then in following condition make is not error exiting
ABC :=
XYZ := hello
all:
ifeq ($(and $(ABC),$(XYZ)),"")
$(error "Either of var is null")
endif
#echo "Done"
One solution could be as follows,(?)
ABC := hello
XYZ := hello
all:
ifeq ($(and $(ABC),$(XYZ)),)
$(error "Var is null")
endif
ifeq ($(and $(ABC),$(XYZ)),"")
$(error "Var is null2")
endif
#echo "Done"
However I feel there could a better way of doing it, Any suggestions ?
EDIT
Just to explain what I want is,
if ABC is empty string(ABC := "") OR empty(ABC := ) OR
XYZ is empty string(XYZ := "") OR empty(XYZ := )
$(error "empty string or null")
endif
Just to be clear, make doesn't care about quotes in any way. When make talks about "empty" variables it means variable with no value. If you write:
ABC := ""
then that variable has a value, the literal characters "". To make, that's no different from assigning ab etc. (at least in how make interprets those values).
For your problem you can use something like:
ifeq (,$(subst ",,$(ABC)$(XYZ)))
$(error empty string or null)
endif
which will replace all quotes with nothing; if the result of that is an empty string then you know the variables were either empty or contained nothing but quotes.
Note that this will also cause variables that contain only one quote, or more than two quotes, to be considered empty; e.g.,
ABC := "
XYZ := """""""""""""
will also be considered empty. If you really want only to consider exactly two quotes to be empty then you need something more fancy.
Ack, I don't have enough reputation to make comments, but it appears as though none of the above actually answer your question. Concatenation is the functional equivalent of $(or, not $(and, so $(ABC)$(XYZ) is not equivalent to $(and $(ABC),$(XYZ)) (and $(subst ",,""xxx) will not be blank either).
Also, your second example in your question will not work, as if $(ABC) is "", then $(and $ABC,xxx) will be xxx, not "".
What you need is a macro, say unquote to remove the qoutes, and then do:
unquote=$(subst ",,$(1))
ifeq ($(and $(call unquote,$(ABC)),$(call unquote,$(XYZ))),)
Which is a bit ugly. You could of course change unquote to only simplify empty quoted strings.
This is an example of use of DEFAULT_GOAL Variable:
ifeq ($(.DEFAULT_GOAL),)
$(warning no default goal is set)
endif
.PHONY: foo
foo: ; #echo $#
$(warning default goal is $(.DEFAULT_GOAL))
# Reset the default goal.
.DEFAULT_GOAL :=
.PHONY: bar
bar: ; #echo $#
$(warning default goal is $(.DEFAULT_GOAL))
# Set our own.
.DEFAULT_GOAL := foo
The output is:
no default goal is set
default goal is foo
default goal is bar
foo
I stuck up understanding that what is the flow of the echo and $(warning ) function i.e. when $(warning ) function is called the echo $# output is suppressed and last output of echo $# is displayed. Because there is 2 echo statement and 3 $(warning ) function call but only one target id printed by echo the last one foo . Why others are not printed and why the last value is printed as foo why not bar?
The warnings and assignments happen as make reads the Makefile. Only then does it begin to decide which of the rules to execute. The other echo statements are never executed because it only makes the default goal at that point, which is foo.
It was suggested in the IS newsgroup to use /D= but using the iscc.exe that came with version 5.2.3 I get an "Unknown option:" error.
Then in the script, how do you use the value of the command line parameter?
You do, as MicSim says, need the preprocessor. It's included in the latest ISPack. Once it's installed, iscc supports /D.
You can then use the values defined like this (assuming you'd done /DVERSION_NAME=1.23):
AppVerName=MyApplication v{#VERSION_NAME}
From the Inno Setup helpfile:
Inno Setup Preprocessor replaces the
standard Inno Setup Command Line
Compiler (ISCC.exe) by an extended
version. This extended version
provides extra parameters to control
Inno Setup Preprocessor.
The "extra parameters" include the /d option.
The point of the answer by #Steven Dunn is to solve the problem with another layer of abstraction: instead of calling iscc your_script.iss directly from the terminal, call your_script.ps1 -YourVar "value", process the switch, write a #define to the .iss file, and then compile it with iscc. This was not articulated well and I don't think the function shown to parse command line arguments added much value. However, I'm giving credit where credit is due.
As #jdigital mentioned, ISPP has the /d switch, but ISPP can't be run directly from the terminal (AFAIK). Hence, something like a secondary scripted approach hinted at by #Steven Dunn is necessary.
You can achieve this by adding placeholders to an existing .iss script and then overwrite them accordingly:
.iss Template
; -- template.iss --
#define MyVar ""
...
.ps1 Script
#requires -PSEdition Core
# create_iss_script.ps1
param(
[Parameter(Mandatory=$true)][String]$MyVar,
[Parameter(Mandatory=$true)][String]$OutFile,
)
$File = '.\template.iss'
$LineToReplace = '#define MyVar ""'
$NewLine = "#define MyVar ""${MyVar}"""
$FileContent = Get-Content -Path $File -Raw
$FileContent.Replace($LineToReplace, $NewLine) | Set-Content -Path $OutFile
Example Terminal Usage
PS> .\create_iss_script.ps1 -MyVar "HelloWorld!" -OutFile "helloworld.iss"
PS> iscc .\helloworld.iss
or run the iscc step from within your .ps1 script, if you prefer.
If you want to parse command line arguments from code in inno, then use a method similar to this. Just call the inno script from the command line as follows:
C:\MyInstallDirectory>MyInnoSetup.exe -myParam parameterValue
Then you can call the GetCommandLineParam like this wherever you need it:
myVariable := GetCommandLineParam('-myParam');
//==================================================================
{ Allows for standard command line parsing assuming a key/value organization }
function GetCommandlineParam (inParam: String):String;
var
LoopVar : Integer;
BreakLoop : Boolean;
begin
{ Init the variable to known values }
LoopVar :=0;
Result := '';
BreakLoop := False;
{ Loop through the passed in arry to find the parameter }
while ( (LoopVar < ParamCount) and
(not BreakLoop) ) do
begin
{ Determine if the looked for parameter is the next value }
if ( (ParamStr(LoopVar) = inParam) and
( (LoopVar+1) < ParamCount )) then
begin
{ Set the return result equal to the next command line parameter }
Result := ParamStr(LoopVar+1);
{ Break the loop }
BreakLoop := True;
end
{ Increment the loop variable }
LoopVar := LoopVar + 1;
end;
end;
Hope this helps...