CMake/Source/cmSourceFileLocation.cxx
Tim Blechmann 22f38c0d6b cmake: avoid getcwd in CollapseFullPath
`CollapseFullPath` calls getcwd, which is a rather expensive system
call. we can replace it with `GetHomeOutputDirectory()` to save us from
the calling overhead
2020-01-14 18:20:39 +08:00

235 lines
7.7 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmSourceFileLocation.h"
#include <cassert>
#include "cmGlobalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmake.h"
cmSourceFileLocation::cmSourceFileLocation() = default;
cmSourceFileLocation::cmSourceFileLocation(const cmSourceFileLocation& loc)
: Makefile(loc.Makefile)
{
this->AmbiguousDirectory = loc.AmbiguousDirectory;
this->AmbiguousExtension = loc.AmbiguousExtension;
this->Directory = loc.Directory;
this->Name = loc.Name;
}
cmSourceFileLocation::cmSourceFileLocation(cmMakefile const* mf,
const std::string& name,
cmSourceFileLocationKind kind)
: Makefile(mf)
{
this->AmbiguousDirectory = !cmSystemTools::FileIsFullPath(name);
this->AmbiguousExtension = true;
this->Directory = cmSystemTools::GetFilenamePath(name);
if (cmSystemTools::FileIsFullPath(this->Directory)) {
this->Directory = cmSystemTools::CollapseFullPath(
this->Directory, mf->GetHomeOutputDirectory());
}
this->Name = cmSystemTools::GetFilenameName(name);
if (kind == cmSourceFileLocationKind::Known) {
this->DirectoryUseSource();
this->AmbiguousExtension = false;
} else {
this->UpdateExtension(name);
}
}
std::string cmSourceFileLocation::GetFullPath() const
{
std::string path = this->GetDirectory();
if (!path.empty()) {
path += '/';
}
path += this->GetName();
return path;
}
void cmSourceFileLocation::Update(cmSourceFileLocation const& loc)
{
if (this->AmbiguousDirectory && !loc.AmbiguousDirectory) {
this->Directory = loc.Directory;
this->AmbiguousDirectory = false;
}
if (this->AmbiguousExtension && !loc.AmbiguousExtension) {
this->Name = loc.Name;
this->AmbiguousExtension = false;
}
}
void cmSourceFileLocation::DirectoryUseSource()
{
assert(this->Makefile);
if (this->AmbiguousDirectory) {
this->Directory = cmSystemTools::CollapseFullPath(
this->Directory, this->Makefile->GetCurrentSourceDirectory());
this->AmbiguousDirectory = false;
}
}
void cmSourceFileLocation::DirectoryUseBinary()
{
assert(this->Makefile);
if (this->AmbiguousDirectory) {
this->Directory = cmSystemTools::CollapseFullPath(
this->Directory, this->Makefile->GetCurrentBinaryDirectory());
this->AmbiguousDirectory = false;
}
}
void cmSourceFileLocation::UpdateExtension(const std::string& name)
{
assert(this->Makefile);
// Check the extension.
std::string ext = cmSystemTools::GetFilenameLastExtension(name);
if (!ext.empty()) {
ext = ext.substr(1);
}
// The global generator checks extensions of enabled languages.
cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
cmMakefile const* mf = this->Makefile;
auto cm = mf->GetCMakeInstance();
if (!gg->GetLanguageFromExtension(ext.c_str()).empty() ||
cm->IsSourceExtension(ext) || cm->IsHeaderExtension(ext)) {
// This is a known extension. Use the given filename with extension.
this->Name = cmSystemTools::GetFilenameName(name);
this->AmbiguousExtension = false;
} else {
// This is not a known extension. See if the file exists on disk as
// named.
std::string tryPath;
if (this->AmbiguousDirectory) {
// Check the source tree only because a file in the build tree should
// be specified by full path at least once. We do not want this
// detection to depend on whether the project has already been built.
tryPath = cmStrCat(this->Makefile->GetCurrentSourceDirectory(), '/');
}
if (!this->Directory.empty()) {
tryPath += this->Directory;
tryPath += "/";
}
tryPath += this->Name;
if (cmSystemTools::FileExists(tryPath, true)) {
// We found a source file named by the user on disk. Trust it's
// extension.
this->Name = cmSystemTools::GetFilenameName(name);
this->AmbiguousExtension = false;
// If the directory was ambiguous, it isn't anymore.
if (this->AmbiguousDirectory) {
this->DirectoryUseSource();
}
}
}
}
bool cmSourceFileLocation::MatchesAmbiguousExtension(
cmSourceFileLocation const& loc) const
{
assert(this->Makefile);
// This location's extension is not ambiguous but loc's extension
// is. See if the names match as-is.
if (this->Name == loc.Name) {
return true;
}
// Check if loc's name could possibly be extended to our name by
// adding an extension.
if (!(this->Name.size() > loc.Name.size() &&
this->Name[loc.Name.size()] == '.' &&
cmHasPrefix(this->Name, loc.Name))) {
return false;
}
// Only a fixed set of extensions will be tried to match a file on
// disk. One of these must match if loc refers to this source file.
std::string const& ext = this->Name.substr(loc.Name.size() + 1);
cmMakefile const* mf = this->Makefile;
auto cm = mf->GetCMakeInstance();
return cm->IsSourceExtension(ext) || cm->IsHeaderExtension(ext);
}
bool cmSourceFileLocation::Matches(cmSourceFileLocation const& loc)
{
assert(this->Makefile);
if (this->AmbiguousExtension == loc.AmbiguousExtension) {
// Both extensions are similarly ambiguous. Since only the old fixed set
// of extensions will be tried, the names must match at this point to be
// the same file.
if (this->Name.size() != loc.Name.size() ||
!cmSystemTools::ComparePath(this->Name, loc.Name)) {
return false;
}
} else {
const cmSourceFileLocation* loc1;
const cmSourceFileLocation* loc2;
if (this->AmbiguousExtension) {
// Only "this" extension is ambiguous.
loc1 = &loc;
loc2 = this;
} else {
// Only "loc" extension is ambiguous.
loc1 = this;
loc2 = &loc;
}
if (!loc1->MatchesAmbiguousExtension(*loc2)) {
return false;
}
}
if (!this->AmbiguousDirectory && !loc.AmbiguousDirectory) {
// Both sides have absolute directories.
if (this->Directory != loc.Directory) {
return false;
}
} else if (this->AmbiguousDirectory && loc.AmbiguousDirectory) {
if (this->Makefile == loc.Makefile) {
// Both sides have directories relative to the same location.
if (this->Directory != loc.Directory) {
return false;
}
} else {
// Each side has a directory relative to a different location.
// This can occur when referencing a source file from a different
// directory. This is not yet allowed.
this->Makefile->IssueMessage(
MessageType::INTERNAL_ERROR,
"Matches error: Each side has a directory relative to a different "
"location. This can occur when referencing a source file from a "
"different directory. This is not yet allowed.");
return false;
}
} else if (this->AmbiguousDirectory) {
// Compare possible directory combinations.
std::string const srcDir = cmSystemTools::CollapseFullPath(
this->Directory, this->Makefile->GetCurrentSourceDirectory());
std::string const binDir = cmSystemTools::CollapseFullPath(
this->Directory, this->Makefile->GetCurrentBinaryDirectory());
if (srcDir != loc.Directory && binDir != loc.Directory) {
return false;
}
} else if (loc.AmbiguousDirectory) {
// Compare possible directory combinations.
std::string const srcDir = cmSystemTools::CollapseFullPath(
loc.Directory, loc.Makefile->GetCurrentSourceDirectory());
std::string const binDir = cmSystemTools::CollapseFullPath(
loc.Directory, loc.Makefile->GetCurrentBinaryDirectory());
if (srcDir != this->Directory && binDir != this->Directory) {
return false;
}
}
// File locations match.
this->Update(loc);
return true;
}