Question

I have a python script which extracts information from a C++ source file and writes it to a header file. Whenever this generated header changes (which happens rarely), I want to re-compile it (via the GCC precompiled header feature).

Obviously, the command invoking the script depends on the source file, and the recompiling command depends on the header file generated by the script.

add_custom_command(OUTPUT "file.pch.h" COMMAND <script> DEPENDS "file.cpp")
add_custom_command(OUTPUT "file.pch.h.gch" COMMAND <compile> DEPENDS "file.pch.h")

But now file.pch.h.gch depends on file.cpp and is recompiled whenever it changes. How can I avoid this?

Was it helpful?

Solution

Have your script write the header to a temporary file, and only copy it over the existing file.pch.h if it’s changed. Then the (fast) script runs every time file.cpp changes, but the precompiled header only gets reprecompiled if it’s changed.

If you don’t want to modify the script, you can use a separate move-if-change script, running a command along these lines:

myscript < file.cpp > file.pch.h.tmp && move-if-change file.pch.h.tmp file.pch.h

There might be a nicer way to do this with Cmake, but this is the old-fashioned way to solve this problem with Make.


Here’s a working example with CMake.

The main program, foo.c:

#include "foo.pch.h"

#ifndef FOO_PCH
#include <stdio.h>
#endif

int main() {
    printf("Hello, world\n");
    return 0;
}

The program to generate the precompiled header, make-pch:

#!/bin/bash

(echo '#define FOO_PCH 1'
 awk '/^#endif/ { p = 0 }
      p { print $0 }
      /^#ifndef FOO_PCH/ { p = 1 }') < foo.c > foo.pch.h.tmp

if ! cmp -s foo.pch.h{.tmp,}; then
    echo 'Header changed, updating'
    mv foo.pch.h{.tmp,}
else
    echo 'Header not changed'
    rm -f foo.pch.h.tmp
fi

And CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)
add_executable(foo foo.c foo.pch.h.gch)
add_custom_command(OUTPUT "foo.pch.h" COMMAND ./make-pch DEPENDS "foo.c")
add_custom_command(OUTPUT "foo.pch.h.gch"
                   COMMAND echo calling gcc
                   COMMAND gcc foo.pch.h
                   DEPENDS "foo.pch.h")

Let’s build it:

$ cmake .
-- The C compiler identification is GNU 4.2.1
-- The CXX compiler identification is Clang 4.1.0
-- Checking whether C compiler has -isysroot
-- Checking whether C compiler has -isysroot - yes
-- Checking whether C compiler supports OSX deployment target flag
-- Checking whether C compiler supports OSX deployment target flag - yes
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/andrew/sx/14662471
$ make
[ 33%] Generating foo.pch.h
Header changed, updating
[ 66%] Generating foo.pch.h.gch
calling gcc
Scanning dependencies of target foo
[100%] Building C object CMakeFiles/foo.dir/foo.c.o
Linking C executable foo
[100%] Built target foo
$ ./foo
Hello, world
$ # it’s up-to-date, so calling make again does nothing
$ make
[100%] Built target foo
$ Let’s change the C file
$ sed -i -e 's/Hello, world/Hello there, world/' foo.c
$ make
[ 33%] Generating foo.pch.h
Header not changed
Scanning dependencies of target foo
[ 33%] Generating foo.pch.h
Header not changed
[ 66%] Building C object CMakeFiles/foo.dir/foo.c.o
Linking C executable foo
[100%] Built target foo
$ # note that the precompiled header was *not* recompiled
$ ./foo
Hello there, world
$ # now let’s add a header file
$ sed -i -e $'s/stdio.h>/stdio.h>\\\n#include <stdlib.h>/' foo.c
$ make
[ 33%] Generating foo.pch.h
Header changed, updating
[ 66%] Generating foo.pch.h.gch
calling gcc
Scanning dependencies of target foo
[100%] Building C object CMakeFiles/foo.dir/foo.c.o
Linking C executable foo
[100%] Built target foo
$ # the precompiled header file *was* recompiled
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top