Added uninstall routine to CMake. (0.17.4)
This commit is contained in:
parent
cfe145f08c
commit
4e5d104e48
2 changed files with 148 additions and 2 deletions
148
CMakeLists.txt
148
CMakeLists.txt
|
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
project(fun VERSION 0.17.3 LANGUAGES C)
|
project(fun VERSION 0.17.4 LANGUAGES C)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
|
@ -188,6 +188,152 @@ add_custom_target(distclean
|
||||||
COMMENT "Remove build outputs and CMake-generated files (reconfigure needed)"
|
COMMENT "Remove build outputs and CMake-generated files (reconfigure needed)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Robust uninstall support as a function (uses install_manifest*.txt)
|
||||||
|
function(fun_add_uninstall_target)
|
||||||
|
set(_FUN_UNINSTALL_SCRIPT "${CMAKE_BINARY_DIR}/cmake_uninstall.cmake")
|
||||||
|
|
||||||
|
# Generate the cmake_uninstall.cmake script at configure time
|
||||||
|
file(WRITE "${_FUN_UNINSTALL_SCRIPT}"
|
||||||
|
"set(_candidates \"\")
|
||||||
|
# Prefer explicit MANIFEST if provided
|
||||||
|
if(DEFINED MANIFEST AND NOT MANIFEST STREQUAL \"\")
|
||||||
|
list(APPEND _candidates \"\${MANIFEST}\")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Common locations (out-of-source and legacy in-source builds)
|
||||||
|
list(APPEND _candidates
|
||||||
|
\"\${CMAKE_BINARY_DIR}/install_manifest.txt\"
|
||||||
|
\"\${CMAKE_SOURCE_DIR}/install_manifest.txt\"
|
||||||
|
\"\${CMAKE_BINARY_DIR}/install_manifest_Debug.txt\"
|
||||||
|
\"\${CMAKE_BINARY_DIR}/install_manifest_Release.txt\"
|
||||||
|
\"\${CMAKE_BINARY_DIR}/install_manifest_RelWithDebInfo.txt\"
|
||||||
|
\"\${CMAKE_BINARY_DIR}/install_manifest_MinSizeRel.txt\"
|
||||||
|
\"\${CMAKE_SOURCE_DIR}/install_manifest_Debug.txt\"
|
||||||
|
\"\${CMAKE_SOURCE_DIR}/install_manifest_Release.txt\"
|
||||||
|
\"\${CMAKE_SOURCE_DIR}/install_manifest_RelWithDebInfo.txt\"
|
||||||
|
\"\${CMAKE_SOURCE_DIR}/install_manifest_MinSizeRel.txt\"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(_manifest_file \"\")
|
||||||
|
foreach(_cand IN LISTS _candidates)
|
||||||
|
if(EXISTS \"\${_cand}\")
|
||||||
|
set(_manifest_file \"\${_cand}\")
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(NOT _manifest_file)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
\"Cannot find install manifest. Tried: \${_candidates}.
|
||||||
|
If you installed from a different build directory, run uninstall from that build directory (where install_manifest*.txt resides).\")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(READ \"\${_manifest_file}\" _manifest)
|
||||||
|
string(REPLACE \"\\n\" \";\" _files \"\${_manifest}\")
|
||||||
|
set(_removed 0)
|
||||||
|
set(_dirs \"\")
|
||||||
|
foreach(_file IN LISTS _files)
|
||||||
|
if(_file STREQUAL \"\")
|
||||||
|
continue()
|
||||||
|
endif()
|
||||||
|
if(EXISTS \"\${_file}\" OR IS_SYMLINK \"\${_file}\")
|
||||||
|
message(STATUS \"Uninstalling: \${_file}\")
|
||||||
|
file(REMOVE \"\${_file}\")
|
||||||
|
# If the file still exists after attempting to remove it, fail with an explicit error
|
||||||
|
if(EXISTS \"\${_file}\" OR IS_SYMLINK \"\${_file}\")
|
||||||
|
message(FATAL_ERROR \"Failed to remove: \${_file}. Try running the uninstall target with sudo if it is a system install.\")
|
||||||
|
endif()
|
||||||
|
# Track the containing directory for potential cleanup
|
||||||
|
get_filename_component(_dir \"\${_file}\" DIRECTORY)
|
||||||
|
list(APPEND _dirs \"\${_dir}\")
|
||||||
|
math(EXPR _removed \"\${_removed}+1\")
|
||||||
|
else()
|
||||||
|
message(STATUS \"Skipping (not found): \${_file}\")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Remove empty directories that contained installed files (and their parents under safe roots)
|
||||||
|
# Safe roots we are allowed to prune if they become empty:
|
||||||
|
set(_safe_roots \"/usr/share/fun\" \"/usr/share/doc/fun\")
|
||||||
|
|
||||||
|
# Expand to include parent directories up to the safe roots
|
||||||
|
set(_all_dirs \"\")
|
||||||
|
foreach(_d IN LISTS _dirs)
|
||||||
|
set(_p \"\${_d}\")
|
||||||
|
while(NOT \"\${_p}\" STREQUAL \"\" AND NOT \"\${_p}\" STREQUAL \"/\")
|
||||||
|
list(APPEND _all_dirs \"\${_p}\")
|
||||||
|
get_filename_component(_p \"\${_p}\" DIRECTORY)
|
||||||
|
# Stop collecting parents once we reach a safe root or the filesystem root
|
||||||
|
list(FIND _safe_roots \"\${_p}\" _root_idx)
|
||||||
|
if(_root_idx GREATER -1)
|
||||||
|
list(APPEND _all_dirs \"\${_p}\")
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endwhile()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Deduplicate
|
||||||
|
list(REMOVE_DUPLICATES _all_dirs)
|
||||||
|
|
||||||
|
# Try multiple passes to remove parents that become empty after children are removed
|
||||||
|
set(_removed_dirs 0)
|
||||||
|
set(_pass 0)
|
||||||
|
while(TRUE)
|
||||||
|
set(_pass_removed 0)
|
||||||
|
foreach(_dir IN LISTS _all_dirs)
|
||||||
|
if(EXISTS \"\${_dir}\" AND IS_DIRECTORY \"\${_dir}\")
|
||||||
|
# Only act on directories within safe roots
|
||||||
|
set(_allowed FALSE)
|
||||||
|
foreach(_root IN LISTS _safe_roots)
|
||||||
|
if(\"\${_dir}\" MATCHES \"^\${_root}(/|\$)\")
|
||||||
|
set(_allowed TRUE)
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
if(NOT _allowed)
|
||||||
|
# e.g. /usr/bin — do not remove
|
||||||
|
continue()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Remove only if empty (do not recurse unrelated content)
|
||||||
|
file(GLOB _dir_contents LIST_DIRECTORIES true \"\${_dir}/*\")
|
||||||
|
list(LENGTH _dir_contents _dir_len)
|
||||||
|
if(_dir_len EQUAL 0)
|
||||||
|
message(STATUS \"Removing empty directory: \${_dir}\")
|
||||||
|
file(REMOVE_RECURSE \"\${_dir}\")
|
||||||
|
if(IS_DIRECTORY \"\${_dir}\")
|
||||||
|
message(FATAL_ERROR \"Failed to remove directory: \${_dir}. Try running the uninstall target with sudo if it is a system install.\")
|
||||||
|
endif()
|
||||||
|
math(EXPR _removed_dirs \"\${_removed_dirs}+1\")
|
||||||
|
math(EXPR _pass_removed \"\${_pass_removed}+1\")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
math(EXPR _pass \"\${_pass}+1\")
|
||||||
|
if(_pass_removed EQUAL 0 OR _pass GREATER 5)
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endwhile()
|
||||||
|
|
||||||
|
message(STATUS \"Uninstall finished. Removed \${_removed} files and \${_removed_dirs} directories (if empty) listed in \${_manifest_file}\")
|
||||||
|
")
|
||||||
|
|
||||||
|
# Expose an 'uninstall' build target (run with sudo if system locations were used)
|
||||||
|
add_custom_target(uninstall
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
-D MANIFEST="${CMAKE_BINARY_DIR}/install_manifest.txt"
|
||||||
|
-D CMAKE_BINARY_DIR="${CMAKE_BINARY_DIR}"
|
||||||
|
-D CMAKE_SOURCE_DIR="${CMAKE_SOURCE_DIR}"
|
||||||
|
-P "${_FUN_UNINSTALL_SCRIPT}"
|
||||||
|
USES_TERMINAL
|
||||||
|
COMMENT "Uninstall files installed by this project (use sudo if needed)"
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Define the uninstall target
|
||||||
|
fun_add_uninstall_target()
|
||||||
|
|
||||||
# Install rules
|
# Install rules
|
||||||
# Binary
|
# Binary
|
||||||
install(TARGETS fun
|
install(TARGETS fun
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ This requires Cygwin to be installed and configured. I will not cover this here.
|
||||||
- [GNU](https://gnu.org/)/[Linux](https://kernel.org/) ([Arch](https://archlinux.org/)/[Artix](https://artixlinux.org/), [Debian](https://www.debian.org/)) using [GCC](https://gcc.gnu.org/) and the [GNU C library](https://www.gnu.org/software/libc/) ([glibc](https://en.wikipedia.org/wiki/Glibc))
|
- [GNU](https://gnu.org/)/[Linux](https://kernel.org/) ([Arch](https://archlinux.org/)/[Artix](https://artixlinux.org/), [Debian](https://www.debian.org/)) using [GCC](https://gcc.gnu.org/) and the [GNU C library](https://www.gnu.org/software/libc/) ([glibc](https://en.wikipedia.org/wiki/Glibc))
|
||||||
- GNU/Linux ([Alpine](https://alpinelinux.org/)) using GCC and the [musl libc](https://musl.libc.org/)
|
- GNU/Linux ([Alpine](https://alpinelinux.org/)) using GCC and the [musl libc](https://musl.libc.org/)
|
||||||
- [FreeBSD](https://www.freebsd.org/) using [Clang](https://clang.llvm.org/) and the [BSD libc](https://en.wikipedia.org/wiki/C_standard_library#BSD_libc)
|
- [FreeBSD](https://www.freebsd.org/) using [Clang](https://clang.llvm.org/) and the [BSD libc](https://en.wikipedia.org/wiki/C_standard_library#BSD_libc)
|
||||||
- [Windows](https://en.wikipedia.org/wiki/Microsoft_Windows){:class="ext"} using [Cygwin](https://www.cygwin.com/){:class="ext"} and GCC.
|
- [Windows](https://en.wikipedia.org/wiki/Microsoft_Windows) using [Cygwin](https://www.cygwin.com/) and GCC.
|
||||||
|
|
||||||
### Other systems
|
### Other systems
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue