mirror of
https://github.com/Kitware/CMake.git
synced 2025-05-09 14:57:08 +08:00

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.
251 lines
7.7 KiB
C++
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);
|
|
}
|
|
}
|