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?
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
Related
In my work I am using a Fortran based program called SPheno. Having SPheno-4.0.4 installed, I tried to install the new version SPheno-4.0.5, however, when selecting F90 = gfortran in the Makefile, just as I did on my working SPheno-4.0.4 version, it returns me the following error:
cd src ; /Library/Developer/CommandLineTools/usr/bin/make F90=gfortran version=400.00
gfortran -c -O -J../include -I../include -DGENERATIONMIXING -DONLYDOUBLE Control.F90
ar ../lib/libSPheno.a Control.o
/Library/Developer/CommandLineTools/usr/bin/ar: illegal option -- .
usage: ar -d [-TLsv] archive file ...
ar -m [-TLsv] archive file ...
ar -m [-abiTLsv] position archive file ...
ar -p [-TLsv] archive [file ...]
ar -q [-cTLsv] archive file ...
ar -r [-cuTLsv] archive file ...
ar -r [-abciuTLsv] position archive file ...
ar -t [-TLsv] archive [file ...]
ar -x [-ouTLsv] archive [file ...]
make[1]: *** [../lib/libSPheno.a(Control.o)] Error 1
make: *** [bin/SPheno] Error 2
What does this error even mean, and what do I have to change? I even tried to copy/paste the Makefile from SPheno-4.0.4, just to check.
In case it matters, when running the command gfortran -v it returns me:
Using built-in specs.
COLLECT_GCC=gfortran
COLLECT_LTO_WRAPPER=/usr/local/Cellar/gcc/11.2.0/libexec/gcc/x86_64-apple-darwin19/11.2.0/lto-wrapper
Target: x86_64-apple-darwin19
Configured with: ../configure --prefix=/usr/local/Cellar/gcc/11.2.0 --libdir=/usr/local/Cellar/gcc/11.2.0/lib/gcc/11 --disable-nls --enable-checking=release --enable-languages=c,c++,objc,obj-c++,fortran,d --program-suffix=-11 --with-gmp=/usr/local/opt/gmp --with-mpfr=/usr/local/opt/mpfr --with-mpc=/usr/local/opt/libmpc --with-isl=/usr/local/opt/isl --with-zstd=/usr/local/opt/zstd --with-pkgversion='Homebrew GCC 11.2.0' --with-bugurl=https://github.com/Homebrew/homebrew-core/issues --enable-libphobos --build=x86_64-apple-darwin19 --with-system-zlib --disable-multilib --with-native-system-header-dir=/usr/include --with-sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.2.0 (Homebrew GCC 11.2.0)
The Makefile at play is the following:
# please put here your preferred F95/F2003 compiler
# the options in src/Makefile have been put for the
# cases NAG's nagfor, gfortran, g95, Lahey's lf95 and Intels ifort
# Please uncomment the corresponding line
# F90 = nagfor
F90 = gfortran
# F90 = g95
# F90 = lf95
# F90 = ifort
Model = src
version = 400.00
bin/SPheno:
cd ${Model} ; ${MAKE} F90=${F90} version=${version}
clean:
rm -f *.o *~ */*.o */*~
cleanall:
rm -f bin/SPheno lib/*.a *.o *~ */*.o */*~ include/*
.PHONY: bin/SPheno clean cleanall
The output means that make invoked this command:
ar ../lib/libSPheno.a Control.o
which runs the ar (archive) program and apparently wants to put the Control.o object into the ../lib/libSPheno.a archive. However, that command line is incorrect because there's supposed to be an option after the ar which tells it what operation to perform. This output:
/Library/Developer/CommandLineTools/usr/bin/ar: illegal option -- .
usage: ar -d [-TLsv] archive file ...
...
is printed by the ar program telling you that your invocation is invalid.
Then this line:
make[1]: *** [../lib/libSPheno.a(Control.o)] Error 1
is make telling you that the command it invoked (ar) to build the target ../lib/libSPheno.a(Control.o) failed, with error code 1.
Somewhere in the makefile you need to find the rule that is telling make to run the ar program. We can't tell you where it is; unfortunately your version of make is too old (Apple is notorious for shipping outdated and buggy versions of GNU utilities): newer versions would give you the line number of the rule in the makefile that failed.
Most likely the rule will refer to the variable $(AR) and maybe $(ARFLAGS) or something.
I do not understand why ar gives a warning when it is creating a library.
I do this:
$ cat foo.c
int foo(int a) {
return a + 1;
}
$ clang -c foo.c
$ ar r foo.a foo.o
ar: warning: creating foo.a
$
Is r the right command to use with ar? Why do I get the warning?
I am using clang and FreeBSD. Not sure if ar comes from clang or from FreeBSD.
If the output file doesn't already exist, you are supposed to use the c modifier. From the man page:
c
Create the archive. The specified archive is always created if it did not exist, when you request an update. But a warning is issued unless you specify in advance that you expect to create it, by using this modifier.
So try ar rc foo.a foo.o to silence the warning.
Compiling C program using gcc is very simple. Just such a command:
gcc code.c -o executable
However, if the code file name does not end with ".c", gcc will treat it as a linker script. How can I use gcc to compile C program whose name does not end with ".c"?
Use -x switch to specify the language.
gcc -xc -o executable anything
You can even read from stdin:
echo 'int main() { printf("Hello world"); }' | gcc -xc -o executable -
With GCC, the -x switch specifies the language to compile for:
gcc -x c code.foo -o executable
gcc has -M-class options (-MMD, -MF, etc.) that allows to generate dependency file during compiling source file. The dependency file contains Makefile rules describing on which source files and headers the generated object file depends on. The dependency file may be included into Makefile and then make will automatically recompile source file when headers are changed.
I need a similar option but for generating dependency file during linking an executable. The dependency file should contain list of libraries used for linking an executable, so if any of libraries is updated, make will re-execute linking of the executable automatically.
I tried to use the same flags (-MMD, -MF), but they doesn't work for linking. It seems they are only for generating dependency files during compiling.
Is there any other flags or means for generating dependency file for executable?
So far I have not found dedicated gcc options for generating dependency file for executable, but found the --trace option (-Wl,--trace when used with gcc). This option generates list of libraries used during linking. Its output has the next format:
gcc -Wl,--trace myprog.c -o myprog -L. -lmylib
-lmylib (./libmylib.a)
-lgcc_s (/usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/libgcc_s.so)
...
The list of libraries then may be converted to Makefile rules using sed:
echo "myprog: " > myprog.dep
gcc -Wl,--trace myprog.c -o myprog -L. -lmylib \
| sed -n 's/.*(\(.*\)).*/\1 \\/p' >> myprog.dep
So myprog.dep will have the following content:
myprog: \
./libmylib.a \
/usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/libgcc_s.so \
...
This dependency file may be included to Makefile and make will relink myprog if any of libraries are updated.
The dependency file doesn't contain list of object files, but the object files are usually known inside Makefile-script without help of compiler:
myprog: $(OBJS)
gcc -Wl,--trace $^ -o myprog -L. -lmylib | sed -n 's/.*(\(.*\)).*/\1 \\/p' >> myprog.dep
I am trying to compile Pro*C lib on Linux.I have following code in my make.
etc=$TABS_HOME/admin
export etc
if [ -f ${1}.pc ]
then
rm $1_x.o
compc $1
make -f $etc/proc64.mk $1_x.o
ar -cvq libtabs.a $1_x.o
else
make -f $etc/proc64.mk $1.o
ar -cvq libtabs.a $1.o
fi
Here is the final command that printed when compilation started:
/usr/bin/gcc -g -m64 -g -I/export/home/cl10gr2/oracle/rdbms/public -I/home/med/src/common -I/u01/app/oradb11r2/product/11.2.0/dbhome_3/rdbms/demo -
I/u01/app/oradb11r2/product/11.2.0/dbhome_3/rdbms/public -
I/u01/app/oradb11r2/product/11.2.0/dbhome_3/precomp/public -ltabs.a -lnapi.a -c commonutil_x.c
I am getting following warning/Error:
gcc: -ltabs.a: linker input file unused because linking not done
gcc: -lnapi.a: linker input file unused because linking not done
Can any please help me out why it is not linking the lib files?
Its not linking them because you aren't linking. You are passing the -c option:
-c Compile or assemble the source files, but do not link. The linking stage simply is not done. The ultimate output is in the form of an object file for each source file.
If you are building intermediate object files, you don't need the libraries until the very end. Include all of the object files and libraries you need in the final stage and link them all together.