As part of our build process, we also install some dependencies that we have. Here is a short mostly-reproduction of what that looks like:
cmake_minimum_required(VERSION 3.17)
project(wat)
# this is a path on my machine that the install
# command is allowed to write to, but I cannot
# I have sudo permission for that command only
set(FMT_MODULE /path/to/wherever/fmt-6.1.2)
# target to install fmt
add_custom_command(
OUTPUT ${FMT_MODULE}/lib64/libfmt.a
COMMAND flock /tmp -c 'sudo yum -y install fmt-6.1.2')
add_custom_target(install-fmt DEPENDS ${FMT_MODULE}/lib64/fmt.a)
# the imported fmt library
add_library(fmt-imported STATIC IMPORTED)
set_target_properties(fmt-imported PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${FMT_MODULE}/include
IMPORTED_LOCATION ${FMT_MODULE}/lib64/libfmt.a
)
# working around a cmake bug
add_library(fmt INTERFACE)
target_link_libraries(fmt INTERFACE fmt-imported)
add_dependencies(fmt install-fmt)
# just a dumb executable that needs fmt
add_executable(foo foo.cxx)
target_link_libraries(foo PUBLIC fmt)
In our setup, yum install will install that package in the path ${FMT_MODULE} and while yum has permissions to do so (and I have permissions to run sudo yum in this context), I don’t, in general, have permissions to do anything in that path.
The above works totally fine if I use Makefile generators. If fmt is not installed, it will be installed (at build time), and everything just works exactly as I would like.
However, If I use Ninja, then this does not work. If fmt is not installed, then I get a permission denied failure because something is trying to run mkdir on that directory and I do not have permissions to do so. I’ve tried multiple permutations tried to indicate that the add_custom_command will end up creating that dependency but everything I’ve tried is a dead end, I just always get that “Permission denied” failure.
Is there a way to get this to work with Ninja? (@craig.scott)
We need to narrow down where the “Permission denied” message is coming from. Are you able to narrow down the command in the build.ninja file that is being executed at the time that message is generated? You could try adding --verbose to your ninja command line.
But the command there is… exactly what I asked for it to do - just install the thing, there’s no mkdir.
That last output is when I also added ${FMT_MODULE}/, the directory, as an OUTPUT, which isn’t what I had in the OP. But the behavior is the same if I go back and remove it again.
I can see how that’s a convenient default, since that’s usually (nearly always?) what you would want to do anyway, but in this particular case it’s… extremely inconvenient.
Is there a workaround to tell Ninja to not do this? Otherwise, we basically can’t use Ninja in this context. Like, I promise that the COMMAND here will actually create the directory and the file as it says it does.
No, there’s no workaround. Though I am wondering how you install a package to an arbitrary directory without a flag like yum --root= or something (I guess you could have a custom repo, but why not force it to be that one repo then?). Usually I would suggest to put such instructions into documentation rather than the build recipe and hard-fail if you can’t find fmt (e.g., this fails on non-Red Hat or newer CentOS/Fedora distros that lack yum for example). You also seem to be assuming x86_64 and not supporting other arches which use /usr/lib instead. In addition, anyone without sudo rights is blocked and must modify the build system rather than being able to provide their own fmt. Maybe that’s fine in this instance, but it is weird to me.
I found a workaround. Instead of trying to use the dependency exactly where it will actually be installed, I’m adding a sym-link in my build directory and pointing the rest of my build through that sym-link. This way, when Ninja creates the directory, it’s inside of my build directory (which is fine). This works on both Ninja and Make.
In reality the yum command is slightly different than what I showed, but not in a way that really affects the answer here - suffice it to say that I know where the package ends up being installed and what it looks like, and that the yum command has permissions to write in that location but I otherwise do not.