ninja-build / ninja

a small build system with a focus on speed
https://ninja-build.org/
Apache License 2.0
11.3k stars 1.61k forks source link

Unnecessarily relink when Link-Time Optimization is on #2535

Open gyatpear opened 2 hours ago

gyatpear commented 2 hours ago

Ninja version: 1.12.1

Ninja seems tracking some link-time temporary files, maybe for link-time optimization, and finds them missing at next build time.

Project to reproduce:

CMakeLists.txt:

cmake_minimum_required(VERSION 3.20)
project(test_proj)
add_executable(main main.c)

main.c:

#include <stdio.h>
int main() {
  printf("Hello world!\n");
  return 0;
}

Case 1: Release build w/o Link-Time Optimization

$ mkdir -p build
$ cd build
$ cmake -S .. -G Ninja -D CMAKE_BUILD_TYPE=Release --fresh
-- The C compiler identification is GNU 14.2.1
-- The CXX compiler identification is GNU 14.2.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.7s)
-- Generating done (0.0s)
-- Build files have been written to: /tmp/tmp.J0m6gKPUMX/build
$ ninja                                                   
[2/2] Linking C executable main
$ ninja -d explain
ninja: no work to do.

Case 2: Release build w/ Link-Time Optimization

$ mkdir -p build
$ cd build
$ cmake -S .. -G Ninja -D CMAKE_BUILD_TYPE=Release -D CMAKE_INTERPROCEDURAL_OPTIMIZATION=ON --fresh
-- The C compiler identification is GNU 14.2.1
-- The CXX compiler identification is GNU 14.2.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.7s)
-- Generating done (0.0s)
-- Build files have been written to: /tmp/tmp.J0m6gKPUMX/build
$ ninja                                                                                            
[2/2] Linking C executable main
$ ninja -d explain
ninja explain: /tmp/ccafrVpr.ltrans0.ltrans.o has no in-edge and is missing
ninja explain: /tmp/ccafrVpr.ltrans0.ltrans.o is dirty
ninja explain: main is dirty
[1/1] Linking C executable main
$ ninja -d explain
ninja explain: /tmp/cckpFqL6.ltrans0.ltrans.o has no in-edge and is missing
ninja explain: /tmp/cckpFqL6.ltrans0.ltrans.o is dirty
ninja explain: main is dirty
[1/1] Linking C executable main
digit-google commented 2 hours ago

It looks like the compiler or linker command is generating incorrect depfile information that includes temporary files that are removed after the compile/link step.

It's either a bug in the toolchain, or the CMake-generated Ninja build plan triggered by CMAKE_INTERPROCEDURAL_OPTIMIZATION=ON, not a problem with Ninja itself.

I recommend you file a bug in the CMake bug tracker for this issue instead.

gyatpear commented 2 hours ago

Addendum:

When LTO is on: GCC+ld: rebuild GCC+lld: cannot build, however ninja has recorded the /tmp/lto-llvm-XXXXXX.o file Clang+ld: rebuild Clang+lld: not rebuild

gyatpear commented 2 hours ago

It looks like the compiler or linker command is generating incorrect depfile information that includes temporary files that are removed after the compile/link step.

It's either a bug in the toolchain, or the CMake-generated Ninja build plan triggered by CMAKE_INTERPROCEDURAL_OPTIMIZATION=ON, not a problem with Ninja itself.

I recommend you file a bug in the CMake bug tracker for this issue instead.

I don't think it is CMake and I have examined the build.ninja and rules.ninja, nothing special. Also, when I changed to Clang, it was so clean that Ninja was not relinking.

I think the /tmp/ccXXXXXX.ltrans0.ltrans.o files are produced by ld (or collect2) and ld cleans them before its every exit.

But how can I make Ninja to omit the files?

For GCC+ld:

$ ninja -t deps
main: #deps 4, deps mtime 1732706691602482002 (VALID)
    /usr/lib/libgcc_s.so
    /usr/lib/libc.so
    /tmp/ccGTaMBy.ltrans0.ltrans.o
    /usr/lib/ld-linux-x86-64.so.2

For Clang+lld

$ ninja -t deps
main: #deps 13, deps mtime 1732706774280601562 (VALID)
    /usr/lib64/Scrt1.o
    /usr/lib64/crti.o
    /usr/lib64/gcc/x86_64-pc-linux-gnu/14.2.1/crtbeginS.o
    CMakeFiles/main.dir/main.c.o
    /usr/lib64/gcc/x86_64-pc-linux-gnu/14.2.1/libgcc.a
    /usr/lib64/libgcc_s.so
    /usr/lib64/libgcc_s.so.1
    /usr/lib64/libc.so
    /usr/lib/libc.so.6
    /usr/lib/libc_nonshared.a
    /usr/lib/ld-linux-x86-64.so.2
    /usr/lib64/gcc/x86_64-pc-linux-gnu/14.2.1/crtendS.o
    /usr/lib64/crtn.o
digit-google commented 2 hours ago

Thanks for the extra information.

Entries like /tmp/ccGTaMBy.ltrans0.ltrans.o in the linker command's depfile are the problems. They should not appear there as they are temporary files that are no real inputs, and no longer exist after it completes.

I.e. there is really no practical reason to list them in the .d file generated by the link command.

So this definitely looks like a linker bug, not CMake or Ninja related per se. You would probably have the same issue with the Make backend if it reuses the same files.

A temporary work-around would use a wrapper script around the linker command to process its generated .d file and remove the problematic entries before Ninja consumes them :-/