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

!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

Related

Computed macro names

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.

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 basics go wrong

So this is a really basic question, but I can't seem to find what I am doing wrong.
So I am fiddling with defines in NSIS and it did not work as I would expect so I have scaled down the problem to its smallest part and I still can not make it work as I would expect.
Script looks as follows:
!ifndef b
!define b ""
!endif
!if $b=="b"
!define a "b"
!else
!define a "c"
!endif
Section
MessageBox MB_OK "a: ${a} b: ${b}"
SectionEnd
I run it with the flag /Db=b.
The output is still:
a: "c" b: "b"
I am missing something trivial here!
b is a define, not a variable:
!ifndef b
!define b ""
!endif
!if "${b}" == "b" # <-- Modify this line.
!define a "b"
!else
!define a "c"
!endif
Section
MessageBox MB_OK "a: ${a} b: ${b}"
SectionEnd
Also, I recommend you quote everything when using if because it'll give an error if define (or value of variable) is empty.

NSIS substring by index

Is there any function which can cut string by letter index? I mean the X letters from the end?
!define myString "abcdefg"
I need to get for ex. "efg"
i tried to do that with nsis strings functions but didnt found what can help me.
${StrStr} and all the other functions doesnt do the work
Thanks
This is very simple. Actually, you don't have any dedicated function to do that, but you StrCpy can do that with 3rd and 4th arguments.
The format is going like that:
user_var(destination) str [maxlen] [start_offset]
And the usage for you case, is:
StrCpy $0 $myString "" -3
$0 will be: efg
More information about StrCpy function, can be found out there: http://nsis.sourceforge.net/Reference/StrCpy
StrCpy $0 "abcdefg" "" -3 ; variable $0 now has the last 3 letters
See the manual for more info about StrCpy

Find a string pattern in a file from an NSIS script

In an NSIS installer script, I'm trying to check if a given httpd.conf file contains the following line :
Include "c:\xxx\yyy.conf"
If so, then my installer script would not append it to the file, otherwise, it would append it.
I've come through {LineFind} but not sure this really makes what i'm trying to achieve.
What could be the simplest way to do a kind of "grep" on a text file from an NSIS script ?
Thank you !
Here is a sample for searching for a given line into a file, using the LogicLib for ease of syntax. The search is stopped as soon as the line is found. This sample works on the sample script itself:
# find.nsi : sample for LineFind from TextFunc.nsh
!include "textfunc.nsh"
!include "logiclib.nsh"
OutFile "find.exe"
!define lookfor `Section` ;will find
;!define lookfor `Sectionn` ;will not find
Var found
Section
StrCpy $found 0
${LineFind} "find.nsi" "/NUL" "1:-1" "GrepFunc"
${if} $found = 1
MessageBox MB_OK "string found"
${else}
MessageBox MB_OK "string NOT found"
${endIf}
SectionEnd
Function GrepFunc
${TrimNewLines} '$R9' $R9
DetailPrint "test for line $R8 `$R9`"
${if} $R9 == "${lookfor}"
StrCpy $found 1 ;set flag
Push "StopLineFind" ;stop find
${else}
Push 0 ;ignore -> continue
${endIf}
FunctionEnd

Resources