cmake/Source/cmOutputRequiredFilesCommand.cxx

515 lines
15 KiB
C++
Raw Normal View History

2016-10-30 18:24:19 +01:00
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmOutputRequiredFilesCommand.h"
2016-07-09 11:21:54 +02:00
2020-02-01 23:06:01 +01:00
#include <cstdio>
2017-04-14 19:02:05 +02:00
#include <map>
2020-02-01 23:06:01 +01:00
#include <set>
2017-04-14 19:02:05 +02:00
#include <utility>
2020-08-30 11:54:41 +02:00
#include <cm/memory>
2020-02-01 23:06:01 +01:00
#include "cmsys/FStream.hxx"
#include "cmsys/RegularExpression.hxx"
#include "cmExecutionStatus.h"
2017-04-14 19:02:05 +02:00
#include "cmGeneratorExpression.h"
#include "cmMakefile.h"
2020-08-30 11:54:41 +02:00
#include "cmProperty.h"
2017-04-14 19:02:05 +02:00
#include "cmSourceFile.h"
2020-02-01 23:06:01 +01:00
#include "cmStringAlgorithms.h"
2017-04-14 19:02:05 +02:00
#include "cmSystemTools.h"
#include "cmTarget.h"
2020-02-01 23:06:01 +01:00
namespace {
2016-03-13 13:35:51 +01:00
/** \class cmDependInformation
* \brief Store dependency information for a single source file.
*
* This structure stores the depend information for a single source file.
*/
class cmDependInformation
{
2016-03-13 13:35:51 +01:00
public:
/**
2016-03-13 13:35:51 +01:00
* Construct with dependency generation marked not done; instance
* not placed in cmMakefile's list.
*/
2019-11-11 23:01:05 +01:00
cmDependInformation() = default;
2016-03-13 13:35:51 +01:00
/**
* The set of files on which this one depends.
*/
2020-02-01 23:06:01 +01:00
using DependencySetType = std::set<cmDependInformation*>;
2016-03-13 13:35:51 +01:00
DependencySetType DependencySet;
/**
* This flag indicates whether dependency checking has been
* performed for this file.
*/
2019-11-11 23:01:05 +01:00
bool DependDone = false;
2016-03-13 13:35:51 +01:00
/**
* If this object corresponds to a cmSourceFile instance, this points
* to it.
*/
2019-11-11 23:01:05 +01:00
const cmSourceFile* SourceFile = nullptr;
2016-03-13 13:35:51 +01:00
/**
* Full path to this file.
*/
std::string FullPath;
/**
* Full path not including file name.
*/
std::string PathOnly;
/**
* Name used to #include this file.
*/
std::string IncludeName;
/**
* This method adds the dependencies of another file to this one.
*/
void AddDependencies(cmDependInformation* info)
{
2016-07-09 11:21:54 +02:00
if (this != info) {
this->DependencySet.insert(info);
}
2016-03-13 13:35:51 +01:00
}
};
2013-03-16 19:13:01 +02:00
2016-03-13 13:35:51 +01:00
class cmLBDepend
{
public:
/**
* Construct the object with verbose turned off.
*/
cmLBDepend()
{
this->Verbose = false;
this->IncludeFileRegularExpression.compile("^.*$");
this->ComplainFileRegularExpression.compile("^$");
}
/**
* Destructor.
*/
2020-08-30 11:54:41 +02:00
~cmLBDepend() = default;
2016-03-13 13:35:51 +01:00
2019-11-11 23:01:05 +01:00
cmLBDepend(const cmLBDepend&) = delete;
cmLBDepend& operator=(const cmLBDepend&) = delete;
2016-03-13 13:35:51 +01:00
/**
* Set the makefile that is used as a source of classes.
*/
void SetMakefile(cmMakefile* makefile)
{
this->Makefile = makefile;
// Now extract the include file regular expression from the makefile.
this->IncludeFileRegularExpression.compile(
2016-07-09 11:21:54 +02:00
this->Makefile->GetIncludeRegularExpression());
2016-03-13 13:35:51 +01:00
this->ComplainFileRegularExpression.compile(
2016-07-09 11:21:54 +02:00
this->Makefile->GetComplainRegularExpression());
2016-03-13 13:35:51 +01:00
// Now extract any include paths from the targets
std::set<std::string> uniqueIncludes;
std::vector<std::string> orderedAndUniqueIncludes;
2019-11-11 23:01:05 +01:00
for (auto const& target : this->Makefile->GetTargets()) {
2020-08-30 11:54:41 +02:00
cmProp incDirProp = target.second.GetProperty("INCLUDE_DIRECTORIES");
2016-07-09 11:21:54 +02:00
if (!incDirProp) {
2016-03-13 13:35:51 +01:00
continue;
2016-07-09 11:21:54 +02:00
}
2016-03-13 13:35:51 +01:00
2016-07-09 11:21:54 +02:00
std::string incDirs = cmGeneratorExpression::Preprocess(
2020-08-30 11:54:41 +02:00
*incDirProp, cmGeneratorExpression::StripAllGeneratorExpressions);
2016-03-13 13:35:51 +01:00
2020-02-01 23:06:01 +01:00
std::vector<std::string> includes = cmExpandedList(incDirs);
2016-03-13 13:35:51 +01:00
2018-01-26 17:06:56 +01:00
for (std::string& path : includes) {
2016-03-13 13:35:51 +01:00
this->Makefile->ExpandVariablesInString(path);
2016-07-09 11:21:54 +02:00
if (uniqueIncludes.insert(path).second) {
2016-03-13 13:35:51 +01:00
orderedAndUniqueIncludes.push_back(path);
}
2016-03-13 13:35:51 +01:00
}
2016-07-09 11:21:54 +02:00
}
2013-03-16 19:13:01 +02:00
2018-01-26 17:06:56 +01:00
for (std::string const& inc : orderedAndUniqueIncludes) {
this->AddSearchPath(inc);
2016-07-09 11:21:54 +02:00
}
2016-03-13 13:35:51 +01:00
}
/**
* Add a directory to the search path for include files.
*/
void AddSearchPath(const std::string& path)
{
this->IncludeDirectories.push_back(path);
}
/**
* Generate dependencies for the file given. Returns a pointer to
* the cmDependInformation object for the file.
*/
2020-08-30 11:54:41 +02:00
const cmDependInformation* FindDependencies(const std::string& file)
2016-03-13 13:35:51 +01:00
{
2020-08-30 11:54:41 +02:00
cmDependInformation* info = this->GetDependInformation(file, "");
2016-03-13 13:35:51 +01:00
this->GenerateDependInformation(info);
return info;
}
protected:
/**
* Compute the depend information for this class.
*/
void DependWalk(cmDependInformation* info)
{
cmsys::ifstream fin(info->FullPath.c_str());
2016-07-09 11:21:54 +02:00
if (!fin) {
2019-11-11 23:01:05 +01:00
cmSystemTools::Error("error can not open " + info->FullPath);
2016-03-13 13:35:51 +01:00
return;
2016-07-09 11:21:54 +02:00
}
2016-03-13 13:35:51 +01:00
std::string line;
2016-07-09 11:21:54 +02:00
while (cmSystemTools::GetLineFromStream(fin, line)) {
2018-04-23 21:13:27 +02:00
if (cmHasLiteralPrefix(line, "#include")) {
2016-03-13 13:35:51 +01:00
// if it is an include line then create a string class
2017-04-14 19:02:05 +02:00
size_t qstart = line.find('\"', 8);
2016-03-13 13:35:51 +01:00
size_t qend;
// if a quote is not found look for a <
2016-07-09 11:21:54 +02:00
if (qstart == std::string::npos) {
2017-04-14 19:02:05 +02:00
qstart = line.find('<', 8);
2016-03-13 13:35:51 +01:00
// if a < is not found then move on
2016-07-09 11:21:54 +02:00
if (qstart == std::string::npos) {
2019-11-11 23:01:05 +01:00
cmSystemTools::Error("unknown include directive " + line);
2016-03-13 13:35:51 +01:00
continue;
}
2017-07-20 19:35:53 +02:00
qend = line.find('>', qstart + 1);
2016-07-09 11:21:54 +02:00
} else {
2017-04-14 19:02:05 +02:00
qend = line.find('\"', qstart + 1);
2016-07-09 11:21:54 +02:00
}
2016-03-13 13:35:51 +01:00
// extract the file being included
2017-04-14 19:02:05 +02:00
std::string includeFile = line.substr(qstart + 1, qend - qstart - 1);
2016-03-13 13:35:51 +01:00
// see if the include matches the regular expression
2016-07-09 11:21:54 +02:00
if (!this->IncludeFileRegularExpression.find(includeFile)) {
if (this->Verbose) {
2020-02-01 23:06:01 +01:00
std::string message =
cmStrCat("Skipping ", includeFile, " for file ", info->FullPath);
2019-11-11 23:01:05 +01:00
cmSystemTools::Error(message);
}
2016-07-09 11:21:54 +02:00
continue;
}
2016-03-13 13:35:51 +01:00
// Add this file and all its dependencies.
2020-08-30 11:54:41 +02:00
this->AddDependency(info, includeFile);
2016-03-13 13:35:51 +01:00
/// add the cxx file if it exists
std::string cxxFile = includeFile;
std::string::size_type pos = cxxFile.rfind('.');
2016-07-09 11:21:54 +02:00
if (pos != std::string::npos) {
2016-03-13 13:35:51 +01:00
std::string root = cxxFile.substr(0, pos);
cxxFile = root + ".cxx";
bool found = false;
// try jumping to .cxx .cpp and .c in order
2018-04-23 21:13:27 +02:00
if (cmSystemTools::FileExists(cxxFile)) {
found = true;
2016-07-09 11:21:54 +02:00
}
2020-02-01 23:06:01 +01:00
for (std::string const& path : this->IncludeDirectories) {
if (cmSystemTools::FileExists(cmStrCat(path, "/", cxxFile))) {
found = true;
}
2016-07-09 11:21:54 +02:00
}
if (!found) {
2016-03-13 13:35:51 +01:00
cxxFile = root + ".cpp";
2018-04-23 21:13:27 +02:00
if (cmSystemTools::FileExists(cxxFile)) {
found = true;
2016-07-09 11:21:54 +02:00
}
2020-02-01 23:06:01 +01:00
for (std::string const& path : this->IncludeDirectories) {
if (cmSystemTools::FileExists(cmStrCat(path, "/", cxxFile))) {
2016-03-13 13:35:51 +01:00
found = true;
}
}
2016-07-09 11:21:54 +02:00
}
if (!found) {
2016-03-13 13:35:51 +01:00
cxxFile = root + ".c";
2018-04-23 21:13:27 +02:00
if (cmSystemTools::FileExists(cxxFile)) {
2016-03-13 13:35:51 +01:00
found = true;
2016-07-09 11:21:54 +02:00
}
2020-02-01 23:06:01 +01:00
for (std::string const& path : this->IncludeDirectories) {
if (cmSystemTools::FileExists(cmStrCat(path, "/", cxxFile))) {
2016-03-13 13:35:51 +01:00
found = true;
}
}
2016-07-09 11:21:54 +02:00
}
if (!found) {
2016-03-13 13:35:51 +01:00
cxxFile = root + ".txx";
2018-04-23 21:13:27 +02:00
if (cmSystemTools::FileExists(cxxFile)) {
found = true;
2016-07-09 11:21:54 +02:00
}
2020-02-01 23:06:01 +01:00
for (std::string const& path : this->IncludeDirectories) {
if (cmSystemTools::FileExists(cmStrCat(path, "/", cxxFile))) {
2016-03-13 13:35:51 +01:00
found = true;
}
}
2016-07-09 11:21:54 +02:00
}
if (found) {
2020-08-30 11:54:41 +02:00
this->AddDependency(info, cxxFile);
}
2016-03-13 13:35:51 +01:00
}
}
2016-07-09 11:21:54 +02:00
}
2016-03-13 13:35:51 +01:00
}
/**
* Add a dependency. Possibly walk it for more dependencies.
*/
2020-08-30 11:54:41 +02:00
void AddDependency(cmDependInformation* info, const std::string& file)
2016-03-13 13:35:51 +01:00
{
cmDependInformation* dependInfo =
2020-08-30 11:54:41 +02:00
this->GetDependInformation(file, info->PathOnly);
2016-03-13 13:35:51 +01:00
this->GenerateDependInformation(dependInfo);
info->AddDependencies(dependInfo);
}
/**
* Fill in the given object with dependency information. If the
* information is already complete, nothing is done.
*/
void GenerateDependInformation(cmDependInformation* info)
{
// If dependencies are already done, stop now.
2016-07-09 11:21:54 +02:00
if (info->DependDone) {
2016-03-13 13:35:51 +01:00
return;
2016-07-09 11:21:54 +02:00
}
2016-10-30 18:24:19 +01:00
// Make sure we don't visit the same file more than once.
info->DependDone = true;
2019-11-11 23:01:05 +01:00
const std::string& path = info->FullPath;
if (path.empty()) {
2016-03-13 13:35:51 +01:00
cmSystemTools::Error(
2016-07-09 11:21:54 +02:00
"Attempt to find dependencies for file without path!");
2016-03-13 13:35:51 +01:00
return;
2016-07-09 11:21:54 +02:00
}
2016-03-13 13:35:51 +01:00
bool found = false;
// If the file exists, use it to find dependency information.
2016-07-09 11:21:54 +02:00
if (cmSystemTools::FileExists(path, true)) {
2016-03-13 13:35:51 +01:00
// Use the real file to find its dependencies.
this->DependWalk(info);
found = true;
2016-07-09 11:21:54 +02:00
}
2016-03-13 13:35:51 +01:00
// See if the cmSourceFile for it has any files specified as
// dependency hints.
2018-01-26 17:06:56 +01:00
if (info->SourceFile != nullptr) {
2016-03-13 13:35:51 +01:00
// Get the cmSourceFile corresponding to this.
const cmSourceFile& cFile = *(info->SourceFile);
// See if there are any hints for finding dependencies for the missing
// file.
2016-07-09 11:21:54 +02:00
if (!cFile.GetDepends().empty()) {
2016-03-13 13:35:51 +01:00
// Dependency hints have been given. Use them to begin the
// recursion.
2018-01-26 17:06:56 +01:00
for (std::string const& file : cFile.GetDepends()) {
2020-08-30 11:54:41 +02:00
this->AddDependency(info, file);
2016-07-09 11:21:54 +02:00
}
2016-03-13 13:35:51 +01:00
// Found dependency information. We are done.
found = true;
}
2016-07-09 11:21:54 +02:00
}
2016-03-13 13:35:51 +01:00
2016-07-09 11:21:54 +02:00
if (!found) {
2016-03-13 13:35:51 +01:00
// Try to find the file amongst the sources
2016-07-09 11:21:54 +02:00
cmSourceFile* srcFile = this->Makefile->GetSource(
cmSystemTools::GetFilenameWithoutExtension(path));
if (srcFile) {
2020-02-01 23:06:01 +01:00
if (srcFile->ResolveFullPath() == path) {
2016-07-09 11:21:54 +02:00
found = true;
} else {
// try to guess which include path to use
2018-01-26 17:06:56 +01:00
for (std::string incpath : this->IncludeDirectories) {
2019-11-11 23:01:05 +01:00
if (!incpath.empty() && incpath.back() != '/') {
2020-02-01 23:06:01 +01:00
incpath += "/";
2016-07-09 11:21:54 +02:00
}
2020-02-01 23:06:01 +01:00
incpath += path;
if (srcFile->ResolveFullPath() == incpath) {
2016-03-13 13:35:51 +01:00
// set the path to the guessed path
info->FullPath = incpath;
2016-07-09 11:21:54 +02:00
found = true;
2016-03-13 13:35:51 +01:00
}
}
}
}
2016-07-09 11:21:54 +02:00
}
2016-03-13 13:35:51 +01:00
2016-07-09 11:21:54 +02:00
if (!found) {
2016-03-13 13:35:51 +01:00
// Couldn't find any dependency information.
2018-10-28 12:09:07 +01:00
if (this->ComplainFileRegularExpression.find(info->IncludeName)) {
2019-11-11 23:01:05 +01:00
cmSystemTools::Error("error cannot find dependencies for " + path);
2016-07-09 11:21:54 +02:00
} else {
2016-03-13 13:35:51 +01:00
// Destroy the name of the file so that it won't be output as a
// dependency.
2018-01-26 17:06:56 +01:00
info->FullPath.clear();
2016-03-13 13:35:51 +01:00
}
2016-07-09 11:21:54 +02:00
}
2016-03-13 13:35:51 +01:00
}
/**
* Get an instance of cmDependInformation corresponding to the given file
* name.
*/
2020-08-30 11:54:41 +02:00
cmDependInformation* GetDependInformation(const std::string& file,
const std::string& extraPath)
2016-03-13 13:35:51 +01:00
{
// Get the full path for the file so that lookup is unambiguous.
std::string fullPath = this->FullPath(file, extraPath);
// Try to find the file's instance of cmDependInformation.
2020-02-01 23:06:01 +01:00
auto result = this->DependInformationMap.find(fullPath);
2016-07-09 11:21:54 +02:00
if (result != this->DependInformationMap.end()) {
2016-03-13 13:35:51 +01:00
// Found an instance, return it.
2020-08-30 11:54:41 +02:00
return result->second.get();
2016-07-09 11:21:54 +02:00
}
2016-10-30 18:24:19 +01:00
// Didn't find an instance. Create a new one and save it.
2020-08-30 11:54:41 +02:00
auto info = cm::make_unique<cmDependInformation>();
auto ptr = info.get();
2016-10-30 18:24:19 +01:00
info->FullPath = fullPath;
info->PathOnly = cmSystemTools::GetFilenamePath(fullPath);
info->IncludeName = file;
2020-08-30 11:54:41 +02:00
this->DependInformationMap[fullPath] = std::move(info);
return ptr;
2016-03-13 13:35:51 +01:00
}
/**
* Find the full path name for the given file name.
* This uses the include directories.
* TODO: Cache path conversions to reduce FileExists calls.
*/
2020-08-30 11:54:41 +02:00
std::string FullPath(const std::string& fname, const std::string& extraPath)
2016-03-13 13:35:51 +01:00
{
2020-08-30 11:54:41 +02:00
auto m = this->DirectoryToFileToPathMap.find(extraPath);
2016-03-13 13:35:51 +01:00
2016-07-09 11:21:54 +02:00
if (m != this->DirectoryToFileToPathMap.end()) {
2016-03-13 13:35:51 +01:00
FileToPathMapType& map = m->second;
2020-02-01 23:06:01 +01:00
auto p = map.find(fname);
2016-07-09 11:21:54 +02:00
if (p != map.end()) {
2016-03-13 13:35:51 +01:00
return p->second;
}
2016-07-09 11:21:54 +02:00
}
2016-03-13 13:35:51 +01:00
2016-07-09 11:21:54 +02:00
if (cmSystemTools::FileExists(fname, true)) {
2016-03-13 13:35:51 +01:00
std::string fp = cmSystemTools::CollapseFullPath(fname);
2020-08-30 11:54:41 +02:00
this->DirectoryToFileToPathMap[extraPath][fname] = fp;
2016-03-13 13:35:51 +01:00
return fp;
2016-07-09 11:21:54 +02:00
}
2016-03-13 13:35:51 +01:00
2018-01-26 17:06:56 +01:00
for (std::string path : this->IncludeDirectories) {
2019-11-11 23:01:05 +01:00
if (!path.empty() && path.back() != '/') {
2020-02-01 23:06:01 +01:00
path += "/";
2016-07-09 11:21:54 +02:00
}
2020-02-01 23:06:01 +01:00
path += fname;
2018-04-23 21:13:27 +02:00
if (cmSystemTools::FileExists(path, true) &&
2016-07-09 11:21:54 +02:00
!cmSystemTools::FileIsDirectory(path)) {
2016-03-13 13:35:51 +01:00
std::string fp = cmSystemTools::CollapseFullPath(path);
2020-08-30 11:54:41 +02:00
this->DirectoryToFileToPathMap[extraPath][fname] = fp;
2016-03-13 13:35:51 +01:00
return fp;
}
2016-07-09 11:21:54 +02:00
}
2016-03-13 13:35:51 +01:00
2020-08-30 11:54:41 +02:00
if (!extraPath.empty()) {
2016-03-13 13:35:51 +01:00
std::string path = extraPath;
2019-11-11 23:01:05 +01:00
if (!path.empty() && path.back() != '/') {
2016-03-13 13:35:51 +01:00
path = path + "/";
2016-07-09 11:21:54 +02:00
}
2016-03-13 13:35:51 +01:00
path = path + fname;
2018-04-23 21:13:27 +02:00
if (cmSystemTools::FileExists(path, true) &&
2016-07-09 11:21:54 +02:00
!cmSystemTools::FileIsDirectory(path)) {
2016-03-13 13:35:51 +01:00
std::string fp = cmSystemTools::CollapseFullPath(path);
this->DirectoryToFileToPathMap[extraPath][fname] = fp;
return fp;
}
2016-07-09 11:21:54 +02:00
}
2016-03-13 13:35:51 +01:00
// Couldn't find the file.
2020-08-30 11:54:41 +02:00
return fname;
2016-03-13 13:35:51 +01:00
}
cmMakefile* Makefile;
bool Verbose;
cmsys::RegularExpression IncludeFileRegularExpression;
cmsys::RegularExpression ComplainFileRegularExpression;
std::vector<std::string> IncludeDirectories;
2020-02-01 23:06:01 +01:00
using FileToPathMapType = std::map<std::string, std::string>;
using DirectoryToFileToPathMapType =
std::map<std::string, FileToPathMapType>;
2020-08-30 11:54:41 +02:00
using DependInformationMapType =
std::map<std::string, std::unique_ptr<cmDependInformation>>;
2016-03-13 13:35:51 +01:00
DependInformationMapType DependInformationMap;
DirectoryToFileToPathMapType DirectoryToFileToPathMap;
};
2020-02-01 23:06:01 +01:00
void ListDependencies(cmDependInformation const* info, FILE* fout,
std::set<cmDependInformation const*>* visited);
}
// cmOutputRequiredFilesCommand
2020-02-01 23:06:01 +01:00
bool cmOutputRequiredFilesCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
2016-07-09 11:21:54 +02:00
if (args.size() != 2) {
2020-02-01 23:06:01 +01:00
status.SetError("called with incorrect number of arguments");
return false;
2016-07-09 11:21:54 +02:00
}
// store the arg for final pass
2020-02-01 23:06:01 +01:00
const std::string& file = args[0];
const std::string& outputFile = args[1];
2013-03-16 19:13:01 +02:00
// compute the list of files
cmLBDepend md;
2020-02-01 23:06:01 +01:00
md.SetMakefile(&status.GetMakefile());
md.AddSearchPath(status.GetMakefile().GetCurrentSourceDirectory());
// find the depends for a file
2020-08-30 11:54:41 +02:00
const cmDependInformation* info = md.FindDependencies(file);
2016-07-09 11:21:54 +02:00
if (info) {
// write them out
2020-02-01 23:06:01 +01:00
FILE* fout = cmsys::SystemTools::Fopen(outputFile, "w");
2016-07-09 11:21:54 +02:00
if (!fout) {
2020-02-01 23:06:01 +01:00
status.SetError(cmStrCat("Can not open output file: ", outputFile));
return false;
2016-07-09 11:21:54 +02:00
}
std::set<cmDependInformation const*> visited;
2020-02-01 23:06:01 +01:00
ListDependencies(info, fout, &visited);
fclose(fout);
2016-07-09 11:21:54 +02:00
}
return true;
}
2020-02-01 23:06:01 +01:00
namespace {
void ListDependencies(cmDependInformation const* info, FILE* fout,
std::set<cmDependInformation const*>* visited)
{
// add info to the visited set
visited->insert(info);
// now recurse with info's dependencies
2018-01-26 17:06:56 +01:00
for (cmDependInformation* d : info->DependencySet) {
if (visited->find(d) == visited->end()) {
if (!info->FullPath.empty()) {
std::string tmp = d->FullPath;
std::string::size_type pos = tmp.rfind('.');
2016-07-09 11:21:54 +02:00
if (pos != std::string::npos && (tmp.substr(pos) != ".h")) {
tmp = tmp.substr(0, pos);
2018-01-26 17:06:56 +01:00
fprintf(fout, "%s\n", d->FullPath.c_str());
}
}
2020-02-01 23:06:01 +01:00
ListDependencies(d, fout, visited);
}
2016-07-09 11:21:54 +02:00
}
}
2020-02-01 23:06:01 +01:00
}