/* 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 #include #include #include #include #include #include #include #include #include #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& depFiles, cmDepends::DependencyMap& dependencies, const std::function& 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* 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 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) { auto& rule_deps = dependencies[entry.rules[index]]; rule_deps.insert(rule_deps.end(), depends.cbegin(), depends.cend()); } auto& rule_deps = dependencies[entry.rules.front()]; std::move(depends.cbegin(), depends.cend(), std::back_inserter(rule_deps)); } } else { if (format == "msvc"_s) { cmsys::ifstream fin(depFile.c_str()); if (!fin) { continue; } std::string line; if (!isValidPath && !source.empty()) { // 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 (!source.empty()) { 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); if (!source.empty()) { // 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( this->LocalGenerator->GetGlobalGenerator()) ->LineContinueDirective; bool supportLongLineDepend = static_cast( this->LocalGenerator->GetGlobalGenerator()) ->SupportsLongLineDependencies(); cmDepends::DependencyMap makeDependencies(dependencies); std::unordered_set 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& depFiles) { for (auto dep = depFiles.begin(); dep != depFiles.end(); dep++) { dep += 3; cmSystemTools::RemoveFile(*dep); } }