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

Use explicit INSTALL_COMMAND to suppress DESTDIR #21

Merged
merged 2 commits into from
Feb 1, 2021
Merged

Conversation

cottsay
Copy link
Contributor

@cottsay cottsay commented Jan 29, 2021

The DESTDIR environment variable is used on UNIX systems during an invocation of 'make install' to specify an alternate directory prefix for installation paths. Since this isn't a cross-platform mechanism, the CMAKE_INSTALL_PREFIX CMake variable can also be used.

Unfortunately, if both of these are specified, they will both be applied. This isn't typically a problem, but can cause problems for the installation phase of ExternalProject_Add if the external project is built during an invocation of 'make install' with DESTDIR set.

Since the CMAKE_INSTALL_PREFIX method is employed by the call to ExternalProject_Add instead of DESTDIR, we should specifically suppress DESTDIR if it is set so that it doesn't interfere with the installation of the external project to the staging directory.

In this package, there is something subtle happening related to using git as an upstream source combined with a patching step that causes the external project to be re-built during the install phase. This is triggering the behavior described above. Each issue alone does not result in the misbehavior, but when both occur, the result is that the install phase of the external project installs the project to the wrong location - $ENV{DESTDIR}/${CMAKE_INSTALL_PREFIX} - instead of just ${CMAKE_INSTALL_PREFIX}, which corresponds to the staging directory.

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

The DESTDIR environment variable is used on UNIX systems during an
invocation of 'make install' to specify an alternate directory prefix
for installation paths. Since this isn't a cross-platform mechanism,
the CMAKE_INSTALL_PREFIX CMake variable can also be used.

Unfortunately, if both of these are specified, they will both be
applied. This isn't typically a problem, but can cause problems for the
installation phase of ExternalProject_Add if the external project is
built during an invocation of 'make install' with DESTDIR set.

Since the CMAKE_INSTALL_PREFIX method is employed by the call to
ExternalProject_Add instead of DESTDIR, we should specifically suppress
DESTDIR if it is set so that it doesn't interfere with the installation
of the external project to the staging directory.

Signed-off-by: Scott K Logan <logans@cottsay.net>
@cottsay cottsay added bug Something isn't working in review Waiting for review (Kanban column) labels Jan 29, 2021
@cottsay cottsay self-assigned this Jan 29, 2021
@nuclearsandwich
Copy link
Contributor

As far as I can tell from reading the only Bad Stuff:tm: that results from this change is that customizing a ROS 2 build using DESTDIR alone wouldn't affect the vendor packages since the variable would be unset during installation.

Is that actually the case? Are there other caveats to this approach? It seems like the best solution given that it doesn't interfere with the usual workflow.

@cottsay
Copy link
Contributor Author

cottsay commented Jan 29, 2021

So we never want DESTDIR to be set during the installation of an external project to a staging directory if we're already using CMAKE_INSTALL_PREFIX. The installation phase of the vendor package that takes the stuff from the staging directory and installs it to the actual target installation directory will continue to use DESTDIR and/or CMAKE_INSTALL_PREFIX normally.

The problem isn't the use of DESTDIR in and of itself, it's that we currently allow that behavior to extend into the external project where we aren't expecting it to be used.

I'm not aware of any circumstances where we'd want the current behavior to continue.

Actually, if the external project is never built without a DESTDIR value, then the bug will always result in a build failure because the staging directory will never be populated by the external project's install phase.

I'm going to open an issue to discuss how colcon should handle an externally supplied DESTDIR environment variable, so this might not work in the future, but you can reproduce this right now:

$ rm build/uncrustify_vendor/ -rf
$ DESTDIR=/tmp/foobar colcon build --merge --install-base /opt/ros/rolling --packages-select uncrustify_vendor
Starting >>> uncrustify_vendor
--- stderr: uncrustify_vendor                                
Cloning into 'uncrustify-0.68.1'...
HEAD is now at 45b836cf Merge pull request #2308 from emersonknapp/md5-armhf
CMake Error at cmake_install.cmake:54 (file):
  file INSTALL cannot find
  "/opt/ros_src/rolling/build/uncrustify_vendor/uncrustify_vendor_install":
  No such file or directory.


---
Failed   <<< uncrustify_vendor [12.9s, exited with code 1]
                                 
Summary: 0 packages finished [13.9s]
  1 package failed: uncrustify_vendor
  1 package had stderr output: uncrustify_vendor
$ find /tmp/foobar
/tmp/foobar
/tmp/foobar/opt
/tmp/foobar/opt/ros_src
/tmp/foobar/opt/ros_src/rolling
/tmp/foobar/opt/ros_src/rolling/build
/tmp/foobar/opt/ros_src/rolling/build/uncrustify_vendor
/tmp/foobar/opt/ros_src/rolling/build/uncrustify_vendor/uncrustify_vendor_install
/tmp/foobar/opt/ros_src/rolling/build/uncrustify_vendor/uncrustify_vendor_install/bin
/tmp/foobar/opt/ros_src/rolling/build/uncrustify_vendor/uncrustify_vendor_install/bin/uncrustify

In this example, the staging directory should be /opt/ros_src/rolling/build/uncrustify_vendor/uncrustify_vendor_install. That's where the vendor package is expecting the external package to install to, but DESTDIR caused it to prefix the install paths with /tmp/foobar. Since the staging directory should be a build directory, it certainly shouldn't end up anywhere in the final install location, so making CMake anticipate the external project's use of DESTDIR isn't the right thing to do either. The best option is to suppress the behavior.

@cottsay
Copy link
Contributor Author

cottsay commented Jan 29, 2021

Here's that colcon issue I promised: colcon/colcon-cmake#103

Copy link
Contributor

@nuclearsandwich nuclearsandwich left a comment

Choose a reason for hiding this comment

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

One of the best explained changes I've reviewed for some time. Thanks for the clear explanation. The solution makes sense to me.

I have one none blocking question about whether a comment belongs in the source to summarize the necessity but otherwise looks good.

CMakeLists.txt Show resolved Hide resolved
@cottsay cottsay merged commit ca32b1e into master Feb 1, 2021
@delete-merged-branch delete-merged-branch bot deleted the cottsay/destdir branch February 1, 2021 19:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working in review Waiting for review (Kanban column)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants