Add support for build tree symlink inside source tree

Since commit c564a3e3ff (Ninja: Always compile sources using absolute
paths, 2021-05-19, v3.21.0-rc1~129^2), both the Ninja and Makefile
generators pass source files and include directories to the compiler as
absolute paths.  However, in some other contexts within generated build
systems, we generate paths that may be relative or absolute.  In these
contexts, we prefer relative paths, but avoid them when they contain a
`../` sequence that leaves both the build tree and the source tree:

* When the build tree is outside of the source tree, all paths to the
  source tree are absolute.

* When the build tree is inside the source tree, we previously assumed
  that it is a real directory such that exiting the build tree with
  `../` enters the source tree.  This allowed paths to the source
  tree to be relative to the build tree.

In the latter case, we previously did not support using a symbolic link
inside the source tree to point at the build tree.  This is because
relative paths to the source tree would be generated with `../`
sequences leaving the build tree, but they would jump to the parent of
the real build tree, which is not the source tree.

Fix this by requiring that `../` sequences stay inside the build tree
even if its path appears to be inside the source tree.  When the build
tree is inside the source tree, all paths to the source tree are now
absolute.  For consistency, this applies regardless of whether the
path to the build tree contains a symbolic link.

Fixes: #21819
This commit is contained in:
Brad King 2022-02-25 08:35:46 -05:00
parent 43416c48ed
commit d33b12d84b
3 changed files with 51 additions and 10 deletions

View File

@ -34,6 +34,7 @@ cmOutputConverter::cmOutputConverter(cmStateSnapshot const& snapshot)
assert(this->StateSnapshot.IsValid());
this->ComputeRelativePathTopSource();
this->ComputeRelativePathTopBinary();
this->ComputeRelativePathTopRelation();
}
void cmOutputConverter::ComputeRelativePathTopSource()
@ -69,6 +70,22 @@ void cmOutputConverter::ComputeRelativePathTopBinary()
this->RelativePathTopBinary = snapshot.GetDirectory().GetCurrentBinary();
}
void cmOutputConverter::ComputeRelativePathTopRelation()
{
if (cmSystemTools::ComparePath(this->RelativePathTopSource,
this->RelativePathTopBinary)) {
this->RelativePathTopRelation = TopRelation::InSource;
} else if (cmSystemTools::IsSubDirectory(this->RelativePathTopBinary,
this->RelativePathTopSource)) {
this->RelativePathTopRelation = TopRelation::BinInSrc;
} else if (cmSystemTools::IsSubDirectory(this->RelativePathTopSource,
this->RelativePathTopBinary)) {
this->RelativePathTopRelation = TopRelation::SrcInBin;
} else {
this->RelativePathTopRelation = TopRelation::Separate;
}
}
std::string const& cmOutputConverter::GetRelativePathTopSource() const
{
return this->RelativePathTopSource;
@ -84,19 +101,40 @@ void cmOutputConverter::SetRelativePathTop(std::string const& topSource,
{
this->RelativePathTopSource = topSource;
this->RelativePathTopBinary = topBinary;
this->ComputeRelativePathTopRelation();
}
std::string cmOutputConverter::MaybeRelativeTo(
std::string const& local_path, std::string const& remote_path) const
{
bool bothInBinary =
PathEqOrSubDir(local_path, this->RelativePathTopBinary) &&
bool localInBinary = PathEqOrSubDir(local_path, this->RelativePathTopBinary);
bool remoteInBinary =
PathEqOrSubDir(remote_path, this->RelativePathTopBinary);
bool bothInSource =
PathEqOrSubDir(local_path, this->RelativePathTopSource) &&
bool localInSource = PathEqOrSubDir(local_path, this->RelativePathTopSource);
bool remoteInSource =
PathEqOrSubDir(remote_path, this->RelativePathTopSource);
switch (this->RelativePathTopRelation) {
case TopRelation::Separate:
// Checks are independent.
break;
case TopRelation::BinInSrc:
localInSource = localInSource && !localInBinary;
remoteInSource = remoteInSource && !remoteInBinary;
break;
case TopRelation::SrcInBin:
localInBinary = localInBinary && !localInSource;
remoteInBinary = remoteInBinary && !remoteInSource;
break;
case TopRelation::InSource:
// Checks are identical.
break;
};
bool const bothInBinary = localInBinary && remoteInBinary;
bool const bothInSource = localInSource && remoteInSource;
if (bothInBinary || bothInSource) {
return cmSystemTools::ForceToRelativePath(local_path, remote_path);
}

View File

@ -147,8 +147,17 @@ private:
// safely by the build tools.
std::string RelativePathTopSource;
std::string RelativePathTopBinary;
enum class TopRelation
{
Separate,
BinInSrc,
SrcInBin,
InSource,
};
TopRelation RelativePathTopRelation = TopRelation::Separate;
void ComputeRelativePathTopSource();
void ComputeRelativePathTopBinary();
void ComputeRelativePathTopRelation();
std::string MaybeRelativeTo(std::string const& local_path,
std::string const& remote_path) const;
};

View File

@ -76,12 +76,6 @@ function (run_symlink_test case src bin src_from_bin bin_from_src)
run_symlink_test_case("${case}" -S "../${name}/${src}" -B "../${name}/${bin}")
# Verify paths passed to compiler.
if(case MATCHES "^(different|asymmetric)-bin_in_src$")
# FIXME: Some generators compute incorrect relative paths.
message(STATUS "${case}-exe - SKIPPED")
message(STATUS "${case}-exe-build - SKIPPED")
return()
endif()
unset(RunCMake_TEST_VARIANT_DESCRIPTION)
run_symlink_test_case("${case}-exe" -S "${src}" -B "${bin}")
if (RunCMake_GENERATOR MATCHES "Xcode")