Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

makefiles: Generate proper dependency files when using ccache #12840

Merged

Conversation

leandrolanzieri
Copy link
Contributor

Contribution description

In #12807 it has been reported that when a change is made in a header file (which is a dependency of a given object file) make does not take this into account, in consequence it does not re-trigger a build.

The cause of this issue are the dependency files generated by the compiler (placed under $(BINDIR)/$(MODULE)/<filename>.d). When ccache is used, if the CCACHE_BASEDIR environmental variable is set, then it will turn all paths into relative (see ccache docs). This has the advantage of allowing one to reuse the cache across multiple RIOT folders, but in our case it passes a relative path to the object in the call to gcc.

Here you can see a snippet of ccache log, the actual call to gcc. Note that paths are relative:

ccache log on master
 Running preprocessor
[2019-11-28T19:50:38.589228 23100] Executing /usr/bin/gcc -Werror -Wall -Wextra -pedantic
-std=gnu11 -m32 -fstack-protector-all -ffunction-sections -fdata-sections -fno-common -Wall
-Wextra -Wmissing-include-dirs -fno-delete-null-pointer-checks -fdiagnostics-color
-Wstrict-prototypes -Wold-style-definition -gz -Wformat=2 -Wformat-overflow -Wformat-truncation
-DRIOT_FILE_RELATIVE="examples/hello-world/main.c" -DRIOT_FILE_NOPATH="main.c"
-DDEVELHELP -DDEBUG_ASSERT_VERBOSE -DRIOT_APPLICATION="hello-world"
-DBOARD_NATIVE="native" -DRIOT_BOARD=BOARD_NATIVE -DCPU_NATIVE="native"
-DRIOT_CPU=CPU_NATIVE -DMCU_NATIVE="native"
-DRIOT_MCU=MCU_NATIVE -include bin/native/riotbuild/riotbuild.h -I../../core/include
-I../../drivers/include -I../../sys/include -I../../boards/native/include -DNATIVE_INCLUDES
-I../../boards/native/include -I../../core/include -I../../drivers/include -I../../cpu/native/include
-I../../sys/include -I../../cpu/native/include -MD -MP -MF bin/native/application_hello-world/main.d
-MQ bin/native/application_hello-world/main.o -E main.c

The compiler then will generate rules with relative paths:

main.d file with relative path to object
bin/native/application_hello-world/main.o: main.c \
 /usr/include/stdc-predef.h bin/native/riotbuild/riotbuild.h \
 /usr/include/stdio.h /usr/include/bits/libc-header-start.h \
 /usr/include/features.h /usr/include/sys/cdefs.h \
 /usr/include/bits/wordsize.h /usr/include/bits/long-double.h \
 /usr/include/gnu/stubs.h /usr/include/gnu/stubs-32.h \
 /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \
 /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \
 /usr/include/bits/types.h /usr/include/bits/timesize.h \
 /usr/include/bits/typesizes.h /usr/include/bits/time64.h \
 /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
 /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
 /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
 /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
 ../../sys/include/net/gnrc/netapi.h ../../core/include/thread.h \
 ../../core/include/clist.h ../../core/include/list.h \
 ../../core/include/cib.h ../../core/include/assert.h \
 ../../core/include/panic.h ../../core/include/kernel_defines.h \
 ../../core/include/msg.h \
 /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h /usr/include/stdint.h \
 /usr/include/bits/wchar.h /usr/include/bits/stdint-intn.h \
 /usr/include/bits/stdint-uintn.h \
 /usr/lib/gcc/x86_64-linux-gnu/8/include/stdbool.h \
 ../../core/include/kernel_types.h /usr/include/inttypes.h \
 /usr/lib/gcc/x86_64-linux-gnu/8/include-fixed/limits.h \
 /usr/lib/gcc/x86_64-linux-gnu/8/include-fixed/syslimits.h \
 /usr/include/limits.h /usr/include/bits/posix1_lim.h \
 /usr/include/bits/local_lim.h /usr/include/linux/limits.h \
 /usr/include/bits/posix2_lim.h ../../cpu/native/include/cpu_conf.h \
 ../../core/include/sched.h ../../core/include/bitarithm.h \
 ../../core/include/native_sched.h ../../sys/include/net/netopt.h \
 ../../sys/include/net/gnrc/nettype.h ../../sys/include/net/ethertype.h \
 ../../sys/include/net/protnum.h ../../sys/include/net/gnrc/pkt.h \
 /usr/include/stdlib.h /usr/include/bits/waitflags.h \
 /usr/include/bits/waitstatus.h /usr/include/bits/floatn.h \
 /usr/include/bits/floatn-common.h /usr/include/sys/types.h \
 /usr/include/bits/types/clock_t.h /usr/include/bits/types/clockid_t.h \
 /usr/include/bits/types/time_t.h /usr/include/bits/types/timer_t.h \
 /usr/include/endian.h /usr/include/bits/endian.h \
 /usr/include/bits/byteswap.h /usr/include/bits/uintn-identity.h \
 /usr/include/sys/select.h /usr/include/bits/select.h \
 /usr/include/bits/types/sigset_t.h /usr/include/bits/types/__sigset_t.h \
 /usr/include/bits/types/struct_timeval.h \
 /usr/include/bits/types/struct_timespec.h \
 /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
 /usr/include/bits/pthreadtypes-arch.h /usr/include/alloca.h \
 /usr/include/bits/stdlib-float.h ../../core/include/kernel_types.h

The dependency files are included then in Makefile.base, but as the rules do not match out pattern in the objects rule, the objects are not updated when a dependency (e.g. a header file) is changed.

To solve this problem, we need absolute paths in the targets of the *.d files. While using ccache 3.6 I managed this just by not setting CCACHE_BASEDIR, so paths would be absolute. @cgundogan reported that this does not seem to fix the issue for version 3.7.6. That is why I modified the gcc flags to pass an explicit absolute path as the target of the generated rule (only doing this for ccache does not solve the issue, as it detects the path and relativizes it). This should not affect users who don't use ccache.

With this PR, dependency files have absolute paths, and any changes to the files the objects depend on will be taken into account.

main.d with this PR
/home/leandro/Work/RIOT/examples/hello-world/bin/native/application_hello-world/main.o: \
 /home/leandro/Work/RIOT/examples/hello-world/main.c \
 /usr/include/stdc-predef.h \
 /home/leandro/Work/RIOT/examples/hello-world/bin/native/riotbuild/riotbuild.h \
 /usr/include/stdio.h /usr/include/bits/libc-header-start.h \
 /usr/include/features.h /usr/include/sys/cdefs.h \
 /usr/include/bits/wordsize.h /usr/include/bits/long-double.h \
 /usr/include/gnu/stubs.h /usr/include/gnu/stubs-32.h \
 /usr/lib/gcc/x86_64-linux-gnu/8/include/stddef.h \
 /usr/lib/gcc/x86_64-linux-gnu/8/include/stdarg.h \
 /usr/include/bits/types.h /usr/include/bits/timesize.h \
 /usr/include/bits/typesizes.h /usr/include/bits/time64.h \
 /usr/include/bits/types/__fpos_t.h /usr/include/bits/types/__mbstate_t.h \
 /usr/include/bits/types/__fpos64_t.h /usr/include/bits/types/__FILE.h \
 /usr/include/bits/types/FILE.h /usr/include/bits/types/struct_FILE.h \
 /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
 /home/leandro/Work/RIOT/sys/include/net/gnrc/netapi.h \
 /home/leandro/Work/RIOT/core/include/thread.h \
 /home/leandro/Work/RIOT/core/include/clist.h \
 /home/leandro/Work/RIOT/core/include/list.h \
 /home/leandro/Work/RIOT/core/include/cib.h \
 /home/leandro/Work/RIOT/core/include/assert.h \
 /home/leandro/Work/RIOT/core/include/panic.h \
 /home/leandro/Work/RIOT/core/include/kernel_defines.h \
 /home/leandro/Work/RIOT/core/include/msg.h \
 /usr/lib/gcc/x86_64-linux-gnu/8/include/stdint.h /usr/include/stdint.h \
 /usr/include/bits/wchar.h /usr/include/bits/stdint-intn.h \
 /usr/include/bits/stdint-uintn.h \
 /usr/lib/gcc/x86_64-linux-gnu/8/include/stdbool.h \
 /home/leandro/Work/RIOT/core/include/kernel_types.h \
 /usr/include/inttypes.h \
 /usr/lib/gcc/x86_64-linux-gnu/8/include-fixed/limits.h \
 /usr/lib/gcc/x86_64-linux-gnu/8/include-fixed/syslimits.h \
 /usr/include/limits.h /usr/include/bits/posix1_lim.h \
 /usr/include/bits/local_lim.h /usr/include/linux/limits.h \
 /usr/include/bits/posix2_lim.h \
 /home/leandro/Work/RIOT/cpu/native/include/cpu_conf.h \
 /home/leandro/Work/RIOT/core/include/sched.h \
 /home/leandro/Work/RIOT/core/include/bitarithm.h \
 /home/leandro/Work/RIOT/core/include/native_sched.h \
 /home/leandro/Work/RIOT/sys/include/net/netopt.h \
 /home/leandro/Work/RIOT/sys/include/net/gnrc/nettype.h \
 /home/leandro/Work/RIOT/sys/include/net/ethertype.h \
 /home/leandro/Work/RIOT/sys/include/net/protnum.h \
 /home/leandro/Work/RIOT/sys/include/net/gnrc/pkt.h /usr/include/stdlib.h \
 /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h \
 /usr/include/bits/floatn.h /usr/include/bits/floatn-common.h \
 /usr/include/sys/types.h /usr/include/bits/types/clock_t.h \
 /usr/include/bits/types/clockid_t.h /usr/include/bits/types/time_t.h \
 /usr/include/bits/types/timer_t.h /usr/include/endian.h \
 /usr/include/bits/endian.h /usr/include/bits/byteswap.h \
 /usr/include/bits/uintn-identity.h /usr/include/sys/select.h \
 /usr/include/bits/select.h /usr/include/bits/types/sigset_t.h \
 /usr/include/bits/types/__sigset_t.h \
 /usr/include/bits/types/struct_timeval.h \
 /usr/include/bits/types/struct_timespec.h \
 /usr/include/bits/pthreadtypes.h /usr/include/bits/thread-shared-types.h \
 /usr/include/bits/pthreadtypes-arch.h /usr/include/alloca.h \
 /usr/include/bits/stdlib-float.h \
 /home/leandro/Work/RIOT/core/include/kernel_types.h

Not setting CCACHE_BASEDIR will have the side effect of not sharing the cache between different RIOT folders that the user may have. If the user really wants this (and does not care about having to clean for every build), the variable can always be set externally.

Testing procedure

Run the test under these two conditions:

  1. Using ccache
  • Make sure that you either have ccache in your path with symbolic links to it (as a wrapper of the compiler) or CCACHE environmental variable set: this depends on your installation.
  1. Not using ccache
  • Set the environmental variable CCACHE_DISABLE

TIP: To check if ccache is being used you can run ccache -s to see the statistics, the last access will change every time it's used.

Test:
Using the example application provided in #12807 (or a similar example):

  • Run rm -Rf bin in the application directory.
  • Build make all term. You should see the current value in of the header file.
  • Check that the main.d file has an absolute path to the object.
  • Change the header file updating the value, and run make all term (NO CLEAN). main.o should be the only object re-built, and the number should be updated.

Issues/PRs references

Fixes #12807

When CCACHE_BASEDIR variable is set, ccache rewrites absolute paths into
relative paths before computing the hash that identifies the compilation
for all the paths under that directory.

The problem is that those paths are also used when the compiler is
called, so the generated dependency files (*.d) will have a relative
path to the object files, and thus, it will not match our rule for
compiling (we use absolute paths). As dependency files define the
targets this way, any change on its dependencies (e.g. an included
header file) will not re-trigger a build.
@leandrolanzieri leandrolanzieri added Type: bug The issue reports a bug / The PR fixes a bug (including spelling errors) Area: build system Area: Build system labels Nov 28, 2019
@cgundogan
Copy link
Member

I confirm that this fixes #12807 for my setup (ccache 3.7.6)

@kaspar030
Copy link
Contributor

While using ccache 3.6 I managed this just by not setting CCACHE_BASEDIR, so paths would be absolute.

this probably breaks ci caching...

@MrKevinWeiss MrKevinWeiss added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Nov 29, 2019
@MrKevinWeiss
Copy link
Contributor

this is essential for CI, unsetting this multiplies CI cache usage, causing 50-100% slowdown.

Ok lets see. If does increase build time we will need either add something in RIOT (maybe a RIOT_CI_BUILD type thing) or something for murdock to do so.

@kaspar030
Copy link
Contributor

Ah, that's correct, murdock shouldn't be affected.

This will affect local caching when building in multiple directories, but I guess that's preferable to broken depencencies.

@kaspar030
Copy link
Contributor

If does increase build time

build time was 31min (+50%), I'll trigger a rebuild.

@kaspar030 kaspar030 added CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR and removed CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Nov 29, 2019
@kaspar030
Copy link
Contributor

kaspar030 commented Nov 29, 2019

build time was 31min (+50%)

That could actually be caused by the new "-MQ" option (which would cause cache misses edit only in the beginning), not by messing up caching. A couple of rebuilds would show.

@cgundogan
Copy link
Member

27 minutes says murdock. Let's trigger again.

@cgundogan cgundogan added CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR and removed CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Nov 29, 2019
@kaspar030
Copy link
Contributor

27 minutes says murdock. Let's trigger again.

I have a script that creates per-branch ccache stats for murdock, which I used for debugging the "-g" issue before. I'll run it for this PR (and current master) once the current run went through.

@kaspar030
Copy link
Contributor

29min. Will get some cache stats now.

@kaspar030
Copy link
Contributor

Here are the results: master, this pr.

TL;DR no difference, the PR doesn't seem to affect ccache on CI negatively.

Copy link
Contributor

@kaspar030 kaspar030 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is OK from my side.

@kaspar030 kaspar030 dismissed their stale review November 29, 2019 14:42

addressed

By passing the -MT flag with the absolute path to the object we make
sure that the compiler generates dependency files with rules that match
our building rules.
@leandrolanzieri leandrolanzieri force-pushed the pr/makefiles_remove_ccache_basedir branch from f3c97b3 to dc84584 Compare November 29, 2019 17:53
@leandrolanzieri
Copy link
Contributor Author

Here are the results: master, this pr.

TL;DR no difference, the PR doesn't seem to affect ccache on CI negatively.

@kaspar030 thanks a lot for this! It was really helpful.

@cgundogan I added the apostrophes.

Copy link
Member

@cgundogan cgundogan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, tested on my local setup and murdock also seems to agree with it. ACK from my side. Let's run it a last time by murdock (with the apostrophe fix included).

@cgundogan cgundogan removed the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Dec 2, 2019
@cgundogan cgundogan added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Dec 2, 2019
@cgundogan cgundogan merged commit 07c84a4 into RIOT-OS:master Dec 2, 2019
@leandrolanzieri leandrolanzieri deleted the pr/makefiles_remove_ccache_basedir branch December 2, 2019 10:42
@leandrolanzieri
Copy link
Contributor Author

Thanks everyone for testing and reviewing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: build system Area: Build system CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Type: bug The issue reports a bug / The PR fixes a bug (including spelling errors)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

make: rebuild doesn't apply to header files
5 participants