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

mixing depfile absolute paths with manifest relative paths breaks rebuilds #1251

Open
bradking opened this issue Feb 24, 2017 · 90 comments
Open

Comments

@bradking
Copy link
Contributor

The ninja manual warns against such mixture, but it has become a serious limitation.

The problem is that people want their build systems to invoke the compiler with an absolute path to the source file. This produces better output for IDEs to match error messages and refer back to the original files. It also may affect the paths recorded in debug information. However, this conflicts with ninja's preference for relative paths in the build manifest.

Here is a script demonstrating the issue: ninja-bug.bash.txt

@bradking
Copy link
Contributor Author

Cc: @nico @chaoren

Over in CMake, there is relevant discussion in issue 16675, issue 13894, and MR 520.

@zlolik
Copy link

zlolik commented Mar 6, 2017

As workaround you can set object output directory explicitly, even "." or "..", but absolute path works also.
ninjatst.bash.txt

@bradking
Copy link
Contributor Author

bradking commented Mar 6, 2017

@zlolik IIUC you suggest placing absolute paths in the build manifest (build.ninja) graph nodes. Certainly that works (and is what CMake does in out-of-source builds), but my point here is that Ninja discourages use of absolute paths and yet IDEs require them in output.

It is also hypothetically possible that a compiler given -c foo.c could generate an absolute path in the depfile anyway. If that happens then Ninja will not recognize the dependency due to the same problem reported here.

Fixing this will require Ninja to be able to recognize when a relative path and absolute path are the same file and have only one graph node internally instead of two. Doing this only for depfile processing may be sufficient, as it is reasonable to ask the build.ninja generators to name paths consistently within the build manifest.

@zlolik
Copy link

zlolik commented Mar 7, 2017

@bradking First of all, I am agree with you that we have an issue with ninja and I only suggest workaround.
I think there are 3 linked issues:

  1. Logic with input absolute/relative path, more details in $pwd #1152 .
  2. Logic to force output absolute/relative path. The problem with compilers - the only way to said to compiler that you want to have absolute path in output is use absolute path in command line. Not ninja issue but ours.
  3. Logic to parse depfile absolute/relative path. This issue.

I use 2 workarounds:

  1. Use prefixes $p and $o in path of build.ninja generator to solve first 2 problems. In my case $p and $o are not absolute but relative from current dir where build.ninja generator was run.
  2. Use some script to convert depfile to ninja compatible path. Some compilers even need separate call to generate depfile. But there is no absolute path problem while.

@bradking
Copy link
Contributor Author

For reference, I've started a thread on the ninja-build mailing list with a proposed solution.

@chfast
Copy link

chfast commented Dec 3, 2017

Another related issues about debug and coverage information: using commands with relative paths creates also debug information files with relative paths. The problem is the debug files are usually placed next to the object files, not the source files, so the relative paths there reference non-existing files.

@thughes
Copy link

thughes commented Jan 4, 2018

Any update on this? There haven't been any responses on the mailing list thread...

@sebknzl
Copy link

sebknzl commented Apr 28, 2019

Hello everyone,

It is also hypothetically possible that a compiler given -c foo.c could generate an absolute path in the depfile anyway.

IAR v8's dependency files contain absolute paths only.

We've hit this problem when doing out-of-source builds with CMake/Ninja/IAR: Generated files which end up in CMake's binary dir are relative in the Ninja manifest, the deps output by IAR are absolute.

To me, this is clearly a Ninja-issue: It shouldn't have two nodes for the same file, no matter what.

I've read @bradking 's suggestion, but I don't really understand the part about symbolic links and why getcwd() can't be used. Would you mind elaborating?

@bradking
Copy link
Contributor Author

I don't really understand the part about symbolic links and why getcwd() can't be used

@sebknzl the problem is that the absolute paths written to build.ninja by the generator may contain symbolic links that are intentionally not resolved for one reason or another. getcwd() typically returns a path with symlinks resolved. If Ninja were to interpret a relative path in the build manifest or a depfile relative to getcwd() then the absolute path it constructs may not match what the build manifest generator used and we'd still end up with two nodes.

The ninja_workdir I propose in the thread you linked is basically telling Ninja what the generator used as the base for relative paths. In practice realpath(ninja_workdir) == realpath(getcwd()) should always be true, but that does not mean ninja_workdir == getcwd(). Another alternative would be for Ninja to realpath everything before creating nodes, but that would mean $in would not expand to what the generator intended.

@bradking
Copy link
Contributor Author

@jhasse please see this issue and my proposal to address it.

@ClausKlein
Copy link
Contributor

Why not generate a build.ninja with absolute paths controlled by an CMAKE_NINJA_GENERATOR_OPTION_ ?

i.e. like this, which works:

Claus-MBP:build clausklein$ cat build.ninja 

# project dir
p = /Users/clausklein/Downloads
# object dir
o = /Users/clausklein/Downloads/.ninjatst.build

rule cp
  command = cp $in $out

rule cc
  depfile = $out.d
  deps = gcc
  command = cc -I$o -c $in -o $out -MD -MT $out -MF $out.d

# All absolute paths works
build all: phony $o/foo.o

# All absolute paths works
build $o/foo.h: cp $p/foo.h.in
  IN = $p/foo.h.in

# All absolute paths works
build $o/foo.o: cc $p/foo.c || $o/foo.h
  IN = "$p/foo.c"

default all

Claus-MBP:build clausklein$ 

generated with ninjatst.bash.txt

@bradking
Copy link
Contributor Author

@ClausKlein we did try teaching CMake to use absolute paths. See the discussions I linked in #1251 (comment). I'd rather not add an option that says "do things in a different way that fixes some cases and breaks others".

We really need the Ninja feature in the proposal I linked in #1251 (comment) to fix this properly.

@ClausKlein
Copy link
Contributor

ClausKlein commented Mar 12, 2020 via email

@jhasse
Copy link
Collaborator

jhasse commented Mar 12, 2020

Let's say Ninja would use ninja_workdir for matching depfile information and we have the situation where ninja_workdir != getcwd() because of a symlink. What if the compiler now prints absolute paths by using getcwd()?

@bradking
Copy link
Contributor Author

The compiler should not use getcwd() for anything unless those things were passed in as relative paths, in which case the buildsystem generator likely needs to take responsibility.

Or we can consider trying to normalize w.r.t. both ninja_workdir and getcwd().

@jhasse
Copy link
Collaborator

jhasse commented Mar 12, 2020

Well, the compiler should also not return an absolute path in the depfile when given relative paths ;)

@ClausKlein we did try teaching CMake to use absolute paths. See the discussions I linked in #1251 (comment). I'd rather not add an option that says "do things in a different way that fixes some cases and breaks others".

What exactly broke when you used absolute paths as suggested by @ClausKlein?

@bradking
Copy link
Contributor Author

If the compiler is given absolute paths then the depfile it generates has absolute paths, and those are not reconciled with relative paths to headers in the build manifest, e.g. for generated headers. See the ninja-bug.bash.txt link in my first post at the top of this issue. It shows a step-by-step example of what happens and is expressed purely in terms of Ninja and no CMake.

@jhasse
Copy link
Collaborator

jhasse commented Mar 12, 2020

I've read that, but why not use #1251 (comment)? Simply because Ninja "discourages" absolute paths? If so, this is about a documentation change?

@bradking
Copy link
Contributor Author

build.ninja files are much smaller without repeating absolute paths everywhere, and that makes them faster. They also look nicer, are easier to debug, and are more appropriate for tab-completion of build target names in shells. IMO it is a good recommendation for Ninja to make. Ninja just needs a bit more information to reconcile this case, and that is what I propose.

@ClausKlein
Copy link
Contributor

@bradking relative paths may not always short:

build jsoncpp@sha/src_lib_json_json_reader.cpp.o: cpp_COMPILER ../../../../../Users/clausklein/Workspace/cpp/jsoncpp/src/lib_json/json_reader.cpp
 DEPFILE = jsoncpp@sha/src_lib_json_json_reader.cpp.o.d
 ARGS = -Ijsoncpp@sha -I. -I../../../../../Users/clausklein/Workspace/cpp/jsoncpp -I../../../../../Users/clausklein/Workspace/cpp/jsoncpp/include -I../../../../../Users/clausklein/Workspace/cpp/jsoncpp/dir1 -I../../../../../Users/clausklein/Workspace/cpp/jsoncpp/dir2 -fdiagnostics-color=always -pipe -Wall -Winvalid-pch -Wnon-virtual-dtor -std=c++17 -O3

my example #1251 (comment) looks nicer!

@bradking
Copy link
Contributor Author

CMake's generators have a rule about not using ../ sequences that leave the build tree. In fully out-of-source builds we do use absolute paths to the source tree and relative paths within the build tree. The latter never start in ../ because they are under the build tree.

@ClausKlein
Copy link
Contributor

I know, it was generated with mesonbuild. ;-)

But than, why not deny the generation into source tree?

@bradking
Copy link
Contributor Author

We can debate the merits of relative v. absolute paths forever. Ninja should not be opinionated about that. The problem here is reconciling cases where both are used within a single build. The buildsystem generator cannot always control what the tools will do with paths. Ninja should support mixed absolute/relative paths to the same file and can do so with a simple approach I've outlined in my proposal. For the motivating use case the changes are isolated to depfile parsing.

@ClausKlein
Copy link
Contributor

Ninja should support mixed absolute/relative paths to the same file

I agree!

@jhasse
Copy link
Collaborator

jhasse commented Mar 12, 2020

Thanks for all the explanations, I'm starting to understand.

What if Ninja would (upon encountering an absolute path in the depfile) realpath both cwd and the path it found? Then it would remove the realpathed cwd from the realpathed depfile-path to get the relative path to match against the generated header. Would that work?

@bradking
Copy link
Contributor Author

getcwd() is always a realpath already so that step isn't necessary. If Ninja were to realpath() the depfile-provided absolute path and check for a cwd prefix that could work. However, realpath() is a possibly-expensive syscall while my proposed ninja_workdir can reconcile paths purely in memory via string processing.

@bradking
Copy link
Contributor Author

Mixing in the other direction should also be allowed: an absolute path in the build manifest but a relative path in the depfile. I don't think Ninja should have to realpath() every absolute path while parsing build.ninja. That would probably be slow.

@jhasse
Copy link
Collaborator

jhasse commented Mar 13, 2020

We could use the result to determine the workdir. E.g.:

getcwd(): /var/xyz/build
depfile entry: /home/foo/build/bar.h
realpath(depfile entry): /var/xyz/build/bar.h
relative realpath: bar.h
remove that from the depfile entry: /home/foo/build -> ninja_workdir

This way we only need to call realpath once.

@bradking
Copy link
Contributor Author

I do not think deriving ninja_workdir automatically is possible in general. How do we know how many logical components of the original symlink-containing logical path correspond to the workdir? We might be able to do it by taking one component at a time until its realpath matches the cwd, but that feels hacky and error prone. It feels risky to trust the first symlink that happens to look like this. Also we'd have to run realpath() on every absolute path we encounter until one resolves this way.

The buildsystem generator knows exactly what logical path to the workdir it used when generating absolute paths in build statements and command lines in build.ninja. It is therefore the most reliable source of information for the workdir path. We just need a way to communicate this information to Ninja, hence my proposed ninja_workdir binding.

KyleFromKitware added a commit to KyleFromKitware/Charm that referenced this issue Mar 26, 2021
CMake 3.20 changes the behavior of AUTOMOC in a subtle way
(https://gitlab.kitware.com/cmake/cmake/-/issues/21977) which
revealed a circular dependency between the autogen target and the
files generated by qt5_wrap_ui(). The circular dependency always
existed, but did not appear until CMake 3.20 due to a bug in Ninja
(ninja-build/ninja#1251).

Resolve this issue by using AUTOUIC as well as AUTOMOC, and not
combining the CMake AUTOMOC approach with the Qt qt5_wrap_ui()
approach.
KyleFromKitware added a commit to KyleFromKitware/Charm that referenced this issue Mar 26, 2021
CMake 3.20 changed the behavior of AUTOMOC in a subtle way
(https://gitlab.kitware.com/cmake/cmake/-/issues/21977) which
revealed a circular dependency between the autogen target and the
files generated by qt5_wrap_ui(). The circular dependency always
existed, but did not appear until CMake 3.20 due to a bug in Ninja
(ninja-build/ninja#1251).

Resolve this issue by using AUTOUIC as well as AUTOMOC, and not
combining the CMake AUTOMOC approach with the Qt qt5_wrap_ui()
approach.
@jhasse
Copy link
Collaborator

jhasse commented Mar 31, 2021

I have several times spent hours tracking down a dependencies problem only to find that is another instance of depfile paths not being reconciled with build manifest paths.

Ninja silently ignoring this is a huge pain point. Maybe we should add an option -d realpath which goes all the way and warns when there's too "different" paths pointing the same "real" path. That option would still be helpful if relative and absolute paths could be mixed because of symbolic links (or with the ninja_workdir change if that option has been set incorrectly).

@kobalicek
Copy link

I'm for merging the mentioned PR - #1924

It solves the problem many users have and it doesn't hurt ninja in any way. I really don't understand why it's so hard to fix ninja issues.

@JBakamovic
Copy link

I'm experiencing problems with my dev environment for years now exactly because of this issue. It's not a deal-breaker per se, I still very much prefer using ninja because of better development turn-around times, but I must say it's very annoying because, whenever compile_commands.json is regenerated (on big project every day), I must not forget to re-apply the sed voodoo which will fix the relative paths in it. If I do, and I do, tooling will start to fail miserably because it will not be able to find appropriate paths anymore and thus basically any operation that comes with it will stop working reliably or will stop working at all (indexing, jumping to definition, finding all references, semantic syntax highlighting, etc.). This is very disruptive and something nobody wants to keep in their mind while doing the development.

Tooling is based on LLVM/Clang infrastructure (libclang) and uses compile_commands.json to extract relevant information about paths, directories, build flags etc. Given that there are many dev tools nowadays which are built on top of this combination too, I'm sure that they are:

  1. Either suffering from the relative path problems and have no workarounds implemented for them
  2. Or they've implemented some sort of heuristics to workaround the problem

This is either bad for the end users (devs using the tooling which do not have workarounds) or it's bad for the tooling devs because they have to maintain it and make sure it does not break in future releases. I think that giving a chance to implant this behavior in ninja will let alone make many devs more happier and more productive.

@jhasse
Copy link
Collaborator

jhasse commented Apr 27, 2021

whenever compile_commands.json is regenerated

by CMake or the compdb tool?

@JBakamovic
Copy link

cmake ../ -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

@duanyutong
Copy link

duanyutong commented Apr 28, 2021

cmake ../ -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

+1. We have the same usage and issue. Commands generated by ninja have relative paths for the dependencies in the exported build_commands.json and there's no way to toggle the path format. No such problem when using Unix Makefiles instead of ninja.

@bradking
Copy link
Contributor Author

cmake ../ -GNinja

When the build tree is placed inside the source tree, CMake chooses to use relative paths starting in ../ to reference source files from build.ninja. If the build tree is placed outside the source tree, CMake will reference files in the source tree by absolute path. CMake Issue 13894 proposes changing to absolute paths in both cases.

@kobalicek
Copy link

kobalicek commented Apr 28, 2021

@bradking It would be really nice to get this fixed somehow. To be honest I'm just an end user of both CMake and Ninja so I don't understand the issue in full detail, but it has been annoying me for years. I started using unix makefiles generator again, because of it, and I don't mind longer build times if the tooling around it works properly.

I don't really expect the PR to be merged, so maybe CMake defaulting to absolute paths or giving users some option to do so?

@JBakamovic
Copy link

@bradking Above was just an example of command. In reality I am using out-of-source builds and the issue with relative paths persists.

Repro:

$ cd /tmp
$ git clone https://github.com/mysql/mysql-server.git
$ mkdir build_ninja
$ cd build_ninja
$ cmake ../mysql-server -GNinja -DCMAKE_BUILD_TYPE=Debug -DDOWNLOAD_BOOST=1 -DWITH_BOOST=../ -DWITH_UNIT_TESTS=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
$ cd /tmp
$ mkdir build_makefile
$ cd build_makefile
$ cmake ../mysql-server -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug -DDOWNLOAD_BOOST=1 -DWITH_BOOST=../ -DWITH_UNIT_TESTS=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
$ tail /tmp/build_ninja/compile_commands.json

"directory": "/tmp/build_ninja",
  "command": "/usr/lib64/ccache/c++ -DHAVE_CONFIG_H -DHAVE_TLSv13 -DLZ4_DISABLE_DEPRECATE_WARNINGS -DMYSQL_ROUTER_LOG_DOMAIN=\\\"routing\\\" -DRAPIDJSON_NO_SIZETYPEDEFINE -DRAPIDJSON_SCHEMA_USE_INTERNALREGEX=0 -DRAPIDJSON_SCHEMA_USE_STDREGEX=1 -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -D_USE_MATH_DEFINES -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Drouting_EXPORTS -I. -Iinclude -I/tmp/mysql-server -I/tmp/mysql-server/include -Irouter -Irouter/include -I/tmp/mysql-server/router/src/metadata_cache/include -I/tmp/mysql-server/router/src/routing/include -Irouter/src/routing/include -I/tmp/mysql-server/router/src/harness/src/../include -Irouter/src/harness/src/src_SHARED -I/tmp/mysql-server/router/src/mysql_protocol/include -I/tmp/mysql-server/router/src/router/src/../include -Irouter/src/harness/src/../include -I/tmp/mysql-server/router/src/io/src/../include -Irouter/src/io/src/../include -I/tmp/mysql-server/router/src/harness/include -isystem /tmp/mysql-server/extra/protobuf/protobuf-3.11.4/src -isystem /tmp/mysql-server/extra/rapidjson/include -isystem /tmp/mysql-server/extra/lz4 -isystem /tmp/mysql-server/extra/libedit/libedit-20191231-3.1/src/editline -isystem /tmp/mysql-server/extra/zstd/lib -isystem extra/zlib -isystem /tmp/mysql-server/extra/zlib -isystem plugin/x/generated/protobuf_lite -std=c++14 -fno-omit-frame-pointer -ftls-model=initial-exec  -Wall -Wextra -Wformat-security -Wvla -Wundef -Wmissing-format-attribute -Woverloaded-virtual -Wcast-qual -Wimplicit-fallthrough=2 -Wstringop-truncation -Wsuggest-override -Wmissing-include-dirs -Wlogical-op -Werror -DSAFE_MUTEX -DENABLED_DEBUG_SYNC -g -fPIC -o router/src/routing/CMakeFiles/routing.dir/src/x_protocol_splicer.cc.o -c /tmp/mysql-server/router/src/routing/src/x_protocol_splicer.cc",
  "file": "/tmp/mysql-server/router/src/routing/src/x_protocol_splicer.cc"
},
{
  "directory": "/tmp/build_ninja",
  "command": "/usr/lib64/ccache/c++ -DHAVE_CONFIG_H -DHAVE_TLSv13 -DLZ4_DISABLE_DEPRECATE_WARNINGS -DMYSQL_ROUTER_LOG_DOMAIN=\\\"routing\\\" -DRAPIDJSON_NO_SIZETYPEDEFINE -DRAPIDJSON_SCHEMA_USE_INTERNALREGEX=0 -DRAPIDJSON_SCHEMA_USE_STDREGEX=1 -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -D_USE_MATH_DEFINES -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Drouting_EXPORTS -I. -Iinclude -I/tmp/mysql-server -I/tmp/mysql-server/include -Irouter -Irouter/include -I/tmp/mysql-server/router/src/metadata_cache/include -I/tmp/mysql-server/router/src/routing/include -Irouter/src/routing/include -I/tmp/mysql-server/router/src/harness/src/../include -Irouter/src/harness/src/src_SHARED -I/tmp/mysql-server/router/src/mysql_protocol/include -I/tmp/mysql-server/router/src/router/src/../include -Irouter/src/harness/src/../include -I/tmp/mysql-server/router/src/io/src/../include -Irouter/src/io/src/../include -I/tmp/mysql-server/router/src/harness/include -isystem /tmp/mysql-server/extra/protobuf/protobuf-3.11.4/src -isystem /tmp/mysql-server/extra/rapidjson/include -isystem /tmp/mysql-server/extra/lz4 -isystem /tmp/mysql-server/extra/libedit/libedit-20191231-3.1/src/editline -isystem /tmp/mysql-server/extra/zstd/lib -isystem extra/zlib -isystem /tmp/mysql-server/extra/zlib -isystem plugin/x/generated/protobuf_lite -std=c++14 -fno-omit-frame-pointer -ftls-model=initial-exec  -Wall -Wextra -Wformat-security -Wvla -Wundef -Wmissing-format-attribute -Woverloaded-virtual -Wcast-qual -Wimplicit-fallthrough=2 -Wstringop-truncation -Wsuggest-override -Wmissing-include-dirs -Wlogical-op -Werror -DSAFE_MUTEX -DENABLED_DEBUG_SYNC -g -fPIC -o router/src/routing/CMakeFiles/routing.dir/src/destination_ssl_context.cc.o -c /tmp/mysql-server/router/src/routing/src/destination_ssl_context.cc",
  "file": "/tmp/mysql-server/router/src/routing/src/destination_ssl_context.cc"
}
]

$ tail /tmp/build_makefile/compile_commands.json

  "directory": "/tmp/build_makefile/router/src/routing",
  "command": "/usr/lib64/ccache/c++ -DHAVE_CONFIG_H -DHAVE_TLSv13 -DLZ4_DISABLE_DEPRECATE_WARNINGS -DMYSQL_ROUTER_LOG_DOMAIN=\\\"routing\\\" -DRAPIDJSON_NO_SIZETYPEDEFINE -DRAPIDJSON_SCHEMA_USE_INTERNALREGEX=0 -DRAPIDJSON_SCHEMA_USE_STDREGEX=1 -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -D_USE_MATH_DEFINES -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Drouting_EXPORTS -I/tmp/build_makefile -I/tmp/build_makefile/include -I/tmp/mysql-server -I/tmp/mysql-server/include -I/tmp/build_makefile/router -I/tmp/build_makefile/router/include -I/tmp/mysql-server/router/src/metadata_cache/include -I/tmp/mysql-server/router/src/routing/include -I/tmp/build_makefile/router/src/routing/include -I/tmp/mysql-server/router/src/harness/src/../include -I/tmp/build_makefile/router/src/harness/src/src_SHARED -I/tmp/mysql-server/router/src/mysql_protocol/include -I/tmp/mysql-server/router/src/router/src/../include -I/tmp/build_makefile/router/src/harness/src/../include -I/tmp/mysql-server/router/src/io/src/../include -I/tmp/build_makefile/router/src/io/src/../include -I/tmp/mysql-server/router/src/harness/include -isystem /tmp/mysql-server/extra/protobuf/protobuf-3.11.4/src -isystem /tmp/mysql-server/extra/rapidjson/include -isystem /tmp/mysql-server/extra/lz4 -isystem /tmp/mysql-server/extra/libedit/libedit-20191231-3.1/src/editline -isystem /tmp/mysql-server/extra/zstd/lib -isystem /tmp/build_makefile/extra/zlib -isystem /tmp/mysql-server/extra/zlib -isystem /tmp/build_makefile/plugin/x/generated/protobuf_lite -std=c++14 -fno-omit-frame-pointer -ftls-model=initial-exec  -Wall -Wextra -Wformat-security -Wvla -Wundef -Wmissing-format-attribute -Woverloaded-virtual -Wcast-qual -Wimplicit-fallthrough=2 -Wstringop-truncation -Wsuggest-override -Wmissing-include-dirs -Wlogical-op -Werror -DSAFE_MUTEX -DENABLED_DEBUG_SYNC -g -fPIC -o CMakeFiles/routing.dir/src/x_protocol_splicer.cc.o -c /tmp/mysql-server/router/src/routing/src/x_protocol_splicer.cc",
  "file": "/tmp/mysql-server/router/src/routing/src/x_protocol_splicer.cc"
},
{
  "directory": "/tmp/build_makefile/router/src/routing",
  "command": "/usr/lib64/ccache/c++ -DHAVE_CONFIG_H -DHAVE_TLSv13 -DLZ4_DISABLE_DEPRECATE_WARNINGS -DMYSQL_ROUTER_LOG_DOMAIN=\\\"routing\\\" -DRAPIDJSON_NO_SIZETYPEDEFINE -DRAPIDJSON_SCHEMA_USE_INTERNALREGEX=0 -DRAPIDJSON_SCHEMA_USE_STDREGEX=1 -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -D_USE_MATH_DEFINES -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Drouting_EXPORTS -I/tmp/build_makefile -I/tmp/build_makefile/include -I/tmp/mysql-server -I/tmp/mysql-server/include -I/tmp/build_makefile/router -I/tmp/build_makefile/router/include -I/tmp/mysql-server/router/src/metadata_cache/include -I/tmp/mysql-server/router/src/routing/include -I/tmp/build_makefile/router/src/routing/include -I/tmp/mysql-server/router/src/harness/src/../include -I/tmp/build_makefile/router/src/harness/src/src_SHARED -I/tmp/mysql-server/router/src/mysql_protocol/include -I/tmp/mysql-server/router/src/router/src/../include -I/tmp/build_makefile/router/src/harness/src/../include -I/tmp/mysql-server/router/src/io/src/../include -I/tmp/build_makefile/router/src/io/src/../include -I/tmp/mysql-server/router/src/harness/include -isystem /tmp/mysql-server/extra/protobuf/protobuf-3.11.4/src -isystem /tmp/mysql-server/extra/rapidjson/include -isystem /tmp/mysql-server/extra/lz4 -isystem /tmp/mysql-server/extra/libedit/libedit-20191231-3.1/src/editline -isystem /tmp/mysql-server/extra/zstd/lib -isystem /tmp/build_makefile/extra/zlib -isystem /tmp/mysql-server/extra/zlib -isystem /tmp/build_makefile/plugin/x/generated/protobuf_lite -std=c++14 -fno-omit-frame-pointer -ftls-model=initial-exec  -Wall -Wextra -Wformat-security -Wvla -Wundef -Wmissing-format-attribute -Woverloaded-virtual -Wcast-qual -Wimplicit-fallthrough=2 -Wstringop-truncation -Wsuggest-override -Wmissing-include-dirs -Wlogical-op -Werror -DSAFE_MUTEX -DENABLED_DEBUG_SYNC -g -fPIC -o CMakeFiles/routing.dir/src/destination_ssl_context.cc.o -c /tmp/mysql-server/router/src/routing/src/destination_ssl_context.cc",
  "file": "/tmp/mysql-server/router/src/routing/src/destination_ssl_context.cc"
}
]

First obvious difference is in -I. -Iinclude vs -I/tmp/build_makefile -I/tmp/build_makefile/include but there're plenty of other relative paths which are not present in Makefile compile_commands.json. E.g. -Irouter, -Irouter/include, -Irouter/src/routing/include, -isystem extra/zlib, -isystem plugin/x/generated/protobuf_lite, etc. etc.

@bradking
Copy link
Contributor Author

@JBakamovic thanks for the example. However, CMake's behavior would be best discussed over in CMake's issue tracker. See CMake Issue 13894. compile_commands.json is generated using the same command lines that are put in build.ninja. They use relative paths in many cases because that is what Ninja conventions prefer. CMake can probably switch to absolute paths for more cases than it has now, but paths inside the build tree probably need to remain relative unless Ninja addresses the concern posted in this comment above.

johslarsen added a commit to johslarsen/dotfiles that referenced this issue May 18, 2021
There is a [ninja #1251] issue that forces cmake to generate build files
with relative instead of absolute paths. The effect is that debug
information in binaries are relative to the build directories, which for
out-of-source builds is not very compatible with running vim from the
project root. When running vim from the project root the paths reported
for build errors, test failures, and coverage information is wrong.

[ninja #1251]: ninja-build/ninja#1251
@dvolosnykh
Copy link

In the same way as @kobalicek, I am the end user of CMake using Ninja as a backend. I also had to switch our usage of CMake to Unix Makefiles generator due to this issue.

What really disappoints me is a 4-year holy war around relative vs absolute paths resulting in nothing. I believe the tool shall give choices.

@bradking I am really thankful for your efforts in pushing this issue for such a long time.

@bradking
Copy link
Contributor Author

CMake 3.21 includes a fix to CMake Issue 13894 that does not require changes to Ninja. Instead CMake uses a workaround to this issue by expressing outputs as both relative and absolute paths. It causes Ninja to be less efficient by stating each output file twice, but at least we can use absolute paths.

@GrandChris
Copy link

@bradking That sounds great, I'd also like to thank you for your efforts on this issue. I've been following this thread for months now, personally I've already given up that there will ever be a solution.

@GrandChris
Copy link

@bradking I've been eager and tried the CMake 3.21.0 version which uses absolute paths and is great. What I noticed is that dependencies no longer work with generated headers.
Specifically: I create header files from Json files with "add_custom_target" via CMake. The header files are included in cpp files, which means that Ninja must discover these headers and include them in the dependency tree. Apparently this only works with relative paths.
For me, this means that if I made some changes in a Json file, I have to compile my project twice in order for those changes to be applied to the executable. For now I rather deal with this than with relative paths, but this is still not perfect.

@bradking
Copy link
Contributor Author

@GrandChris please open an issue in CMake's issue tracker for that.

@dvolosnykh
Copy link

@bradking @GrandChris Has the issue with generated headers been filed in CMake's issue tracker? Could not find anything related.

@bradking
Copy link
Contributor Author

bradking commented Sep 2, 2021

@dvolosnykh it is CMake Issue 22454, and turned out not to be a regression or a problem caused by the switch to absolute paths.

@motoz
Copy link

motoz commented Mar 18, 2022

I'm having trouble with our custom generator where i try to support both in tree and completely out of tree builds. To me it seems that I have no control over when gcc starts outputting absolute paths in the .d files, which then breaks incremental builds badly. I have taken great pains to use relative paths everywhere but despite that plain old gcc (on windows, but still) starts to spit out absolute paths seemingly when it finds a header using a relative path that 'touches root' or something like that. I'd like to be able to point the generator with e.g. --builddir=/temp/testbuild while running the configurator (and ninja) from the source tree. The problem is that I generate headers during the build that I naturally place in the build directory (which then is referenced by e.g. -I../../temp/testbuild, i.e. relatively), but despite that gcc insists on switching to an absolute path for headers found from that build directory. If I run the same build with e.g. --builddir=build, i.e. on top of the source tree, then gcc spits out a relative path for the same header. If I could work out the exact logic how and when gcc generates a relative or an absolute path (well I guess I could look at the source...) I suppose I should be able to work around it in the generator, but it would be so much easier if ninja could handle it.

@GrandChris
Copy link

Hello @motoz , relative paths are fine, if all paths start at the root of your project directory. If this is not possible, e.g. because some tool insists of starting all paths from a build directory, I highly recommend using absolute paths for everything. Otherwise you will keep fighting all sorts of issues with your paths.

@motoz
Copy link

motoz commented Mar 21, 2022

Thanks @GrandChris for the pointer. Switching everything to absolute paths really works, at least so far I haven't came across something that doesn't. And it also seems to be the only way to make generated headers work with gcc, which really goes against the recommendation to use relative paths in the ninja documentations. The doc was probably the reason I went with all relative paths (them being generally shorter and nicer to read another).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet