Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sub-makefiles and passing variables upward

I have a project that involves sub-directories with sub-makefiles. I'm aware that I can pass variables from a parent makefile to a sub-makefile through the environment using the export command. Is there a way to pass variables from a sub-makefile to its calling makefile? I.e. can export work in the reverse? I've attempted this with no success. I'm guessing once the sub-make finishes its shell is destroyed along with its environment variables. Is there another standard way of passing variables upward?

like image 721
Andrew Avatar asked Jul 14 '15 18:07

Andrew


1 Answers

The short answer to your question is: no, you can't [directly] do what you want for a recursive build (see below for a non-recursive build).

Make executes a sub-make process as a recipe line like any other command. Its stdout/stderr get printed to the terminal like any other process. In general, a sub-process cannot affect the parent's environment (obviously we're not talking about environment here, but the same principle applies) -- unless you intentionally build something like that into the parent process, but then you'd be using IPC mechanisms to pull it off.

There are a number of ways I could imagine for pulling this off, all of which sound like an awful thing to do. For example you could write to a file and source it with an include directive (note: untested) inside an eval:

some_target:
  ${MAKE} ${MFLAGS} -f /path/to/makefile

some_other_target : some_target
  $(eval include /path/to/new/file)

... though it has to be in a separate target as written above because all $(macro statements) are evaluated before the recipe begins execution, even if the macro is on a later line of the recipe.

gmake v4.x has a new feature that allows you to write out to a file directly from a makefile directive. An example from the documentation:

If the command required each argument to be on a separate line of the input file, you might write your recipe like this:

program: $(OBJECTS)
        $(file >[email protected]) $(foreach O,$^,$(file >>[email protected],$O))
        $(CMD) $(CMDFLAGS) @[email protected]
        @rm [email protected]

(gnu.org)

... but you'd still need an $(eval include ...) macro in a separate recipe to consume the file contents.

I'm very leery of using $(eval include ...) in a recipe; in a parallel build, the included file can affect make variables and the timing for when the inclusion occurs could be non-deterministic w/respect to other targets being built in parallel.

You'd be much better off finding a more natural solution to your problem. I would start by taking a step back and asking yourself "what problem am I trying to solve, and how have other people solved that problem?" If you aren't finding people trying to solve that problem, there's a good chance it's because they didn't start down a path you're on.


edit You can do what you want for a non-recursive build. For example:

# makefile1
include makefile2

my_tool: ${OBJS}


# makefile2
OBJS := some.o list.o of.o objects.o

... though I caution you to be very careful with this. The build I maintain is extremely large (around 250 makefiles). Each level includes with a statement like the following:

include ${SOME_DIRECTORY}/*/makefile

The danger here is you don't want people in one tree depending on variables from another tree. There are a few spots where for the short term I've had to do something like what you want: sub-makefiles append to a variable, then that variable gets used in the parent makefile. In the long term that's going away because it's brittle/unsafe, but for the time being I've had to use it.

I suggest you read the paper Recursive Make Considered Harmful (if that link doesn't work, just google the name of the paper).

like image 156
Brian Vandenberg Avatar answered Oct 15 '22 00:10

Brian Vandenberg