Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to force a certain groups of targets to be always run sequentially?

Is there a way how to ask gmake to never run two targets from a set in parallel?

I don't want to use .NOTPARALLEL, because it forces the whole Makefile to be run sequentially, not just the required part.

I could also add dependencies so that one depends on another, but then (apart from being ugly) I'd need to build all of them in order to build the last one, which isn't necessary.


The reason why I need this is that (only a) part of my Makefile invokes ghc --make, which takes care of its dependencies itself. And it's not possible to run it in parallel on two different targets, because if the two targets share some dependency, they can rewrite each other's .o file. (But ghc is fine with being called sequentially.)

Update: To give a specific example. Let's say I need to compile two programs in my Makefile:

  • prog1 depends on prog1.hs and mylib.hs;
  • prog2 depends on prog2.hs and mylib.hs.

Now if I invoke ghc --make prog1.hs, it checks its dependencies, compiles both prog1.hs and mylib.hs into their respective object and interface files, and links prog1. The same happens when I call ghc --make prog2.hs. So if they the two commands get to run in parallel, one will overwrite mylib.o of the other one, causing it to fail badly.

However, I need that neither prog1 depends on prog2 nor vice versa, because they should be compilable separately. (In reality they're very large with a lot of modules and requiring to compile them all slows development considerably.)

like image 388
Petr Avatar asked Feb 17 '14 14:02

Petr


Video Answer


1 Answers

Hmmm, could do with a bit more information, so this is just a stab in the dark.

Make doesn't really support this, but you can sequential-ise two targets in a couple of ways. First off, a real use for recursive make:

targ1: ; recipe1...
targ2: ; recipe2...

both-targets:
    ${MAKE} targ1
    ${MAKE} targ2

So here you can just make -j both-targets and all is fine. Fragile though, because make -j targ1 targ2 still runs in parallel. You can use dependencies instead:

targ1: ; recipe1...
targ2: | targ1 ; recipe2...

Now make -j targ1 targ2 does what you want. Disadvantage? make targ2 will always try to build targ1 first (sequentially). This may (or may not) be a show-stopper for you.

EDIT

Another unsatisfactory strategy is to explicitly look at $MAKECMDGOALS, which lists the targets you specified on the command-line. Still a fragile solution as it is broken when someone uses dependencies inside the Makefile to get things built (a not unreasonable action).

Let's say your makefile contains two independent targets targ1 and targ2. Basically they remain independent until someone specifies on the command-line that they must both be built. In this particular case you break this independence. Consider this snippet:

$(and $(filter targ1,${MAKECMDGOALS)),$(filter targ2,${MAKECMDGOALS}),$(eval targ1: | targ2))

Urk! What's going on here?

  • Make evaluates the $(and)
  • It first has to expand $(filter targ1,${MAKECMDGOALS})
  • Iff targ1 was specified, it goes on to expand $(filter targ2,${MAKECMDGOALS})
  • Iff targ2 was also specified, it goes on to expand the $(eval), forcing the serialization of targ1 and targ2.
    • Note that the $(eval) expands to nothing (all its work was done as a side-effect), so that the original $(and) always expands to nothing at all, causing no syntax error.

Ugh!

[Now that I've typed that out, the considerably simpler prog2: | $(filter prog1,${MAKECMDGOALS}) occurs to me. Oh well.]

YMMV and all that.

I'm not familiar with ghc, but the correct solution would be to get the two runs of ghc to use different build folders, then they can happily run in parallel.

like image 165
bobbogo Avatar answered Sep 20 '22 16:09

bobbogo