Computed macro names - nmake

The GNU Make program proves the feature of computed names. I have to use Microsoft nmake program and need to check if either a macro has a specified value or is at least defined.
The makefile defines a macro FOO with the value DEVICE. Further it can be that the macro PLAT_DEVICE is defined with the value 1. In GNU make syntax you could use
FOO=DEVICE
PLAT_DEVICE=1
!if "$(PLAT_$(FOO))" == "1"
!message I am here.
!endif
The value of the macro FOO defines what other macro is checked here. Unfortunately nmake doesn't understand this. The condition evaluates always to false, the message is never shown.
How can I implement this with nmake?

Edit: This is my first answer, and is not that useful. See my second answer on using environment variables.
Unfortunately, NMAKE does not have Computed Variable Names as in GNU Make.
But it does allow the construction of macro names from other macros: see https://learn.microsoft.com/en-us/cpp/build/defining-an-nmake-macro?view=vs-2017.
So the following work-around may work, depending on your situation:
DEVICE = 1
PLAT_DEVICE_$(DEVICE) =
!ifdef PLAT_DEVICE_1
!message I am here.
!endif
Or, as minor variation of this idea:
DEVICE = 1
PLAT_DEVICE_$(DEVICE) = plat_device
!if "plat_device" == "$(PLAT_DEVICE_1)"
!message I am here.
!endif

Short answer: you can use environment variables instead of make macros to compute the names, For example, this makefile:
FOO=DEVICE
PLAT_DEVICE=1
!if [set PLAT_DEVICE=$(PLAT_DEVICE)] # add macro to environment
!endif
!if [cmd /c if "%PLAT_$(FOO)%"=="1" exit 1] # do test
!message I am here.
!endif
will give:
>nmake -l
I am here.
Or this variant,
FOO=DEVICE
PLAT_DEVICE=42
!if [set FOO=$(FOO)] && \
[set PLAT_DEVICE=$(PLAT_DEVICE)]
!endif
!if [cmd /c if "%PLAT_$(FOO)%"=="42" exit 1]
!message Test 1a: I am here.
!endif
!if [cmd /c if "%PLAT_%FOO%%"=="42" exit 1]
!message Test 1b: I am here too.
!endif
all:
# echo Test 2a: %%PLAT_$(FOO)%%
#call echo Test 2b: %%PLAT_%%FOO%%%%
will give:
>nmake -l
Test 1a: I am here.
Test 1b: I am here too.
Test 2a: 42
Test 2b: 42
One major drawback in this answer is that the exporting has to be done explicitly for each make macro. The nmake -l switch is a just an abbreviation for /nologo (undocumented?).
Longer answer. The above makefile uses several techniques or tricks. I will try to unravel these in a list of four items.
Item 1: First note nmake, unlike GNU make, has persistent environment variables (aka variables in nmake). For example,
all: bar
#echo Test 3: %%BAR%%
bar:
#set BAR=Hello World!
gives:
>nmake -l
Test 3: Hello World!
Item 2: You can convert macros to variables, or create a variable, in at least two places:
in a recipe command line, as shown in Item 1, or
in a command executed during preprocessing, in square brackets [...].
For example,
AAA = FOO
BBB = BAR
FOO_BAR = This works!
FOO_BAZ = This also works!
!if [set FOO_BAR=$(FOO_BAR)] && \
[set FOO_BAZ=$(FOO_BAZ)] && \
[set CCC=%$(AAA)_$(BBB)%]
!endif
all:
#echo Test 4: %%$(AAA)_$(BBB)%%
#echo Test 5: %%CCC%%
gives:
>nmake -l
Test 4: This works!
Test 5: This works!
>nmake -l BBB=BAZ
Test 4: This also works!
Test 5: This also works!
Two odd points about this. First, it seems each variable must be set with its own command. For example,
!if [set FOO_BAR=$(FOO_BAR) && set FOO_BAZ=$(FOO_BAZ)]
!endif
does not work (I may be missing something obvious here). Second, the && connective is almost irrelevant here: it does not short-circuit in nmake, and we are discarding the the result, so probably anything else like + would work just as well.
Item 3: Item 2 doesn't really illustrate nesting: the nesting shown is a macro within a variable. But true nesting does work. The makefile:
AAA = FOO
BBB = BAR
FOO_BAR = This works!
!if [set AAA=$(AAA)] && \
[set BBB=$(BBB)] && \
[set FOO_BAR=$(FOO_BAR)]
!endif
!if [cmd /c if "%%AAA%_%BBB%%"=="This works!" exit 1]
!message Test 6: I am here
!endif
all:
#call echo Test 7: %%%%AAA%%_%%BBB%%%%
will give:
>nmake -l
Test 6: I am here
Test 7: This works!
In the recipe, the call seems to be needed simulate delayed expansion; see Stephan's answer to Variables are not behaving as expected. It is not needed in the Test 6, =="This works!" test.
Item 4: The preprocessing tests shown in:
!if [cmd /c if "%PLAT_$(FOO)%"=="42" exit 1]
!message Test 1a: I am here.
!endif
!if [cmd /c if "%PLAT_%FOO%%"=="42" exit 1]
!message Test 1b: I am here too.
!endif
are similar to the Unix test command, except here TRUE=1.

Related

Check a c file output in Linux

I have 2 files .c which only contain a printf("x")
I am in bash script and i want to check if the values in the printf are for project1.c =20 and for project 2 =10,and then make some changes depending on the values.
How am i supposed to make the comparison in the if command?
This is what i have tried to do,not sure if it is right way.
for d in $1/*/*
do
gcc project1 project1.c
if[ ./project1 = 20 ];then
$project1 =30
else
$project1 =0
fi
gcc project2 project2.c
if[ ./project2 =10 ];then
$project2 = 70
else
$project2 = 0
fi
sum=$project1 + $project2
echo "project1 : $project1 project2: $project2 total grade: $sum" >> grades.txt
done
fi
Your invocation of gcc is wrong. You have to specify the output file:
gcc -o project1 project1.c
Next, in shell, variable substitution is a different process than assignment. So, you can't write $var=foo. The correct syntax is var=foo.
Then, space is a special character (it is used to separate arguments). So var=foo is not the same than var = foo. So, the correct syntax is:
project1=30
Next, in shell, the pattern $(command) is replaced by the result of command. So. I have to do:
if [ $(./project2) == 10 ]; then
Finally, you can do arithmetic using $((calculus)). So, you have to write:
sum=$(($project1 + $project2))

Passing more than one parameter to another nsi

I have an installer I inherited, and I need to pass 2 parameters to another *.nsi on install. Currently it works fine with one param, which is just a string:
ExecShell "" '"$TEMP\Setup.exe"' "Param1"
This gets read as so, from the other side:
${GetParameters} $commandLineParam
The second param is a variable ($version) that needs to be sent over:
StrCpy $version "1.1.1.0"
Just adding an additional "Param2" doesn't build
Error 13 error MSB3721: The command ""C:\Program Files (x86)\NSIS\Unicode\makensis" ... exited with code 1.
I'm sure I'm missing something syntax-wise.
MakeNSIS removes the outer-most set of quotes when it parses the .NSI file.
StrCpy $0 "blah"
StrCpy $1 '"baz"'
ExecShell "" "c:\foo\bar.exe" 'p1 "Hello World" p3 $0 $1'
will run c:\foo\bar.exe with p1 "Hello World" p3 blah "baz" as the parameters.

NSIS: Despite preproccsor get warning unknown variable/constant "test" detected

I use only use some code if the Flag is set. So I got the warning
Variable "test" not referenced or never set, wasting memory!
so I use the preprocessor commands
!if ${Flag} == 1
Var test
!endif
But I still get a warning
unknown variable/constant "test" detected, ignoring (macro:_==:1)
So why do I still get a warning and how could I disable the warning:
Maybe I could use !pragma to disable the warning. But this works only for nsis3. What could I do at nsis2?
You most likely messed up your !if guards around $test.
!define Flag 1
!if ${Flag} == 1
Var test
!endif
will print Variable "test" not referenced or never set, wasting memory! if you never access $test in a Section/Function.
On the other hand
Section
${If} $test == "something"
${EndIf}
SectionEnd
will print unknown variable/constant "test" detected, ignoring (macro:_==:1) if the code does not do Var test first.
And finally
Var test
Section
${If} $test == "something"
${EndIf}
SectionEnd
will also print Variable "test" not referenced or never set, wasting memory! for some reason but the warning goes away if you actually assign something to $test somewhere in your code:
Var test
Function .onInit
StrCpy $test ""
FunctionEnd
Section
${If} $test == "something"
${EndIf}
SectionEnd

NSIS: How to check at compile time if an environment variable exists

!echo "$%MY_VAR%"
Prints out
$%MY_VAR% (...\installer.nsi:46)
So the variable isn't replaced at all. Is this a bug? I'm a bit stumped here. How am I supposed to test if the variable exists? So far I thought I could use
!if "$%MY_VAR%" = ""
but that's not going to work if there is no replacement at all. The logical conclusion would be to use this:
!if "$%MY_VAR%" = "$%MY_VAR%"
Am I doing it wrong?
!if x = y is actually a number test, you need to use == to test strings.
You need to trick the compiler to get this to work:
; NSIS 2+
!define DOLLAR $
!if "$%foo%" == "${DOLLAR}%foo%"
!echo "%foo% not set"
!endif
; NSIS 3+
!if "$%foo%" == "${U+24}%foo%"
!echo "%foo% not set"
!endif
I just did a quick test (NSIS 3.0a0 and Unicode NSIS 2.46.5, both on Windows 7 x64) and I have no problem getting the $%ENV_VAR% replaced. Here's how I'm testing it:
!if "$%MY_VAR%" != ""
!echo "MY_VAR is set"
!else
!warning "MY_VAR not set"
!endif
I take it you already double checked your environmental variable was really saved

Is there a "goto" statement in bash?

Is there a "goto" statement in bash ? I know It is considered bad practice, but I need specifically "goto".
If you are using it to skip part of a large script for debugging (see Karl Nicoll's comment), then if false could be a good option (not sure if "false" is always available, for me it is in /bin/false):
# ... Code I want to run here ...
if false; then
# ... Code I want to skip here ...
fi
# ... I want to resume here ...
The difficulty comes in when it's time to rip out your debugging code. The "if false" construct is pretty straightforward and memorable, but how do you find the matching fi? If your editor allows you to block indent, you could indent the skipped block (then you'll want to put it back when you're done). Or a comment on the fi line, but it would have to be something you'll remember, which I suspect will be very programmer-dependent.
No, there is not; see ยง3.2.4 "Compound Commands" in the Bash Reference Manual for information about the control structures that do exist. In particular, note the mention of break and continue, which aren't as flexible as goto, but are more flexible in Bash than in some languages, and may help you achieve what you want. (Whatever it is that you want . . .)
It indeed may be useful for some debug or demonstration needs.
I found that Bob Copeland solution http://bobcopeland.com/blog/2012/10/goto-in-bash/ elegant:
#!/bin/bash
# include this boilerplate
function jumpto
{
label=$1
cmd=$(sed -n "/$label:/{:a;n;p;ba};" $0 | grep -v ':$')
eval "$cmd"
exit
}
start=${1:-"start"}
jumpto $start
start:
# your script goes here...
x=100
jumpto foo
mid:
x=101
echo "This is not printed!"
foo:
x=${x:-10}
echo x is $x
results in:
$ ./test.sh
x is 100
$ ./test.sh foo
x is 10
$ ./test.sh mid
This is not printed!
x is 101
You can use case in bash to simulate a goto:
#!/bin/bash
case bar in
foo)
echo foo
;&
bar)
echo bar
;&
*)
echo star
;;
esac
produces:
bar
star
If you're testing/debugging a bash script, and simply want to skip forwards past one or more sections of code, here is a very simple way to do it that is also very easy to find and remove later (unlike most of the methods described above).
#!/bin/bash
echo "Run this"
cat >/dev/null <<GOTO_1
echo "Don't run this"
GOTO_1
echo "Also run this"
cat >/dev/null <<GOTO_2
echo "Don't run this either"
GOTO_2
echo "Yet more code I want to run"
To put your script back to normal, just delete any lines with GOTO.
We can also prettify this solution, by adding a goto command as an alias:
#!/bin/bash
shopt -s expand_aliases
alias goto="cat >/dev/null <<"
goto GOTO_1
echo "Don't run this"
GOTO_1
echo "Run this"
goto GOTO_2
echo "Don't run this either"
GOTO_2
echo "All done"
Aliases don't usually work in bash scripts, so we need the shopt command to fix that.
If you want to be able to enable/disable your goto's, we need a little bit more:
#!/bin/bash
shopt -s expand_aliases
if [ -n "$DEBUG" ] ; then
alias goto="cat >/dev/null <<"
else
alias goto=":"
fi
goto '#GOTO_1'
echo "Don't run this"
#GOTO1
echo "Run this"
goto '#GOTO_2'
echo "Don't run this either"
#GOTO_2
echo "All done"
Then you can do export DEBUG=TRUE before running the script.
The labels are comments, so won't cause syntax errors if disable our goto's (by setting goto to the ':' no-op), but this means we need to quote them in our goto statements.
Whenever using any kind of goto solution, you need to be careful that the code you're jumping past doesn't set any variables that you rely on later - you may need to move those definitions to the top of your script, or just above one of your goto statements.
Although others have already clarified that there is no direct goto equivalent in bash (and provided the closest alternatives such as functions, loops, and break), I would like to illustrate how using a loop plus break can simulate a specific type of goto statement.
The situation where I find this the most useful is when I need to return to the beginning of a section of code if certain conditions are not met. In the example below, the while loop will run forever until ping stops dropping packets to a test IP.
#!/bin/bash
TestIP="8.8.8.8"
# Loop forever (until break is issued)
while true; do
# Do a simple test for Internet connectivity
PacketLoss=$(ping "$TestIP" -c 2 | grep -Eo "[0-9]+% packet loss" | grep -Eo "^[0-9]")
# Exit the loop if ping is no longer dropping packets
if [ "$PacketLoss" == 0 ]; then
echo "Connection restored"
break
else
echo "No connectivity"
fi
done
This solution had the following issues:
Indiscriminately removes all code lines ending in a :
Treats label: anywhere on a line as a label
Here's a fixed (shell-check clean and POSIX compatible) version:
#!/bin/sh
# GOTO for bash, based upon https://stackoverflow.com/a/31269848/5353461
goto() {
label=$1
cmd=$(sed -En "/^[[:space:]]*#[[:space:]]*$label:[[:space:]]*#/{:a;n;p;ba};" "$0")
eval "$cmd"
exit
}
start=${1:-start}
goto "$start" # GOTO start: by default
#start:# Comments can occur after labels
echo start
goto end
# skip: # Whitespace is allowed
echo this is usually skipped
# end: #
echo end
There is one more ability to achieve a desired results: command trap. It can be used to clean-up purposes for example.
There is no goto in bash.
Here is some dirty workaround using trap which jumps only backwards:)
#!/bin/bash -e
trap '
echo I am
sleep 1
echo here now.
' EXIT
echo foo
goto trap 2> /dev/null
echo bar
Output:
$ ./test.sh
foo
I am
here now.
This shouldn't be used in that way, but only for educational purposes. Here is why this works:
trap is using exception handling to achieve the change in code flow.
In this case the trap is catching anything that causes the script to EXIT. The command goto doesn't exist, and hence throws an error, which would ordinarily exit the script. This error is being caught with trap, and the 2>/dev/null hides the error message that would ordinarily be displayed.
This implementation of goto is obviously not reliable, since any non-existent command (or any other error, for that manner), would execute the same trap command. In particular, you cannot choose which label to go-to.
Basically in real scenario you don't need any goto statements, they're redundant as random calls to different places only make your code difficult to understand.
If your code is invoked many times, then consider to use loop and changing its workflow to use continue and break.
If your code repeats it-self, consider writing the function and calling it as many times as you want.
If your code needs to jump into specific section based on the variable value, then consider using case statement.
If you can separate your long code into smaller pieces, consider moving it into separate files and call them from the parent script.
I found out a way to do this using functions.
Say, for example, you have 3 choices: A, B, and C. A and Bexecute a command, but C gives you more info and takes you to the original prompt again. This can be done using functions.
Note that since the line containg function demoFunction is just setting up the function, you need to call demoFunction after that script so the function will actually run.
You can easily adapt this by writing multiple other functions and calling them if you need to "GOTO" another place in your shell script.
function demoFunction {
read -n1 -p "Pick a letter to run a command [A, B, or C for more info] " runCommand
case $runCommand in
a|A) printf "\n\tpwd being executed...\n" && pwd;;
b|B) printf "\n\tls being executed...\n" && ls;;
c|C) printf "\n\toption A runs pwd, option B runs ls\n" && demoFunction;;
esac
}
demoFunction
This is a small correction of the Judy Schmidt script put up by Hubbbitus.
Putting non-escaped labels in the script was problematic on the machine and caused it to crash. This was easy enough to resolve by adding # to escape the labels. Thanks to Alexej Magura and access_granted for their suggestions.
#!/bin/bash
# include this boilerplate
function goto {
label=$1
cmd=$(sed -n "/$#label#:/{:a;n;p;ba};" $0 | grep -v ':$')
eval "$cmd"
exit
}
start=${1:-"start"}
goto $start
#start#
echo "start"
goto bing
#boom#
echo boom
goto eof
#bang#
echo bang
goto boom
#bing#
echo bing
goto bang
#eof#
echo "the end mother-hugger..."
A simple searchable goto for the use of commenting out code blocks when debugging.
GOTO=false
if ${GOTO}; then
echo "GOTO failed"
...
fi # End of GOTO
echo "GOTO done"
Result is-> GOTO done
My idea for creating something like "goto" is to use select with case and assign a variable, which I then check in an if statement. Not perfect, but may help in some cases
Example:
#!/usr/bin/env bash
select goto in Ubuntu Debian Quit ; do
case $goto in
Ubuntu) { CHOICE="Ubuntu" ; break ; } ;;
Debian) { CHOICE="Debian" ; break ; } ;;
Quit) { echo "Bye" ; exit ; } ;;
*) { echo "Invalid selection, please try again..." ; } ;;
esac
done
if [ "$CHOICE" == "Ubuntu" ]; then
echo "I'm in Ubuntu"
fi
if [ "$CHOICE" == "Debian" ]; then
echo "I'm in Debian"
fi
Why don't anyone just use functions directly ?
BTW functions are easier to deal with than making a new thing
My style :
#!/bin/bash
# Your functions
function1 ()
{
commands
}
function2 ()
{
commands
}
:
:
functionn ()
{
commands
}
# Execute 1 to n in order
for i in {1..n}
do
function$i
done
# with conditions
for i in {1..n}
do
[ condition$i ] && function$i
done
# Random order
function1
functionn
function5
:
:
function3
Example for above style :
#!/bin/bash
# Your functions
function1 ()
{
echo "Task 1"
}
function2 ()
{
echo "Task 2"
}
function3 ()
{
echo "Task 3"
}
function1
function3
function2
Output :
Task 1
Task 3
Task 2
Drawbacks :
Script in an organized way.
Less problems and not prone to errors.
You can make function inside a existing function.
Move back and forth without any problems.

Resources