Yu Chen
Yu Chen

Reputation: 7440

Echoing exit code in Makefile

I've checked a few posts on SO, but not able to get the answer I want.

I want to run a Stata script as part of a Makefile. So in one of my targets, I define

live_data:
    @echo "Executing Stata code"
    StataIC -e 'stata_code.do'
    @exit_status=$?
    @echo "Finished execution of Stata code."
    @echo Code finished with exit code of $(exit_status)

However, whenever I look at the output of my make all command (which includes live_data), I see

Executing Stata code
StataIC -e stata_code.do'
Finished execution of Stata code.
Code finished with exit code of 

Basically, the variable exit_code is always empty.

However, if I pop up a terminal and simply run StataIC -e stata_code.do followed by exit_status=$? and echo $exit_status, I get the correct result (0 or 1). Could someone point me to what exactly I'm missing here?

Upvotes: 0

Views: 180

Answers (2)

chepner
chepner

Reputation: 530853

Note that you can simplify the recipe so that you don't need to save the value of $?.

live_data:
    @echo "Executing Stata code"; \
      StataIC -e 'stata_code.do'; \
      printf '%s\n' "Finished execution of Stata code." \
                    "Code finished with exit code of $$?"

Here, both strings are arguments to a single printf called immediately after StataIC, so $? correctly refers to the exit status of StataIC.

Upvotes: 1

Beta
Beta

Reputation: 99084

You have a couple of problems. First, as @AProgrammer has pointed out, each line in a recipe is executed in its own subshell, so a variable set in one line does not survive into the next:

trial:
    @var=blue
    @echo this command has no access to what was stored in var

The solution is to put the commands in one line

trial:
    @var=blue ; echo this command can use what was stored in var

and if that's too long, wrap the line with a backslash:

trial:
    @var=blue ;\
  echo this command can use what was stored in var

(Note that there is only one TAB, the one just before the @; the whitespace to the left of "echo" is just some spaces to make the rule easy to read.)

Second, you must be careful about the syntax. The way to expand a variable in Make is $(var), but the way to expand it in a shell is $var; using either syntax in the other context will not give you the result you want.

var=red
$(info $(var)) # this is a Make command

trial:
    @var=blue ; echo this is a shell command, so $(var) will not work

Third, although a command in a rule is a shell command, Make will attempt to expand any variable it sees before running the rule, so we must escape the '$' in "$var" with another '$':

trial:
    @var=blue ; echo $var will not work, but $$var will

Put it all together and we get:

live_data:
    @echo "Executing Stata code" ; \
  StataIC -e 'stata_code.do' ; \
  exit_status=$$? ; \
  echo "Finished execution of Stata code." ; \
  echo Code finished with exit code of $$exit_status

Upvotes: 1

Related Questions