1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
7 #include "cmsys/FStream.hxx"
9 #include "cmFileTime.h"
10 #include "cmFileTimeCache.h"
11 #include "cmGeneratedFileStream.h"
13 #include "cmLocalUnixMakefileGenerator3.h"
14 #include "cmMakefile.h"
15 #include "cmStringAlgorithms.h"
16 #include "cmSystemTools.h"
19 cmDepends::cmDepends(cmLocalUnixMakefileGenerator3
* lg
, std::string targetDir
)
21 , TargetDirectory(std::move(targetDir
))
25 cmDepends::~cmDepends() = default;
27 bool cmDepends::Write(std::ostream
& makeDepends
, std::ostream
& internalDepends
)
29 std::map
<std::string
, std::set
<std::string
>> dependencies
;
31 // Lookup the set of sources to scan.
34 std::string
const srcLang
= "CMAKE_DEPENDS_CHECK_" + this->Language
;
35 cmMakefile
* mf
= this->LocalGenerator
->GetMakefile();
36 pairs
.assign(mf
->GetSafeDefinition(srcLang
));
38 for (auto si
= pairs
.begin(); si
!= pairs
.end();) {
39 // Get the source and object file.
40 std::string
const& src
= *si
++;
41 if (si
== pairs
.end()) {
44 std::string
const& obj
= *si
++;
45 dependencies
[obj
].insert(src
);
48 for (auto const& d
: dependencies
) {
49 // Write the dependencies for this pair.
50 if (!this->WriteDependencies(d
.second
, d
.first
, makeDepends
,
56 return this->Finalize(makeDepends
, internalDepends
);
59 bool cmDepends::Finalize(std::ostream
& /*unused*/, std::ostream
& /*unused*/)
64 bool cmDepends::Check(const std::string
& makeFile
,
65 const std::string
& internalFile
,
66 DependencyMap
& validDeps
)
68 // Check whether dependencies must be regenerated.
70 cmsys::ifstream
fin(internalFile
.c_str());
71 if (!(fin
&& this->CheckDependencies(fin
, internalFile
, validDeps
))) {
72 // Clear all dependencies so they will be regenerated.
73 this->Clear(makeFile
);
74 cmSystemTools::RemoveFile(internalFile
);
75 this->FileTimeCache
->Remove(internalFile
);
81 void cmDepends::Clear(const std::string
& file
) const
83 // Print verbose output.
85 cmSystemTools::Stdout(
86 cmStrCat("Clearing dependencies in \"", file
, "\".\n"));
89 // Write an empty dependency file.
90 cmGeneratedFileStream
depFileStream(file
);
91 depFileStream
<< "# Empty dependencies file\n"
92 "# This may be replaced when dependencies are built.\n";
95 bool cmDepends::WriteDependencies(const std::set
<std::string
>& /*unused*/,
96 const std::string
& /*unused*/,
97 std::ostream
& /*unused*/,
98 std::ostream
& /*unused*/)
100 // This should be implemented by the subclass.
104 bool cmDepends::CheckDependencies(std::istream
& internalDepends
,
105 const std::string
& internalDependsFileName
,
106 DependencyMap
& validDeps
)
108 // Read internal depends file time
109 cmFileTime internalDependsTime
;
110 if (!this->FileTimeCache
->Load(internalDependsFileName
,
111 internalDependsTime
)) {
115 // Parse dependencies from the stream. If any dependee is missing
116 // or newer than the depender then dependencies should be
119 bool dependerExists
= false;
123 std::string depender
;
124 std::string dependee
;
125 cmFileTime dependerTime
;
126 cmFileTime dependeeTime
;
127 std::vector
<std::string
>* currentDependencies
= nullptr;
129 while (std::getline(internalDepends
, line
)) {
130 // Check if this an empty or a comment line
131 if (line
.empty() || line
.front() == '#') {
134 // Drop carriage return character at the end
135 if (line
.back() == '\r') {
141 // Check if this a depender line
142 if (line
.front() != ' ') {
144 dependerExists
= this->FileTimeCache
->Load(depender
, dependerTime
);
145 // If we erase validDeps[this->Depender] by overwriting it with an empty
146 // vector, we lose dependencies for dependers that have multiple
147 // entries. No need to initialize the entry, std::map will do so on first
149 currentDependencies
= &validDeps
[depender
];
153 // This is a dependee line
154 dependee
= line
.substr(1);
156 // Add dependee to depender's list
157 if (currentDependencies
!= nullptr) {
158 currentDependencies
->push_back(dependee
);
161 // Dependencies must be regenerated
162 // * if the dependee does not exist
163 // * if the depender exists and is older than the dependee.
164 // * if the depender does not exist, but the dependee is newer than the
166 bool regenerate
= false;
167 bool dependeeExists
= this->FileTimeCache
->Load(dependee
, dependeeTime
);
168 if (!dependeeExists
) {
169 // The dependee does not exist.
172 // Print verbose output.
174 cmSystemTools::Stdout(cmStrCat("Dependee \"", dependee
,
175 "\" does not exist for depender \"",
178 } else if (dependerExists
) {
179 // The dependee and depender both exist. Compare file times.
180 if (dependerTime
.Older(dependeeTime
)) {
181 // The depender is older than the dependee.
184 // Print verbose output.
186 cmSystemTools::Stdout(cmStrCat("Dependee \"", dependee
,
187 "\" is newer than depender \"",
192 // The dependee exists, but the depender doesn't. Regenerate if the
193 // internalDepends file is older than the dependee.
194 if (internalDependsTime
.Older(dependeeTime
)) {
195 // The depends-file is older than the dependee.
198 // Print verbose output.
200 cmSystemTools::Stdout(cmStrCat("Dependee \"", dependee
,
201 "\" is newer than depends file \"",
202 internalDependsFileName
, "\".\n"));
208 // Dependencies must be regenerated.
211 // Remove the information of this depender from the map, it needs
213 if (currentDependencies
!= nullptr) {
214 validDeps
.erase(depender
);
215 currentDependencies
= nullptr;
218 // Remove the depender to be sure it is rebuilt.
219 if (dependerExists
) {
220 cmSystemTools::RemoveFile(depender
);
221 this->FileTimeCache
->Remove(depender
);
222 dependerExists
= false;
230 void cmDepends::SetIncludePathFromLanguage(const std::string
& lang
)
232 // Look for the new per "TARGET_" variant first:
233 std::string includePathVar
=
234 cmStrCat("CMAKE_", lang
, "_TARGET_INCLUDE_PATH");
235 cmMakefile
* mf
= this->LocalGenerator
->GetMakefile();
236 cmValue includePath
= mf
->GetDefinition(includePathVar
);
238 cmExpandList(*includePath
, this->IncludePath
);
240 // Fallback to the old directory level variable if no per-target var:
241 includePathVar
= cmStrCat("CMAKE_", lang
, "_INCLUDE_PATH");
242 includePath
= mf
->GetDefinition(includePathVar
);
244 cmExpandList(*includePath
, this->IncludePath
);