You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
253 lines
7.8 KiB
253 lines
7.8 KiB
/* 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(), this->LocalGenerator->GetCurrentBinaryDirectory(),
|
|
GccDepfilePrependPaths::Deps);
|
|
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);
|
|
}
|
|
}
|