2

I wrote a Makefile which uses a similar idea to the one here. That is, I define a set of directories and using foreach and define I build all objects in each one of the directories. Here how my Makefile looks roughly

all_dirs := $(shell ...) # long shell command which finds all directories
all_objs := $(shell ...) # long shell command which finds all objects

define compile_rule
$(BUILDDIR)/$(1)/%.o :  $(1)/%.cpp
    $(CXX) $(CXXFLAGS) -c $< -o $@ -MD -MF $(@:.o=.dep)
endef

$(foreach dir,$(all_dirs:$(BUILDDIR)/%=%),$(eval $(call compile_rule,$(dir))))

all : program_name

program_name: $(all_objs)
    $(LD) -o $@ $(all_objs) $(LDFLAGS)

I always run the make command with -j32 argument. Usually it works fine but in some cases I get an error from the Makefile it self saying No rule to make target with the first object file name in $(all_objs) list needed by program_name.

Looking here the problem is clear. Since I'm using -j it seems like the make command starts to evaluate program_name before it expands the rules created by foreach. I tried to write the foreach inside a rule so I could guarantee the order of execution between program_name and the foreach but then I got the error prerequisites cannot be defined in recipes.

Any idea how to solve this? I might be able to solve this using a two step make, that is by first building $(all_objs) with something like make objs and then linking them together with something like make prog but I prefer it will all happen in one command and I have a feeling I miss something simple. Please advise.

Community
  • 1
  • 1
e271p314
  • 3,841
  • 7
  • 36
  • 61

1 Answers1

2

This may work, but I haven't tested it:

# ...

prereq: $(foreach dir ...)

program_name: $(all_objs) | prereq
  $(LD) ...

It seems to work on my test setup, but I've only tested with a few directories and objects. The pipe is an order-only prerequisite, and it looks like it's OK to put the for-loop in a target.

Suggestion

If you really want to, you can download the GNU Make 3.82 source, apply the patch below with patch < function.c.patch and then invoke make with make --debug=b -j32 ... and watch for Call, Evaluating and Executing lines.

--- function.c  2010-07-13 03:20:39.000000000 +0200
+++ function.c.patched  2015-04-18 20:41:15.000000000 +0200
@@ -1371,6 +1371,8 @@

   install_variable_buffer (&buf, &len);

+   DB (DB_BASIC, (_("Evaluating: %s\n"), argv[0]));
+
   eval_buffer (argv[0]);

   restore_variable_buffer (buf, len);
@@ -1652,6 +1654,11 @@
       return o;
     }

+   DB (DB_BASIC, (_("Executing shell command:")));
+  for ( char **p=command_argv; *p!=NULL; ++p )
+    DB (DB_BASIC, (_(" %s"), *p));
+  DB (DB_BASIC, (_("\n")));
+
 # ifdef __EMX__
   /* close some handles that are unnecessary for the child process */
   CLOSE_ON_EXEC(pipedes[1]);
@@ -2311,6 +2318,12 @@
     --cp;
   cp[1] = '\0';

+   DB (DB_BASIC, (_("Call %s:"), fname));
+  for ( char **p=argv; *p!=NULL; ++p )
+    DB (DB_BASIC, (_(" %s"), *p));
+  DB (DB_BASIC, (_("\n")));
+
+
   /* Calling nothing is a no-op */
   if (*fname == '\0')
     return o;

This way you can actually see if call compile_rule is run when it's not supposed to. In my case, the output is:

GNU Make 3.82
Built for x86_64-apple-darwin14.3.0
Copyright (C) 2010  Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Reading makefiles...
Executing shell command: find . -type d
Call compile_rule: compile_rule .
Evaluating: ./%.foo: ./%.txt
    @echo build $@ from $<
Call compile_rule: compile_rule ./1
Evaluating: ./1/%.foo: ./1/%.txt
    @echo build $@ from $<
Call compile_rule: compile_rule ./2
Evaluating: ./2/%.foo: ./2/%.txt
    @echo build $@ from $<
Updating goal targets....
 File `program' does not exist.
   File `1/1.foo' does not exist.
  Must remake target `1/1.foo'.
Invoking recipe from Makefile:11 to update target `1/1.foo'.
   File `2/2.foo' does not exist.
  Must remake target `2/2.foo'.
Invoking recipe from Makefile:11 to update target `2/2.foo'.
   File `prereq' does not exist.
  Must remake target `prereq'.
  Successfully remade target file `prereq'.
build 1/1.foo from 1/1.txt
 File `program' does not exist.
build 2/2.foo from 2/2.txt
 File `program' does not exist.
Must remake target `program'.
Invoking recipe from Makefile:9 to update target `program'.
link program w/args 1/1.foo 2/2.foo
csl
  • 10,937
  • 5
  • 57
  • 89
  • Unfortunately, the same issue exists. When you say it worked for you I believe you, it also works for me, but not always. I compile my program on AWS EC2 service, most of the time the instance is shut down. When I want to compile I have a python script (using [boto](https://boto.readthedocs.org/en/latest/)) which turns on the instance and runs the `make -j32` command. At the first compilation when the instance is turned on, this problem reproduces almost every time. Please let me know if you have a better idea. – e271p314 Apr 18 '15 at 17:44
  • Which version of GNU make do you use? – csl Apr 18 '15 at 18:13
  • Try running the make command with the `-d` flag. It should print a lot of debugging info which could perhaps be used to see why this is happening. – csl Apr 18 '15 at 18:22
  • So `-d` plus redirecting the output to a file solved the issue (indirectly) I guess cause the time that it takes for the debug info be written to file allows the Makefile to resolve all rules. This is of course not the solution I was looking for but it certainly helped :-) and eventually I do appreciate the time you found to let me know your opinion about my Makefile problems. – e271p314 Apr 23 '15 at 13:57
  • As a last comment I'd like to say that this problem reproduces mainly (but not only) when the system starts up. I guess some conditions are met at start up which create this problem more easily then in the rest of the system up time. However, after a week of looking into this issue, I admit I'm clueless. The only true solution I can think of to write all the rules explicitly. – e271p314 Apr 23 '15 at 14:05
  • Ok, this sounds more like an issue that should be posted on the GNU makefile lists? Maybe it's a known issue, or a bug? – csl Apr 25 '15 at 04:50