Search code examples
gnu-makeautomake

Disable parallel execution in make


I have a building process that creates a header file. In the second stage, several source files are generated from that header file. Then these source files are built into a binary. If anyone is interested these sources are generated with gSOAP utilities (wsdl2h, soapcpp2).

I have made Makefile.am, etc to build these sources, but there are problems when I want to use parallel execution.

Makefile.am would look something like this in a very simplified form

## generate header file
service.h : service.wsdl
    wsdl2h -o $@ service.wsdl

## list of generated source files
generated_files = source1.cpp source2.cpp source3.cpp

## generate source files
$(generated_files) : service.h
    soapcpp2 $^

## build binary
binary: $(generated_files)
    gcc -o $@ $^

The rules say that service.h will be generated if service.wsdl changes. If service.h changes, soapcpp2 will generate source?.cpp files with one command execution.

Everything works fine until I try to build in parallel (for instance make -j4). The problematic line is the last one which generates many soruce files. If running in parrallel all these files are generated many times, while other make processes already try to compile them.

I followed instructions to disable parallel https://www.gnu.org/software/make/manual/html_node/Parallel-Disable.html, but with no success. If I try

.NOTPARALLEL: $(generated_files)

or

.NOTPARALLEL: service.h

The parallel execution just does not work any more. I also tried with .WAIT, and got no rule to make target .WAIT.


Solution

  • First, the .WAIT special target was introduced in GNU make 4.4. Since you are getting a no rule to make target error for it, it's clear you're using an older version which doesn't support it. It's usually a good idea to include the version of whatever tool you're using when asking for help.

    The best thing to do is not disable parallelism but instead tell make that a single invocation of the recipe will generate all the files. If you have GNU make 4.3, then you can use a "grouped target" rule, like this:

    ## generate source files
    $(generated_files) &: service.h
            soapcpp2 $^
    

    the &: here tells make that instead of building each target with a different invocation of the recipe, which is the default, a single invocation of the recipe builds all the targets.

    If you don't have GNU make 4.3 then you'll need to play a trick to get the same behavior, something like this:

    ## generate source files
    .sentinel : service.h
            soapcpp2 $^
            @touch $@
    
    $(generated_files) : .sentinel ;
    
    ## build binary
    binary: $(generated_files)
            gcc -o $@ $^
    

    This has all the generated files depend on a single file .sentinel (you can name it whatever you want), which is the one make knows is generated by the recipe that also creates all the other source files. This isn't perfect but it will work for simple situations.