Question

So I have this fragment of code in a Makefile.am file (automake):

RUNNER = \
    for asm in $${TEST_ASSEMBLIES}; do \
            echo -e "Running tests on $${asm}..."; \
            $(ENV_OPTIONS) $(NUNIT_CONSOLE) -nologo -noshadow $$asm; \
            echo $$?; \
    done

test:
    @pushd $(DIR_BIN) &>/dev/null; \
    export TEST_ASSEMBLIES="$(TEST_ASSEMBLIES)"; $(RUNNER); \
    popd &>/dev/null;

The statement echo $$?; works: it prints 0 when the test works and 1 when it fails.

But what I'm trying to do is have a global var called GLOBAL_EXIT_CODE or something, which would have 0 if all tests run well, or 1 if any of the tests fail. But for the life of me, I cannot even capture the value of $$? in a variable. I've tried numerous things: export, assignemnt via simple "VAR = $$?", also with the += operator... etc. Nothing works! :(

The reason I want to have a variable GLOBAL_EXIT_CODE is so I can do exit $GLOBAL_EXIT_CODE in the test target, in case its value was higher than zero, in order to have the command make test return an error.

Was it helpful?

Solution

You can't do that. You have to remember that the recipe is running in a shell that make has invoked. That's a separate process, not in the make process.

There's no way that anything that happens in the shell (a child process) can change any aspect of make (the parent process) such as set a make variable. Once the shell exits, all the information it had is lost. The only thing that make gets back from the shell is the ultimate exit code; that's how make decides whether the command succeeded or failed.

The only way to do this is to write the value to a file and then have make read the file and put the resulting value into a make variable.

ETA:

But if all you want to do is preserve the exit code within the shell (as your edits say), then you don't need a make variable at all. You need a shell variable, and that's very easy. Also, note that make always invokes /bin/sh which is a POSIX shell, and pushd and popd are bash shell features, so this makefile is not portable. Luckily, there's no point in doing the pushd or popd anyway, because the working directory (in a POSIX system) is a feature of an individual process (the shell in this case) so any directory changes you make inside the shell only impact that shell and are gone when the shell exits.

So, try something like this:

RUNNER = \
    RETURN=0; \
    for asm in $${TEST_ASSEMBLIES}; do \
        echo -e "Running tests on $${asm}..."; \
        $(ENV_OPTIONS) $(NUNIT_CONSOLE) -nologo -noshadow $$asm \
            || RETURN=$$?; \
    done

test:
        @export TEST_ASSEMBLIES="$(TEST_ASSEMBLIES)"; \
         $(RUNNER); \
         exit $$RETURN

I don't really understand why you're setting the shell variable TEST_ASSEMBLIES to the value of the make variable $(TEST_ASSEMBLIES). It's not necessary, but it will work.

OTHER TIPS

I'm expanding my comment into an answer; this is the automake way to solve the problem, since the other answer focuses on plain make.

Assuming your TEST_ASSEMBLIES is a list of programs that can be run in stand-alone fashion:

TESTS = $(TEST_ASSEMBLIES)

Done. Do a make check and you should see the summary of the tests. By default automake will use its own test driver, which runs each test, redirects the output to a log file, and prints out the summary.

If you need the tests to be run by a special program, use the LOG_COMPILER variable:

TESTS = $(TEST_ASSEMBLIES)
LOG_COMPILER = $(NUNIT_CONSOLE)
AM_LOG_FLAGS = -nologo -noshadow

If you need environment variables set up (is that what ENV_OPTIONS does?), you can use AM_TESTS_ENVIRONMENT for that.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top