file(INSTALL): Add FOLLOW_SYMLINK_CHAIN argument
This commit is contained in:
parent
64a7f491ef
commit
e3ff7ced63
@ -311,6 +311,7 @@ Create the given directories and their parents as needed.
|
||||
[FILE_PERMISSIONS <permissions>...]
|
||||
[DIRECTORY_PERMISSIONS <permissions>...]
|
||||
[NO_SOURCE_PERMISSIONS] [USE_SOURCE_PERMISSIONS]
|
||||
[FOLLOW_SYMLINK_CHAIN]
|
||||
[FILES_MATCHING]
|
||||
[[PATTERN <pattern> | REGEX <regex>]
|
||||
[EXCLUDE] [PERMISSIONS <permissions>...]] [...])
|
||||
@ -324,6 +325,32 @@ at the destination with the same timestamp. Copying preserves input
|
||||
permissions unless explicit permissions or ``NO_SOURCE_PERMISSIONS``
|
||||
are given (default is ``USE_SOURCE_PERMISSIONS``).
|
||||
|
||||
If ``FOLLOW_SYMLINK_CHAIN`` is specified, ``COPY`` will recursively resolve
|
||||
the symlinks at the paths given until a real file is found, and install
|
||||
a corresponding symlink in the destination for each symlink encountered. For
|
||||
each symlink that is installed, the resolution is stripped of the directory,
|
||||
leaving only the filename, meaning that the new symlink points to a file in
|
||||
the same directory as the symlink. This feature is useful on some Unix systems,
|
||||
where libraries are installed as a chain of symlinks with version numbers, with
|
||||
less specific versions pointing to more specific versions.
|
||||
``FOLLOW_SYMLINK_CHAIN`` will install all of these symlinks and the library
|
||||
itself into the destination directory. For example, if you have the following
|
||||
directory structure:
|
||||
|
||||
* ``/opt/foo/lib/libfoo.so.1.2.3``
|
||||
* ``/opt/foo/lib/libfoo.so.1.2 -> libfoo.so.1.2.3``
|
||||
* ``/opt/foo/lib/libfoo.so.1 -> libfoo.so.1.2``
|
||||
* ``/opt/foo/lib/libfoo.so -> libfoo.so.1``
|
||||
|
||||
and you do:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
file(COPY /opt/foo/lib/libfoo.so DESTINATION lib FOLLOW_SYMLINK_CHAIN)
|
||||
|
||||
This will install all of the symlinks and ``libfoo.so.1.2.3`` itself into
|
||||
``lib``.
|
||||
|
||||
See the :command:`install(DIRECTORY)` command for documentation of
|
||||
permissions, ``FILES_MATCHING``, ``PATTERN``, ``REGEX``, and
|
||||
``EXCLUDE`` options. Copying directories preserves the structure
|
||||
|
6
Help/release/dev/file-install-follow-symlink-chain.rst
Normal file
6
Help/release/dev/file-install-follow-symlink-chain.rst
Normal file
@ -0,0 +1,6 @@
|
||||
file-install-follow-symlink-chain
|
||||
---------------------------------
|
||||
|
||||
* The :command:`file(INSTALL)` command learned a new argument,
|
||||
``FOLLOW_SYMLINK_CHAIN``, which can be used to recursively resolve and
|
||||
install symlinks.
|
@ -31,6 +31,7 @@ cmFileCopier::cmFileCopier(cmFileCommand* command, const char* name)
|
||||
, UseGivenPermissionsFile(false)
|
||||
, UseGivenPermissionsDir(false)
|
||||
, UseSourcePermissions(true)
|
||||
, FollowSymlinkChain(false)
|
||||
, Doing(DoingNone)
|
||||
{
|
||||
}
|
||||
@ -249,6 +250,9 @@ bool cmFileCopier::CheckKeyword(std::string const& arg)
|
||||
this->Doing = DoingPattern;
|
||||
} else if (arg == "REGEX") {
|
||||
this->Doing = DoingRegex;
|
||||
} else if (arg == "FOLLOW_SYMLINK_CHAIN") {
|
||||
this->FollowSymlinkChain = true;
|
||||
this->Doing = DoingNone;
|
||||
} else if (arg == "EXCLUDE") {
|
||||
// Add this property to the current match rule.
|
||||
if (this->CurrentMatchRule) {
|
||||
@ -464,16 +468,69 @@ bool cmFileCopier::Install(const std::string& fromFile,
|
||||
if (cmSystemTools::SameFile(fromFile, toFile)) {
|
||||
return true;
|
||||
}
|
||||
if (cmSystemTools::FileIsSymlink(fromFile)) {
|
||||
return this->InstallSymlink(fromFile, toFile);
|
||||
|
||||
std::string newFromFile = fromFile;
|
||||
std::string newToFile = toFile;
|
||||
|
||||
if (this->FollowSymlinkChain &&
|
||||
!this->InstallSymlinkChain(newFromFile, newToFile)) {
|
||||
return false;
|
||||
}
|
||||
if (cmSystemTools::FileIsDirectory(fromFile)) {
|
||||
return this->InstallDirectory(fromFile, toFile, match_properties);
|
||||
|
||||
if (cmSystemTools::FileIsSymlink(newFromFile)) {
|
||||
return this->InstallSymlink(newFromFile, newToFile);
|
||||
}
|
||||
if (cmSystemTools::FileExists(fromFile)) {
|
||||
return this->InstallFile(fromFile, toFile, match_properties);
|
||||
if (cmSystemTools::FileIsDirectory(newFromFile)) {
|
||||
return this->InstallDirectory(newFromFile, newToFile, match_properties);
|
||||
}
|
||||
return this->ReportMissing(fromFile);
|
||||
if (cmSystemTools::FileExists(newFromFile)) {
|
||||
return this->InstallFile(newFromFile, newToFile, match_properties);
|
||||
}
|
||||
return this->ReportMissing(newFromFile);
|
||||
}
|
||||
|
||||
bool cmFileCopier::InstallSymlinkChain(std::string& fromFile,
|
||||
std::string& toFile)
|
||||
{
|
||||
std::string newFromFile;
|
||||
std::string toFilePath = cmSystemTools::GetFilenamePath(toFile);
|
||||
while (cmSystemTools::ReadSymlink(fromFile, newFromFile)) {
|
||||
if (!cmSystemTools::FileIsFullPath(newFromFile)) {
|
||||
std::string fromFilePath = cmSystemTools::GetFilenamePath(fromFile);
|
||||
newFromFile = fromFilePath + "/" + newFromFile;
|
||||
}
|
||||
|
||||
std::string symlinkTarget = cmSystemTools::GetFilenameName(newFromFile);
|
||||
|
||||
bool copy = true;
|
||||
if (!this->Always) {
|
||||
std::string oldSymlinkTarget;
|
||||
if (cmSystemTools::ReadSymlink(toFile, oldSymlinkTarget)) {
|
||||
if (symlinkTarget == oldSymlinkTarget) {
|
||||
copy = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->ReportCopy(toFile, TypeLink, copy);
|
||||
|
||||
if (copy) {
|
||||
cmSystemTools::RemoveFile(toFile);
|
||||
cmSystemTools::MakeDirectory(toFilePath);
|
||||
|
||||
if (!cmSystemTools::CreateSymlink(symlinkTarget, toFile)) {
|
||||
std::ostringstream e;
|
||||
e << this->Name << " cannot create symlink \"" << toFile << "\".";
|
||||
this->FileCommand->SetError(e.str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fromFile = newFromFile;
|
||||
toFile = toFilePath + "/" + symlinkTarget;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cmFileCopier::InstallSymlink(const std::string& fromFile,
|
||||
|
@ -64,6 +64,7 @@ protected:
|
||||
// Translate an argument to a permissions bit.
|
||||
bool CheckPermissions(std::string const& arg, mode_t& permissions);
|
||||
|
||||
bool InstallSymlinkChain(std::string& fromFile, std::string& toFile);
|
||||
bool InstallSymlink(const std::string& fromFile, const std::string& toFile);
|
||||
bool InstallFile(const std::string& fromFile, const std::string& toFile,
|
||||
MatchProperties match_properties);
|
||||
@ -86,6 +87,7 @@ protected:
|
||||
bool UseGivenPermissionsFile;
|
||||
bool UseGivenPermissionsDir;
|
||||
bool UseSourcePermissions;
|
||||
bool FollowSymlinkChain;
|
||||
std::string Destination;
|
||||
std::string FilesFromDir;
|
||||
std::vector<std::string> Files;
|
||||
|
168
Tests/RunCMake/file/INSTALL-FOLLOW_SYMLINK_CHAIN.cmake
Normal file
168
Tests/RunCMake/file/INSTALL-FOLLOW_SYMLINK_CHAIN.cmake
Normal file
@ -0,0 +1,168 @@
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/dest1")
|
||||
|
||||
file(TOUCH "${CMAKE_BINARY_DIR}/file1.txt")
|
||||
file(CREATE_LINK file1.txt "${CMAKE_BINARY_DIR}/file1.txt.sym" SYMBOLIC)
|
||||
file(TOUCH "${CMAKE_BINARY_DIR}/dest1/file1.txt.sym")
|
||||
|
||||
file(TOUCH "${CMAKE_BINARY_DIR}/file2.txt")
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/file2")
|
||||
file(CREATE_LINK ../file2.txt "${CMAKE_BINARY_DIR}/file2/file2.txt.sym" SYMBOLIC)
|
||||
|
||||
file(TOUCH "${CMAKE_BINARY_DIR}/file3.txt")
|
||||
file(CREATE_LINK "${CMAKE_BINARY_DIR}/file3.txt" "${CMAKE_BINARY_DIR}/file3.txt.sym" SYMBOLIC)
|
||||
|
||||
file(TOUCH "${CMAKE_BINARY_DIR}/file4.txt")
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/file4")
|
||||
file(CREATE_LINK "${CMAKE_BINARY_DIR}/file4.txt" "${CMAKE_BINARY_DIR}/file4/file4.txt.sym" SYMBOLIC)
|
||||
|
||||
file(TOUCH "${CMAKE_BINARY_DIR}/file5.txt")
|
||||
|
||||
file(TOUCH "${CMAKE_BINARY_DIR}/file6.txt")
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/file6/file6")
|
||||
file(CREATE_LINK file6.txt "${CMAKE_BINARY_DIR}/file6.txt.sym.1" SYMBOLIC)
|
||||
file(CREATE_LINK ../file6.txt.sym.1 "${CMAKE_BINARY_DIR}/file6/file6.txt.sym.2" SYMBOLIC)
|
||||
file(CREATE_LINK "${CMAKE_BINARY_DIR}/file6/file6.txt.sym.2" "${CMAKE_BINARY_DIR}/file6/file6/file6.txt.sym.3" SYMBOLIC)
|
||||
file(CREATE_LINK file6.txt.sym.3 "${CMAKE_BINARY_DIR}/file6/file6/file6.txt.sym.4" SYMBOLIC)
|
||||
|
||||
file(TOUCH "${CMAKE_BINARY_DIR}/file7.txt")
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/file7")
|
||||
|
||||
file(TOUCH "${CMAKE_BINARY_DIR}/file8.txt")
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/file8")
|
||||
file(CREATE_LINK "${CMAKE_BINARY_DIR}/file8/../file8.txt" "${CMAKE_BINARY_DIR}/file8/file8.txt.sym" SYMBOLIC)
|
||||
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/file9")
|
||||
file(TOUCH "${CMAKE_BINARY_DIR}/file9/file9.txt")
|
||||
file(CREATE_LINK "${CMAKE_BINARY_DIR}/file9" "${CMAKE_BINARY_DIR}/file9.sym" SYMBOLIC)
|
||||
|
||||
file(TOUCH "${CMAKE_BINARY_DIR}/file10.txt")
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/file10")
|
||||
file(CREATE_LINK "." "${CMAKE_BINARY_DIR}/file10/file10" SYMBOLIC)
|
||||
file(CREATE_LINK "${CMAKE_BINARY_DIR}/file10/file10/../file10.txt" "${CMAKE_BINARY_DIR}/file10/file10.txt.sym" SYMBOLIC)
|
||||
|
||||
file(INSTALL
|
||||
"${CMAKE_BINARY_DIR}/file1.txt.sym"
|
||||
DESTINATION "${CMAKE_BINARY_DIR}/dest1"
|
||||
FOLLOW_SYMLINK_CHAIN
|
||||
)
|
||||
|
||||
file(INSTALL
|
||||
"${CMAKE_BINARY_DIR}/file1.txt.sym"
|
||||
"${CMAKE_BINARY_DIR}/file2/file2.txt.sym"
|
||||
"${CMAKE_BINARY_DIR}/file3.txt.sym"
|
||||
"${CMAKE_BINARY_DIR}/file4/file4.txt.sym"
|
||||
"${CMAKE_BINARY_DIR}/file5.txt"
|
||||
"${CMAKE_BINARY_DIR}/file6/file6/file6.txt.sym.4"
|
||||
"${CMAKE_BINARY_DIR}/file8/file8.txt.sym"
|
||||
"${CMAKE_BINARY_DIR}/file7/../file7.txt"
|
||||
"${CMAKE_BINARY_DIR}/file8.txt"
|
||||
"${CMAKE_BINARY_DIR}/file9.sym/file9.txt"
|
||||
"${CMAKE_BINARY_DIR}/file10/file10/file10.txt.sym"
|
||||
DESTINATION "${CMAKE_BINARY_DIR}/dest2"
|
||||
FOLLOW_SYMLINK_CHAIN
|
||||
)
|
||||
|
||||
set(resolved_file1.txt.sym file1.txt)
|
||||
set(resolved_file10.txt.sym file10.txt)
|
||||
set(resolved_file2.txt.sym file2.txt)
|
||||
set(resolved_file3.txt.sym file3.txt)
|
||||
set(resolved_file4.txt.sym file4.txt)
|
||||
set(resolved_file6.txt.sym.1 file6.txt)
|
||||
set(resolved_file6.txt.sym.2 file6.txt.sym.1)
|
||||
set(resolved_file6.txt.sym.3 file6.txt.sym.2)
|
||||
set(resolved_file6.txt.sym.4 file6.txt.sym.3)
|
||||
set(resolved_file8.txt.sym file8.txt)
|
||||
set(syms)
|
||||
foreach(f
|
||||
file1.txt
|
||||
file1.txt.sym
|
||||
file10.txt
|
||||
file10.txt.sym
|
||||
file2.txt
|
||||
file2.txt.sym
|
||||
file3.txt
|
||||
file3.txt.sym
|
||||
file4.txt
|
||||
file4.txt.sym
|
||||
file5.txt
|
||||
file6.txt
|
||||
file6.txt.sym.1
|
||||
file6.txt.sym.2
|
||||
file6.txt.sym.3
|
||||
file6.txt.sym.4
|
||||
file7.txt
|
||||
file8.txt
|
||||
file8.txt.sym
|
||||
file9.txt
|
||||
)
|
||||
string(REPLACE "." "\\." r "${f}")
|
||||
list(APPEND syms "[^;]*/Tests/RunCMake/file/INSTALL-FOLLOW_SYMLINK_CHAIN-build/dest2/${r}")
|
||||
set(filename "${CMAKE_BINARY_DIR}/dest2/${f}")
|
||||
if(DEFINED resolved_${f})
|
||||
file(READ_SYMLINK "${filename}" resolved)
|
||||
if(NOT resolved STREQUAL "${resolved_${f}}")
|
||||
message(SEND_ERROR "Expected symlink resolution for ${f}: ${resolved_${f}}\nActual resolution: ${resolved}")
|
||||
endif()
|
||||
elseif(NOT EXISTS "${filename}" OR IS_SYMLINK "${filename}" OR IS_DIRECTORY "${filename}")
|
||||
message(SEND_ERROR "${f} should be a regular file")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
file(GLOB_RECURSE actual_syms LIST_DIRECTORIES true "${CMAKE_BINARY_DIR}/dest2/*")
|
||||
if(NOT actual_syms MATCHES "^${syms}$")
|
||||
message(SEND_ERROR "Expected files:\n\n ^${syms}$\n\nActual files:\n\n ${actual_syms}")
|
||||
endif()
|
||||
|
||||
file(INSTALL
|
||||
"${CMAKE_BINARY_DIR}/file1.txt.sym"
|
||||
"${CMAKE_BINARY_DIR}/file2/file2.txt.sym"
|
||||
"${CMAKE_BINARY_DIR}/file3.txt.sym"
|
||||
"${CMAKE_BINARY_DIR}/file4/file4.txt.sym"
|
||||
"${CMAKE_BINARY_DIR}/file5.txt"
|
||||
"${CMAKE_BINARY_DIR}/file6/file6/file6.txt.sym.4"
|
||||
"${CMAKE_BINARY_DIR}/file8/file8.txt.sym"
|
||||
"${CMAKE_BINARY_DIR}/file7/../file7.txt"
|
||||
"${CMAKE_BINARY_DIR}/file8.txt"
|
||||
"${CMAKE_BINARY_DIR}/file9.sym/file9.txt"
|
||||
"${CMAKE_BINARY_DIR}/file10/file10/file10.txt.sym"
|
||||
DESTINATION "${CMAKE_BINARY_DIR}/dest3"
|
||||
)
|
||||
|
||||
set(resolved_file1.txt.sym [[^file1\.txt$]])
|
||||
set(resolved_file10.txt.sym [[/Tests/RunCMake/file/INSTALL-FOLLOW_SYMLINK_CHAIN-build/file10/file10/\.\./file10\.txt$]])
|
||||
set(resolved_file2.txt.sym [[^\.\./file2\.txt$]])
|
||||
set(resolved_file3.txt.sym [[/Tests/RunCMake/file/INSTALL-FOLLOW_SYMLINK_CHAIN-build/file3\.txt$]])
|
||||
set(resolved_file4.txt.sym [[/Tests/RunCMake/file/INSTALL-FOLLOW_SYMLINK_CHAIN-build/file4\.txt$]])
|
||||
set(resolved_file6.txt.sym.4 [[^file6\.txt\.sym\.3$]])
|
||||
set(resolved_file8.txt.sym [[/Tests/RunCMake/file/INSTALL-FOLLOW_SYMLINK_CHAIN-build/file8/\.\./file8\.txt$]])
|
||||
set(syms)
|
||||
foreach(f
|
||||
file1.txt.sym
|
||||
file10.txt.sym
|
||||
file2.txt.sym
|
||||
file3.txt.sym
|
||||
file4.txt.sym
|
||||
file5.txt
|
||||
file6.txt.sym.4
|
||||
file7.txt
|
||||
file8.txt
|
||||
file8.txt.sym
|
||||
file9.txt
|
||||
)
|
||||
string(REPLACE "." "\\." r "${f}")
|
||||
list(APPEND syms "[^;]*/Tests/RunCMake/file/INSTALL-FOLLOW_SYMLINK_CHAIN-build/dest3/${r}")
|
||||
set(filename "${CMAKE_BINARY_DIR}/dest3/${f}")
|
||||
if(DEFINED resolved_${f})
|
||||
file(READ_SYMLINK "${filename}" resolved)
|
||||
if(NOT resolved MATCHES "${resolved_${f}}")
|
||||
message(SEND_ERROR "Expected symlink resolution for ${f}: ${resolved_${f}}\nActual resolution: ${resolved}")
|
||||
endif()
|
||||
elseif(NOT EXISTS "${filename}" OR IS_SYMLINK "${filename}" OR IS_DIRECTORY "${filename}")
|
||||
message(SEND_ERROR "${f} should be a regular file")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
file(GLOB_RECURSE actual_syms LIST_DIRECTORIES true "${CMAKE_BINARY_DIR}/dest3/*")
|
||||
if(NOT actual_syms MATCHES "^${syms}$")
|
||||
message(SEND_ERROR "Expected files:\n\n ^${syms}$\n\nActual files:\n\n ${actual_syms}")
|
||||
endif()
|
@ -64,6 +64,7 @@ if(NOT WIN32 OR CYGWIN)
|
||||
run_cmake(READ_SYMLINK)
|
||||
run_cmake(READ_SYMLINK-noexist)
|
||||
run_cmake(READ_SYMLINK-notsymlink)
|
||||
run_cmake(INSTALL-FOLLOW_SYMLINK_CHAIN)
|
||||
endif()
|
||||
|
||||
if(RunCMake_GENERATOR STREQUAL "Ninja")
|
||||
|
Loading…
Reference in New Issue
Block a user