Assigning a variable to iteslf: difference between $() and ${}

(Test config is from MicroStation CONNECT 10.17.01.58 but pretty sure it happens in everything probably back to the beginning of config files)

TL;DR: Don't assign a variable, or part of a variable, to itself using $(VARIABLE), either use additional variables or use ${VARIABLE}

Just drove myself crazy for a couple of hours debugging a config file thought I'd post it here in case someone else runs into it.

I wanted to take a directory like: C:\Folder1\Folder2\Folder3\

I wanted to do something with each folder name so I wrote a config to extract the name, get the parent directory, extract that name, get the parent directory, etc...:

TEST_PATH = $(_USTN_SYSTEMROOT)
%echo     test: =>$(TEST_PATH)<=
%echo

LAST_DIR = lastdirpiece($(TEST_PATH))
PARENT_PATH = parentdevdir($(TEST_PATH))
%echo lastdir1: =>$(LAST_DIR)<=
%echo  parent1: =>$(PARENT_PATH)<=
%echo

LAST_DIR = lastdirpiece($(PARENT_PATH))
PARENT_PATH = parentdevdir($(PARENT_PATH))
%echo lastdir2: =>$(LAST_DIR)<=
%echo  parent2: =>$(PARENT_PATH)<=
%echo

LAST_DIR = lastdirpiece($(PARENT_PATH))
PARENT_PATH = parentdevdir($(PARENT_PATH))
%echo lastdir3: =>$(LAST_DIR)<=
%echo  parent3: =>$(PARENT_PATH)<=
%echo

This produced:

    test: =>C:\Program Files\Bentley\MicroStation CONNECT Edition\MicroStation\Default\<=

lastdir1: =>Default<=
 parent1: =>C:\Program Files\Bentley\MicroStation CONNECT Edition\MicroStation\<=

lastdir2: =>
 parent2: =>

lastdir3: =>
 parent3: =>

So the first extraction works, but then the 2nd and 3rd where I overwrite the variables doesn't. After much hair pulling I finally remembered the difference between $() and ${}. Basically $() means "don't actually resolve this variable yet, just keep the $(variable) string and replace it later" while ${} means "use the value of this variable as it exists right now!"

Now I rewrote my tests as:

TEST_PATH = $(_USTN_SYSTEMROOT)
%echo     test: =>${TEST_PATH}<=
%echo

LAST_DIR = lastdirpiece(${TEST_PATH})
PARENT_PATH = parentdevdir(${TEST_PATH})
%echo lastdir1: =>${LAST_DIR}<=
%echo  parent1: =>${PARENT_PATH}<=
%echo

LAST_DIR = lastdirpiece(${PARENT_PATH})
PARENT_PATH = parentdevdir(${PARENT_PATH})
%echo lastdir2: =>${LAST_DIR}<=
%echo  parent2: =>${PARENT_PATH}<=
%echo

LAST_DIR = lastdirpiece(${PARENT_PATH})
PARENT_PATH = parentdevdir(${PARENT_PATH})
%echo lastdir3: =>${LAST_DIR}<=
%echo  parent3: =>${PARENT_PATH}<=
%echo

This time it worked:

    test: =>C:\Program Files\Bentley\MicroStation CONNECT Edition\MicroStation\Default\<=

lastdir1: =>Default<=
 parent1: =>C:\Program Files\Bentley\MicroStation CONNECT Edition\MicroStation\<=

lastdir2: =>MicroStation<=
 parent2: =>C:\Program Files\Bentley\MicroStation CONNECT Edition\<=

lastdir3: =>MicroStation CONNECT Edition<=
 parent3: =>C:\Program Files\Bentley\<=

This issue isn't because of the parentdevdir, same thing happens anytime you use $() to assign the value of a variable (or part of the value) to itself:

TEST_PATH = $(TEST_PATH)

even indirect assignments will have issues:

TEST_PATH1 = $(_USTN_SYSTEMROOT)

TEST_PATH2 = $(TEST_PATH1)

TEST_PATH1 = $(TEST_PATH2)

even replacing that last line with:

TEST_PATH1 = ${TEST_PATH2} ALSO FAILS because the "value right now" of TEST_PATH2 is still $(TEST_PATH1) with regular ()

Because resolution is delayed this line says "Assign TEST_PATH to TEST_PATH once it's resolved" well that's never going to resolve because it's a big circle.