Sub-makefiles and passing variables upward - makefile

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?

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 >$#.in) $(foreach O,$^,$(file >>$#.in,$O))
$(CMD) $(CMDFLAGS) #$#.in
#rm $#.in
(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).

Your directory structure probably looks like this:
my_proj
|-- Makefile
|-- dir1
| `-- Makefile
`-- dir2
`-- Makefile
And what you are doing in your parent Makefile is probably this:
make -C ./dir1
make -C ./dir2
This actually spawns/forks a new child process for every make call.
You are asking for updating the environment of the parent process from its children, but that's not possible by design (1, 2).
You still could work around this by:
using a file as shared memory between two processes (see Brian's answer)
using the child's exit error code as a trigger for different actions [ugly trick]

I think the simplest solution is using standard out from a sub Makefile.
Parent Makefile
VAR := $(shell $(MAKE) -s -C child-directory)
all:
echo $(VAR)
Child Makefile
all:
#echo "MessageToTheParent"

Related

Expand Variables in Target of an Included Makefile

I am trying to build a framework which is supposed to apply similar operations to different designs/projects. Therefore, I have a general Makefile which defines general targets used for most of the operations. The idea is then that each design has its own main Makefile. This main Makefile includes the general Makefile for the general functionality, defines some variables for some basic configuration of the general Makefile, but can also extend or override variables from the general Makefile or define new targets or override targets when they are not applicable.
So the simplified directory structure looks something like this:
<Root Dir>
| -- targets.mk
| -- design1
| -- Makefile
| -- design2
| -- Makefile
The simplified general Makefile targets.mk looks something like this
${FF_LIST}: ${SRC_FILES}
#echo "Extract FF List for ${DESIGN_NAME}"
.PHONY: get_ff_list
get_ff_list: ${FF_LIST}
#echo "Get FF list for ${DESIGN_NAME} from ${FF_LIST}"
And the simplified design specific Makefile looks something like this:
include ../targets.mk
DESIGN_NAME = design1
FF_LIST = ./misc/ff_list.csv
With this implementation, I have the problem now, when calling the target get_ff_list within the design1 directory, that the recipe for the get_ff_list target is executed but the prerequisites are not, although the echo prints the right file.
user:/tmp/make_test/design1$ make get_ff_list
Get FF list for design1 from ./misc/ff_list.csv
It seems like that the target ${FF_LIST} is not expanded correctly. I can understand that during the time I am including the targets.mk Makefile this variable does not exist. However, my understanding of Makefile's recursive variable declaration with = should expand the variable every time the variable is used (as it is done and seems to work within the recipe itself).
I could include the targets.mk Makefile at the end after the configuration/setting the variables, like:
DESIGN_NAME = design1
FF_LIST = ./misc/ff_list.csv
include ../targets.mk
This seems to work and solve this particular issue. However, when I also want to extend or override variables/targets from the general Makefile, then it becomes a bit less obvious where to include it. Especially, if I am not the only one using the framework and other users create there own new designs.
Maybe this is even not a good way to use Makefiles to begin with. I would also be happy to get suggestions of better ways to implement this.
However, my understanding of Makefile's recursive variable declaration with = should expand the variable every time the variable is used (as it is done and seems to work within the recipe itself).
No. Read the section of the manual on How make Reads a Makefile to understand when variables are expanded immediately, and when the expansion is deferred.
The simplest way to do what you want is for the include targets.mk to come at the end of the Makefile, not at the beginning. If that's not feasible then you'll have to split the main makefile into two parts, one that sets variables and is included first, and the other that defines rules and is included last.

how do i modify a global variable in makefile?

i have something like this in my makefile :
JAR = jar1.jar
run:
java -cp $(JAR)
what i want to do is for the makefile to do run multiple times but with each iteration, it uses another jar, they're all called jarx.jar with x going from 1 to 10 for example
is it possible to do it without passing the jar name inside the statement? the example i gave is quite simple for the sake of simplicity but the actual makefile i'm working with is already quite complicated...
make uses filesystem objects to keep state. A common solution to keeping track of things which are not directly visible as files in the current directory is to create a local flag file.
JARS := first.jar 2nd.jar thirdjar.jar
.PHONY: all run
all: $(patsubst %, .made-%,$(JARS))
run: all
.made-%.jar: %.jar
java -cp $<
touch $#
So the existence of .made-first.jar signals to Make that this target has been performed for the prerequisite first.jar, etc.
If you have a clean target, or at least realclean, it should probably clean up all the flag files.

GNU Makefile "preprocessor"?

Is there an option to output the "preprocessed" makefile, something equivalent to the GCC's -E option?
I have a project comprised of an hierarchy of dozens of modules, each with its makefile. The build is invoked from a master makefile. That master makefile contains includes, variable definitions, command line option dependent variables, etc.
So, essentially, I am looking for the processed makefile, including all substitutions.
Not that I'm aware of. The closest thing you can get to this is the output from make -qp (or similar) which will dump the make database out at you.
Part of the problem with this request is that many of the substitutions/etc. happen as targets are processed and the list of targets isn't necessarily known without actually attempting a build (at least to an extent) so it isn't necessarily possible to fully expand/etc. a makefile in-place.
The make -d output is also useful for certain incidental information related to how make has processed the makefiles but doesn't contain makefile contents directly.
Remake might also be able to provide some extra useful information.
If you are looking for the computed value of some assembled/etc. global make variable then this blog post by Eric Melski is likely to be very helpful.
tl;dr It adds a target like this to the Makefile (though there's more magic in the blog post so I suggest reading it).
print-%:
#echo '$*=$($*)'
#echo ' origin = $(origin $*)'
#echo ' flavor = $(flavor $*)'
#echo ' value = $(value $*)'
Though in personal use I replaced that first line with something more like this
#echo '$*=$(subst ','\'',$($*))'
to keep the quoting of the result correct.

calling another target inside my makefile without depending on it

I would prefer to avoid a recursive call of make. Unless someone recommends this, it seems inefficient. (if someone does recommend it, we can mark as duplicate of How can I call a specific target from my makefile?)
I've found related threads but most of the solutions involve refactoring the make command, e.g.
makefile execute another target
or depending on the target that you want to run, e.g.
How do I make a target in a makefile invoke another target in the makefile
In fact, I would be interested in how to refactor my makefile. I am newbie with makefiles so I wouldn't be surprised if I'm making things more complicated than they need to be.
# depend on all files in folder abc
myTarget1: $(shell find abc -type f)
make otherTarget
some commands...
# I have various targets that need to do the following:
.PHONY : otherTarget
otherTarget:
common commands...

Executing commands in the directory in which the file that they were defined in is located

Consider, if you will, the following situation:
$ pwd
/tmp/submake_example
$ head $(find -type f)
==> ./subdir/Makefile <==
subtarget:
echo "executing subtarget from directory $$(pwd)"
==> ./Makefile <==
include subdir/Makefile
$ make subtarget
echo "executing subtarget from directory $(pwd)"
executing subtarget from directory /tmp/submake_example
Is there a way that I can have the command for the target subtarget executed from within the directory subdir? I'd like to be able to access the targets of included makefiles from the command line in the main directory.
After some digging and experimentation, I've discovered a couple of things.
The approach recommended by this reference, which refers to the paper Recursive Make Considered Harmful, is pretty complicated, and involves manually maintaining a directory stack throughout the makefile inclusion heirarchy.
I also discovered, independently of any reference, that the last element of the variable $(MAKEFILE_LIST) will, at least for some basic test cases, contain the name of the makefile that's currently being included. This means that it's possible to store its directory in a simply expanded, target-specific variable with code like
foo: dir := $(dir $(lastword $(MAKEFILE_LIST)))
That's it. Then you can just use $(dir) within that rule like any other variable; it retains the value that it assumed during makefile inclusion. cd to it, put it in command paths, whatever.
You have to be careful to put the assignment to dir before any include directives in that makefile.
This won't work if there's a space in the directory name, but I gather that pretty much nothing in make works if there are spaces in file or directory names.
It's also notable that this approach uses features only found in GNU make. Sounds like a killer app to me, though I remain a bit perplexed as to why they don't just provide the directory name in a builtin variable or function.
Anyway this seems to work, at least at first glance. I'm still pretty much just floating this one without any real backup, so comments on the practicality of this approach, and alternative suggestions, are very welcome.

Resources