1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-05-09 14:57:08 +08:00
CMake/Source/cmDependsCompiler.cxx
Brad King 15fa320071 cmLocalGenerator: Factor out relative path conversion helpers
Most calls to `MaybeConvertToRelativePath` use one of our common work
directories (e.g. top of the build tree) as the local path.  Add helpers
for each of the common cases to simplify and clarify call sites.
2021-05-13 12:47:25 -04:00

251 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 "cmDependsCompiler.h"
#include <algorithm>
#include <map>
#include <memory>
#include <string>
#include <unordered_set>
#include <utility>
#include <cm/optional>
#include <cm/string_view>
#include <cm/vector>
#include <cmext/string_view>
#include "cmsys/FStream.hxx"
#include "cmFileTime.h"
#include "cmGccDepfileReader.h"
#include "cmGccDepfileReaderTypes.h"
#include "cmGlobalUnixMakefileGenerator3.h"
#include "cmLocalUnixMakefileGenerator3.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
bool cmDependsCompiler::CheckDependencies(
const std::string& internalDepFile, const std::vector<std::string>& depFiles,
cmDepends::DependencyMap& dependencies,
const std::function<bool(const std::string&)>& isValidPath)
{
bool status = true;
bool forceReadDeps = true;
cmFileTime internalDepFileTime;
// read cached dependencies stored in internal file
if (cmSystemTools::FileExists(internalDepFile)) {
internalDepFileTime.Load(internalDepFile);
forceReadDeps = false;
// read current dependencies
cmsys::ifstream fin(internalDepFile.c_str());
if (fin) {
std::string line;
std::string depender;
std::vector<std::string>* currentDependencies = nullptr;
while (std::getline(fin, line)) {
if (line.empty() || line.front() == '#') {
continue;
}
// Drop carriage return character at the end
if (line.back() == '\r') {
line.pop_back();
if (line.empty()) {
continue;
}
}
// Check if this a depender line
if (line.front() != ' ') {
depender = std::move(line);
currentDependencies = &dependencies[depender];
continue;
}
// This is a dependee line
if (currentDependencies != nullptr) {
currentDependencies->emplace_back(line.substr(1));
}
}
fin.close();
}
}
// Now, update dependencies map with all new compiler generated
// dependencies files
cmFileTime depFileTime;
for (auto dep = depFiles.begin(); dep != depFiles.end(); dep++) {
const auto& source = *dep++;
const auto& target = *dep++;
const auto& format = *dep++;
const auto& depFile = *dep;
if (!cmSystemTools::FileExists(depFile)) {
continue;
}
if (!forceReadDeps) {
depFileTime.Load(depFile);
}
if (forceReadDeps || depFileTime.Compare(internalDepFileTime) >= 0) {
status = false;
if (this->Verbose) {
cmSystemTools::Stdout(cmStrCat("Dependencies file \"", depFile,
"\" is newer than depends file \"",
internalDepFile, "\".\n"));
}
std::vector<std::string> depends;
if (format == "custom"_s) {
auto deps = cmReadGccDepfile(
depFile.c_str(), this->LocalGenerator->GetCurrentBinaryDirectory());
if (!deps) {
continue;
}
for (auto& entry : *deps) {
depends = std::move(entry.paths);
if (isValidPath) {
cm::erase_if(depends, isValidPath);
}
// copy depends for each target, except first one, which can be
// moved
for (auto index = entry.rules.size() - 1; index > 0; --index) {
dependencies[entry.rules[index]] = depends;
}
dependencies[entry.rules.front()] = std::move(depends);
}
} else {
if (format == "msvc"_s) {
cmsys::ifstream fin(depFile.c_str());
if (!fin) {
continue;
}
std::string line;
if (!isValidPath) {
// insert source as first dependency
depends.push_back(source);
}
while (cmSystemTools::GetLineFromStream(fin, line)) {
depends.emplace_back(std::move(line));
}
} else if (format == "gcc"_s) {
auto deps = cmReadGccDepfile(depFile.c_str());
if (!deps) {
continue;
}
// dependencies generated by the compiler contains only one target
depends = std::move(deps->front().paths);
if (depends.empty()) {
// unexpectedly empty, ignore it and continue
continue;
}
// depending of the effective format of the dependencies file
// generated by the compiler, the target can be wrongly identified
// as a dependency so remove it from the list
if (depends.front() == target) {
depends.erase(depends.begin());
}
// ensure source file is the first dependency
if (depends.front() != source) {
cm::erase(depends, source);
if (!isValidPath) {
depends.insert(depends.begin(), source);
}
} else if (isValidPath) {
// remove first dependency because it must not be filtered out
depends.erase(depends.begin());
}
} else {
// unknown format, ignore it
continue;
}
if (isValidPath) {
cm::erase_if(depends, isValidPath);
// insert source as first dependency
depends.insert(depends.begin(), source);
}
dependencies[target] = std::move(depends);
}
}
}
return status;
}
void cmDependsCompiler::WriteDependencies(
const cmDepends::DependencyMap& dependencies, std::ostream& makeDepends,
std::ostream& internalDepends)
{
// dependencies file consumed by make tool
const auto& lineContinue = static_cast<cmGlobalUnixMakefileGenerator3*>(
this->LocalGenerator->GetGlobalGenerator())
->LineContinueDirective;
bool supportLongLineDepend = static_cast<cmGlobalUnixMakefileGenerator3*>(
this->LocalGenerator->GetGlobalGenerator())
->SupportsLongLineDependencies();
cmDepends::DependencyMap makeDependencies(dependencies);
std::unordered_set<cm::string_view> phonyTargets;
// external dependencies file
for (auto& node : makeDependencies) {
auto target = this->LocalGenerator->ConvertToMakefilePath(
this->LocalGenerator->MaybeRelativeToTopBinDir(node.first));
auto& deps = node.second;
std::transform(deps.cbegin(), deps.cend(), deps.begin(),
[this](const std::string& dep) {
return this->LocalGenerator->ConvertToMakefilePath(
this->LocalGenerator->MaybeRelativeToTopBinDir(dep));
});
bool first_dep = true;
if (supportLongLineDepend) {
makeDepends << target << ": ";
}
for (const auto& dep : deps) {
if (supportLongLineDepend) {
if (first_dep) {
first_dep = false;
makeDepends << dep;
} else {
makeDepends << ' ' << lineContinue << " " << dep;
}
} else {
makeDepends << target << ": " << dep << std::endl;
}
phonyTargets.emplace(dep.data(), dep.length());
}
makeDepends << std::endl << std::endl;
}
// add phony targets
for (const auto& target : phonyTargets) {
makeDepends << std::endl << target << ':' << std::endl;
}
// internal dependencies file
for (const auto& node : dependencies) {
internalDepends << node.first << std::endl;
for (const auto& dep : node.second) {
internalDepends << ' ' << dep << std::endl;
}
internalDepends << std::endl;
}
}
void cmDependsCompiler::ClearDependencies(
const std::vector<std::string>& depFiles)
{
for (auto dep = depFiles.begin(); dep != depFiles.end(); dep++) {
dep += 3;
cmSystemTools::RemoveFile(*dep);
}
}