Arithmetic operation on String in makefile without Shell utility - windows

I want to perform Arithmetic operation on string variable, I don't have shell utility in makefile system platform for i386-pc-mingw32 (windows).
Can anybody help me how to perform Arithmetic operation( substraction , comparsion ) on string variable by any means ??

To add a late answer: The GNUmake table toolkit features (in spite of its name) many arithmetic functions. You can add, subtract, multiply, divide, take the modulus in base 8,10 and 16. Also there are the usual binary operations and, or, xor and not. Numbers can be around 60 digits but you can adapt this, if you need more. The code is pure GNUmake syntax and therefore portable between Windows and Unix, contrary to shell scripts - in case you want to number crunch, there may be better solutions ;) of course.
Here is an example:
include gmtt/gmtt.mk
NUMBER_A := -12392834798732429827442389
NUMBER_B := 984398723982791273498234
$(info $(call add,$(NUMBER_A),$(NUMBER_B)))
$(info $(call sub,$(NUMBER_A),$(NUMBER_B)))
$(info $(call mul,$(NUMBER_A),$(NUMBER_B)))
$(info $(call div,$(NUMBER_A),$(NUMBER_B)))
$(info $(call mod,$(NUMBER_A),$(NUMBER_B)))
Output:
$ make
-11408436074749638553944155
-13377233522715221100940623
-12199490762401735834920873237276176262117128241026
-12
-580050110938934545463581

Related

GNU Make/Makefile: Speedup recursive wildcard on windows

I'm using this rwildcard make function (taken from https://stackoverflow.com/a/18258352)
ifeq ($(OS),Windows_NT)
SHELL := cmd
endif
rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d))
INPUT_JavaFileStorageTest-AS = $(call rwildcard,src/java/android-filechooser-AS/app/src,*) $(call rwildcard,src/java/JavaFileStorage/app/src,*) $(call rwildcard,src/java/JavaFileStorageTest-AS/app/src,*.java)
However, I noticed that:
while on linux this is rather fast (cannot perceive the duration)
on Windows this is actually very slow. (10 seconds)
[SHELL is voluntarily set to cmd on windows because the user might not have a POSIX shell in its path].
Any idea why this? How can I improve this?
This should work with both GNU make 3.x & 4.x (because on macOS it is version 3.x that is shipped in the devel command line tools, there is no 4.x there)
EDIT
After investigation the problem wasn't the recursive wildcard. I could speed up by running make --no-builtin-rules or adding MAKEFLAGS += --no-builtin-rules in the Makefile, or adding .SUFFIXES: (with empty value). This removed all the lag observed on Windows.
There is a wildcard-rec function in gmtt which maybe does what you want. The big upside of it is that it is platform-agnostic.
include gmtt.mk
INPUT_JavaFileStorageTest-AS := $(call wildcard-rec,src/java/android-filechooser-AS/app/src/**) \
$(call wildcard-rec,src/java/JavaFileStorage/app/src/**) \
$(call wildcard-rec,src/java/JavaFileStorageTest-AS/app/src/**.java)
** is the recursive-descent glob code which means that the routine will step down into all subdirectories.

How to programmatically generate arguments to user-defined function in GNU Make?

I am attempting a small project using meta-programming in GNU Make, and I have a specialized need to programmatically create all of the arguments that will be passed to some of my user-defined functions.
override CMD:=foo
define foo =
$(info You have called function [$0] with $$1='$1' and $$2='$2')
endef
# this doesn't work
override ARGS:=bar,baz
$(call $(CMD),$(ARGS))
# this doesn't work either
override COMMA:=,
override ARGS:=bar;baz
$(call $(CMD),$(subst ;,$(COMMA),$(ARGS)))
$(info ---)
$(info CMD was '$(CMD)')
$(info ARGS was '$(ARGS)')
$(info COMMA was '$(COMMA)')
The problem I run into is that GNU Make is smarter than I want it to be, and can discern the difference between a syntactical comma, and a comma within a variable or immediate value:
You have called function [foo] with $1='bar,baz' and $2=''
You have called function [foo] with $1='bar,baz' and $2=''
---
CMD was 'foo'
ARGS was 'bar;baz'
COMMA was ','
Are there any sneaky ways to do this so that my 'foo' function sees $1 as 'bar', and $2 as 'baz'? Or will I have to abandon this approach altogether?
NOTE1: space delimiters and $(foreach) is not an adequate solution, as that will just be kicking the can down the road. Ultimately, I need foo to be fooled into thinking it has been passed the expected number of arguments. (Although, I am not opposed to solutions that use $(foreach) as some intermediate step.)
NOTE2: While I don't want to discourage solutions that rely on the programmatic generation of a secondary makefile to be included at the bottom of this one-- as that might be useful to other SO readers-- I am not personally interested in any such approach.
Eval will save you from unnecessary includes:
$(eval $$(call $$(CMD),$(ARGS)))

make rule that invokes another rule several times with different values for a variable

I have a rule something, that works on the variable VAR. I also have another rule something-all, that needs to run something, with VAR set to each value in vars.
vars = hello world
something:
echo $(VAR)
something-all:
$(foreach VAR,$(vars),something)
This doesn't quite work, I get
noob#work:~/Desktop$ make something-all
something something
make: something: No such file or directory
make: *** [something-all] Error 1
It should probably print hello\nworld.
I used to do this with wildcard rules by retrieving VAR from %, but got the feeling that was the wrong way to do it. This looked like this:
vars = hello world
all: $(foreach VAR,$(vars),something-$(VAR))
something-%:
echo $*
The below should fix your problem
Using foreach (Tried on GNU Make 3.80 on sparc-solaris 2.8 and windows)
vars = hello world
something:
echo $(VAR)
something-all:
$(foreach i, $(vars), $(MAKE) something VAR=$i || exit 1;)
Using shell for-loop (Tried on GNU Make 3.80 and cc make on sparc-solaris 2.8)
vars = hello world
something:
echo $(VAR)
something-all:
for i in $(vars); do $(MAKE) something VAR=$$i || exit 1; done
TL;DR: If you want to program make, drop GNU Make in favor of BSD Make.
This is a personal recommendation. While BSD Make seems more limited than GNU Make, as it offers less programming facilities, it is much easier to program and has a few unique killer features. This is why I propose a solution with GNU Make and another solution for BSD Make:
Doing it in GNU Make
Using GNU Make, you can write a macro to define a target. The canonical way to define a sequence in a Makefile is to add the steps of the sequence as dependencies to a target, as reflected by the snippet below:
vars= hello world
define something_t =
something: something-$(1)
something-$(1):
#echo $(1)
endef
$(foreach _,$(vars),$(eval $(call something_t,$_)))
It is recommended to use this organisation (rather than defining just one target), because you can work on it to make the task easily resumable if you interrupt the sequence. A Makefile describes a job whose advancement is entirely described by the state of the file system. A task is then easily resumable, if each step is associated to a file, usually a compilation object but sometimes also an empty file which is touch'ed to indicate that important checkpoints have been passed.
Using an auxiliary macro is a flexible solution that can be adapted to more complicated tasks than just echoing a name. Note that this does work with newest versions of GNU Make (4.1). On GNU Make 3.81, you should remove the equal sign from the macro definition.
Adapting your example for BSD Make
If this is an option for you, I recommand dropping the use of GNU Make and replace it by BSD Make, which is way easier to program: it has a short and to the point documentation, while the documentation of GNU Make is very verbose and somewhat unclear, BSD Make has industrial-strength examples of complex rulesets (FreeBSD Build system or BSD Owl), and it has a simple and predictable macro language.
vars= hello world
something:
.for _var in ${vars}
echo ${_var}
.endfor
This can evolve to support more complicated tasks, just by replacing the echo by the adapted commands, or using intermediary steps.
Allow the user to override some tasks, also in BSD Make
In this slightly more advanced variation, we allow the user to override our own recipes for building targets something-hello and something-world.
For each item in our list, a target something-* is created it if it does not already exist, and added to the dependencies of something. The whole operation of defining these targets only happens if something has been left undefined. Therefore, users of these macros can:
Override the recipes for something-hello and something-world
Override the full procedure bound to something.
Implementing such customisation possibilities is mandatory if we want to write useful, reusable, macros for Make. Unluckily, customisation of this sort is nearly impossible in GNU Make.
vars = hello world
.if!target(depend)
.for _var in ${vars}
.if!target(something-${_var})
something-${_var}:
echo ${_var}
.endif
something: something-${_var}
.endfor
.endif
Here's one way to do it:
VARS := hello world
THINGS := $(addprefix something-, $(VARS))
allthings: $(THINGS)
something-%:
echo $*
It should be no surprise that
vars := hello world
something-all:
$(foreach VAR,$(vars),something)
tries to run something something. That's exactly what the foreach expands to, since you don't reference VAR in the third expression.
All you need to do is reference VAR and use a command such as echo:
vars := hello world
something-all:
$(foreach VAR,$(vars),echo $(VAR);)
$ make
echo hello; echo world;
hello
world
Note how chaining the commands with a semicolon avoids forking several shells or -- GASP! -- recursive make invocations. It doesn't get more performant than that.
Alternatively, if your command accepts several somethings as arguments,
vars := hello world
something-all:
echo $(foreach VAR,$(vars),$(VAR))
$ make
echo hello world
hello world
But that is equivalent to the super simple echo $(vars). So it might pay off to think outside the box trying to change your requirements to make this simple solution work.

Make 3.82 - Backward incompatibility issue?

I'm having some issues after try to run some small Makefile with make 3.82.
error:
[me#localhost make]$ make
Makefile:3: *** empty variable name. Stop.
This works with make 3.81, but not with the new one. I know there are some backward compatibilities with the old version.
I have two Makefiles, a base one and main one.
This is my Makebase
define TestFile
ifeq ($$(shell test $(1) $(2) || echo 1),1)
$$(error $(2) mmm, not found)
endif
endef
define CheckIt
$(eval $(call TestFile,-d,$(1)))
endef
define CheckDir
p := $(foreach d,$1,$(call CheckIt,$d))
endef
define SomeCheck
$(call CheckDir,$(1))
endef
This is my Makefile
include Makebase
$(call SomeCheck, ~/test/make)
As I said, it works fine in make 3.81.
Any help will be appreciated.
Thanks
BR
So, I have no idea what this was intended to do in GNU make 3.81. As Etan points out, when I run your makefile with GNU make 3.81 I get this error:
make: *** No rule to make target `=', needed by `p'. Stop.
That's because a call function cannot expand to a variable assignment, so make interprets the p := as if it were p: = (that is, a target p with a prerequisite of =). I don't see how this is actually what you want. If you don't see this error all I can assume is that somewhere in your makefile, someone has declared a recipe with target = (ugh!!)
In GNU make 3.82 I see the empty variable name message. The reason for this is that GNU make 3.82 introduced parser enhancements which caused some backwards-incompatibility. The NEWS file gives this warning:
As a result of parser enhancements, three backward-compatibility issues
exist: first, a prerequisite containing an "=" cannot be escaped with a
backslash any longer. You must create a variable containing an "=" and
use that variable in the prerequisite.
An unnoticed side-effect of this is that an equals sign with no value before it in the prerequisites list is now considered a target-specific variable where the variable name is empty, whereas before it was assumed to be a target since it didn't meet the requirements for a variable assignment. I am not sure this is a bug... in general I'm not a fan of "tricking" the parser with odd corner cases so I actually prefer the newer behavior.
This entire define is quite bogus:
define CheckDir
p := $(foreach d,$1,$(call CheckIt,$d))
endef
Why? Because the CheckIt user-defined function contains nothing but an eval statement. But eval statements are expanded and the results parsed by make, so they always expand to the empty string. Therefore, the entire foreach loop expands to the empty string. Therefore even if this were interpreted as you (apparently) intended by make, it would always simply expand to:
p :=
which doesn't seem very useful. If you change the above define to simply:
define CheckDir
$(foreach d,$1,$(call CheckIt,$d))
endef
then it will always work, and you won't see these weird problems.
I'm not going to comment on how bogus this makefile is in general... :)

How do I perform arithmetic in a makefile?

Is it possible to perform some operations on variables in a makefile?
For instance, defining
JPI=4
JPJ=2
is it possible to define in the same makefile a variable JPIJ equal to the expanded value of $(JPI)*$(JPJ)?
Using Bash arithmetic expansion:
SHELL=/bin/bash
JPI=4
JPJ=2
all:
echo $$(( $(JPI) * $(JPJ) ))
The first line is to choose the Bash shell instead of the default (sh). Typically, sh doesn't support arithmetic expansion. However in Ubuntu, /bin/sh is provided by Dash, which supports this feature. So that line could be skipped.
The double dollar sign is because we want the expansion to be done by the shell. Note: the JPI and JPJ variables are expanded by make first, then the expression is passed to bash like this:
$(( 4 * 2 ))
Answer from #mrkj is great but as #Daniel mentions, not all systems have bc (for example, I don't have it on MSys).
I have found the two following methods, both using shell: $$(( ... )) and expr ...
JPI=4
JPJ=2
#With Double-dollar
JPIJ_1 = $(shell echo $$(( $(JPI) + $(JPJ) )))
#With 'expr'
JPIJ_2 = $(shell expr $(JPI) + $(JPJ) )
$(info Sum with Double-$$: $(JPIJ_1))
$(info Sum with 'expr': $(JPIJ_2))
Note that when using expr, you shall put spaces around the + or it will return 4+2. This is not required when using $$.
.
When you have bc available, you might definitely go with it. I found the following page very interesting: http://www.humbug.in/2010/makefile-tricks-arithmetic-addition-subtraction-multiplication-division-modulo-comparison/
If you're using GNU make and have bc installed on your system, you can use something like this:
JPI=4
JPJ=2
FOO=$(shell echo $(JPI)\*$(JPJ) | bc)
all:
echo $(FOO)
It's clumsy (or brilliant, depending on your perspective), but you can do arithmetic directly in GNU make. See Learning GNU Make Functions with Arithmetic. Be aware though that this method doesn't scale well. It will work wonderfully for small numbers as you have shown in your question, but it doesn't do well when you're working with numbers with a large magnitude (greater than 10,000,000).
In GNU Make with Guile support (i.e. since version 4.0) it is easy to use call to Scheme language for arithmetic or other calculations. It is done without creating any subshell or child process.
An example
JP-I := 4
JP-J := 2
JP-IJ := $(guile (* $(JP-I) $(JP-J) ))
$(info JP-IJ = $(JP-IJ) )
# prints: JP-IJ = 8
See also the manual for Guile Arithmetic Functions.
A possible check for Guile:
ifeq (,$(filter guile,$(.FEATURES)))
$(error Your Make version $(MAKE_VERSION) is not built with support for Guile)
endif
The GNU Make Standard Library provides integer arithmetic functions.
include gmsl
JPI = 4
JPJ = 2
JPIJ = $(call plus,$(JPI),$(JPJ))
With makepp it's much easier. You get direct access to the underlying Perl interpreter. In this case the makeperl function does variable expansion before evaluating as Perl, the perl function OTOH would only evaluate:
JPI=4
JPJ=2
JPIJ = $(makeperl $(JPI)*$(JPJ))
&echo result: $(JPIJ)
You can use the builtin &echo command outside of a rule as a statement.
To add a late answer to the pool: The GNUmake table toolkit features many arithmetic functions. You can add, subtract, multiply, divide, take the modulus in base 8,10 and 16. Also there are the usual binary operations and, or, xor and not. Numbers can be around 60 digits but you can adapt this, if you need more. The code is pure GNUmake syntax and therefore portable between Windows and Unix, contrary to shell scripts - in case you want to number crunch, there may be better solutions ;) of course.
Here is an example:
include gmtt/gmtt.mk
NUMBER_A := -12392834798732429827442389
NUMBER_B := 984398723982791273498234
$(info $(call add,$(NUMBER_A),$(NUMBER_B)))
$(info $(call sub,$(NUMBER_A),$(NUMBER_B)))
$(info $(call mul,$(NUMBER_A),$(NUMBER_B)))
$(info $(call div,$(NUMBER_A),$(NUMBER_B)))
$(info $(call mod,$(NUMBER_A),$(NUMBER_B)))
Output:
$ make
-11408436074749638553944155
-13377233522715221100940623
-12199490762401735834920873237276176262117128241026
-12
-580050110938934545463581
After an hour of hair pulling, I ended up on this (assuming you got hexadecimal and decimal numbers mixed in like my use case):
JPI = 0x123
JPJ = 2
MUL = $(shell python3 -c "print($(JPI)*$(JPJ))")

Resources