81

I am trying to find some sudo-free solution to enable my users install and unistall my application. Using

set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/opt/${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}/")
SET(CMAKE_INSTALL_RPATH "$ENV{HOME}/${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}/")

I can direct the files to the user's home directory, and

make install

works fine. With reference to What's the opposite of 'make install', ie. how do you uninstall a library in Linux? I did not find any idea, which is sudo-free and is not complex for a non-system-admin person.

  1. Is anyhow make uninstall supported by CMake?

  2. My uninstall is quite simple: all files go in a subdirectory of the user's home. In principle, removed that new subdirectory could solve the problem. Has make install, with parameters above, any side effect, or I can write in my user's guide that the newly produced subdirectory can be removed as 'uninstall'?

Community
  • 1
  • 1
katang
  • 2,474
  • 5
  • 24
  • 48

6 Answers6

143

If you want to add an uninstall target you can take a look to the official CMake FAQ at:

https://gitlab.kitware.com/cmake/community/wikis/FAQ#can-i-do-make-uninstall-with-cmake

If you just want a quick way to uninstall all files, just run:

xargs rm < install_manifest.txt

install_manifest.txt file is created when you run make install.

John
  • 1,472
  • 1
  • 19
  • 22
  • 10
    But this only removes files not directories. That's ok if you only have headers and libraries but if you install a python package, you have to create directories like `/home/user/.local/miniconda3/envs/galario3/lib/python3.6/site-packages/galario/`. the simple `xargs ...` does not delete them so you have to manually add the logic, for example in an `uninstall` target – Fred Schoen Sep 29 '17 at 11:00
  • Thanks for the tip for a way to get around one of the many glaring stupidities of cmake. An uninstall target it very important. – Edward Hartnett Feb 18 '19 at 14:47
  • `xargs -a install_manifest.txt rm` also works. – abu_bua Nov 25 '22 at 03:56
  • 1
    When using the environment variable `DESTDIR` for installation, the above needs to be generalized, for instance to `xargs -I{} rm -vf $DESTDIR{} – dvo May 11 '23 at 15:21
32

No there is not. See in the FAQ from CMake wiki:

By default, CMake does not provide the "make uninstall" target, so you cannot do this. We do not want "make uninstall" to remove useful files from the system.

If you want an "uninstall" target in your project, then nobody prevents you from providing one. You need to delete the files listed in install_manifest.txt file. [followed by some example code]

usr1234567
  • 21,601
  • 16
  • 108
  • 128
  • 4
    I am glad to see that there are people who belive that if one installs a file during the development, it shall stay there forever. Thanks for the hint, I will use the example code. BTW: I think you also answered my #2 question: simply removing the directory containing the files will have no side effect. – katang Jan 04 '17 at 20:14
27

Remove files and folders (empty only) added by make install from a cmake project:

cat install_manifest.txt | sudo xargs rm
cat install_manifest.txt | xargs -L1 dirname | sudo xargs rmdir -p

The second command will print a bunch of errors because it recursively deletes folders until it finds one that is not empty. I like seeing those errors to know which folders are left. If you want to hide these errors you can add --ignore-fail-on-non-empty to rmdir.

qwertzguy
  • 15,699
  • 9
  • 63
  • 66
  • 2
    Seems like xargs in the first command will not [handle spaces](https://stackoverflow.com/questions/16758525/how-can-i-make-xargs-handle-filenames-that-contain-spaces) in file names correctly. – Xunie May 04 '18 at 01:01
  • Right, it would need some tweaking to support that. But at least the good thing is it will just error out, and won't delete bunch of unrelated files because of the space character. – qwertzguy May 05 '18 at 02:46
  • 3
    **Definitely not!** It will NOT error out if the path actually exists. Example: If the working directory is `~` and xargs is passed `/tmp/school work`, this is interpreted as both `/tmp/school` and `work`. Have fun recovering `~/work`! -- `rm` might halt if it finds out it's a directory, though. – Xunie May 06 '18 at 15:38
  • 4
    `rm` _will_ not delete it if it's a directory. And `rmdir` _will_ only delete if it's an empty directory. (Unless you made some alias to those commands of course). So my comment still stands. The only thing it could delete is one single file that matches that exact name in between space characters. – qwertzguy May 06 '18 at 20:53
  • 6
    AFAIK using `xargs -I "{}" -- rm -- '{}'` should solve the whitespace problem – JepZ Feb 15 '19 at 13:09
  • 1
    this seems to work also on ubuntu 18 `cat install_manifest.txt | xargs -L1 dirname | sudo xargs rm -rv` – briancollins081 Jan 22 '21 at 06:52
10

From the source folder:

  1. open install_manifest.txt (created by make install)

  2. remove all the directories/files listed

  3. remove any remaining files you missed:

    xargs rm < install_manifest.txt

  4. remove any hidden directories/files:

    $rm -rf ~/.packagename

Remove the source folder.

V Li
  • 169
  • 2
  • 7
  • Dangerous and redundant. Step 1 and 2 are of course already covered with step 3, and step 4 will remove configuration for _all_ installed versions, including custom configuration that one might want to use after installing the tool again. – pipe Aug 20 '23 at 13:36
8
# make uninstall
add_custom_target("uninstall" COMMENT "Uninstall installed files")
add_custom_command(
    TARGET "uninstall"
    POST_BUILD
    COMMENT "Uninstall files with install_manifest.txt"
    COMMAND xargs rm -vf < install_manifest.txt || echo Nothing in
            install_manifest.txt to be uninstalled!
)

Add this to CMakeLists.txt, then an uninstall target is made by hand.

Yan QiDong
  • 3,696
  • 1
  • 24
  • 25
4

One solution is to use packaging with CPack. In Linux, that will create a package that can be installed/uninstalled by your package manager. In Windows with the NSIS generator, you'll get an installer which also deploys uninstall.exe to your program files.

Here's a basic example of creating a debian package:

$ touch file
$ cat CMakeLists.txt
cmake_minimum_required(VERSION 3.0)

install(FILES file DESTINATION etc)

set(CPACK_PACKAGE_NAME foo)
set(CPACK_PACKAGE_CONTACT "me <me@example.com>")
set(CPACK_GENERATOR DEB)
include(CPack)
$ cmake .
$ cpack

Then instead of make install DESTDIR=/usr/local use sudo dpkg -i foo-0.1.1-Linux.deb.

To uninstall use sudo dpkg -P foo or sudo apt purge foo.

The advantage of using a package manager over make install are numerous. Here are a few:

  • If you lose the source code, you can still uninstall the software.
  • If you dpkg -S /etc/foo, it will tell you which package "owns" this file.
  • If you want to install a new version of the software, you won't need to manually uninstall the previous version. It's all automatic.
  • You can publish the package so others can install it.
  • If your package deploys a file that is also owned by another package, it will fail to install. That's good because it prevents you from accidentally destroying other packages.
  • You have the ability to add scripts to the installation. Instead of simply copying files, you can add system users, enable serves, or perform compatibility operations on old databases during upgrade.
Stewart
  • 4,356
  • 2
  • 27
  • 59