1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmDepends.h"
4 
5 #include <utility>
6 
7 #include "cmsys/FStream.hxx"
8 
9 #include "cmFileTime.h"
10 #include "cmFileTimeCache.h"
11 #include "cmGeneratedFileStream.h"
12 #include "cmLocalUnixMakefileGenerator3.h"
13 #include "cmMakefile.h"
14 #include "cmStringAlgorithms.h"
15 #include "cmSystemTools.h"
16 #include "cmValue.h"
17 
cmDepends(cmLocalUnixMakefileGenerator3 * lg,std::string targetDir)18 cmDepends::cmDepends(cmLocalUnixMakefileGenerator3* lg, std::string targetDir)
19   : LocalGenerator(lg)
20   , TargetDirectory(std::move(targetDir))
21 {
22 }
23 
24 cmDepends::~cmDepends() = default;
25 
Write(std::ostream & makeDepends,std::ostream & internalDepends)26 bool cmDepends::Write(std::ostream& makeDepends, std::ostream& internalDepends)
27 {
28   std::map<std::string, std::set<std::string>> dependencies;
29   {
30     // Lookup the set of sources to scan.
31     std::vector<std::string> pairs;
32     {
33       std::string const srcLang = "CMAKE_DEPENDS_CHECK_" + this->Language;
34       cmMakefile* mf = this->LocalGenerator->GetMakefile();
35       cmExpandList(mf->GetSafeDefinition(srcLang), pairs);
36     }
37     for (auto si = pairs.begin(); si != pairs.end();) {
38       // Get the source and object file.
39       std::string const& src = *si++;
40       if (si == pairs.end()) {
41         break;
42       }
43       std::string const& obj = *si++;
44       dependencies[obj].insert(src);
45     }
46   }
47   for (auto const& d : dependencies) {
48     // Write the dependencies for this pair.
49     if (!this->WriteDependencies(d.second, d.first, makeDepends,
50                                  internalDepends)) {
51       return false;
52     }
53   }
54 
55   return this->Finalize(makeDepends, internalDepends);
56 }
57 
Finalize(std::ostream &,std::ostream &)58 bool cmDepends::Finalize(std::ostream& /*unused*/, std::ostream& /*unused*/)
59 {
60   return true;
61 }
62 
Check(const std::string & makeFile,const std::string & internalFile,DependencyMap & validDeps)63 bool cmDepends::Check(const std::string& makeFile,
64                       const std::string& internalFile,
65                       DependencyMap& validDeps)
66 {
67   // Check whether dependencies must be regenerated.
68   bool okay = true;
69   cmsys::ifstream fin(internalFile.c_str());
70   if (!(fin && this->CheckDependencies(fin, internalFile, validDeps))) {
71     // Clear all dependencies so they will be regenerated.
72     this->Clear(makeFile);
73     cmSystemTools::RemoveFile(internalFile);
74     this->FileTimeCache->Remove(internalFile);
75     okay = false;
76   }
77   return okay;
78 }
79 
Clear(const std::string & file) const80 void cmDepends::Clear(const std::string& file) const
81 {
82   // Print verbose output.
83   if (this->Verbose) {
84     cmSystemTools::Stdout(
85       cmStrCat("Clearing dependencies in \"", file, "\".\n"));
86   }
87 
88   // Write an empty dependency file.
89   cmGeneratedFileStream depFileStream(file);
90   depFileStream << "# Empty dependencies file\n"
91                    "# This may be replaced when dependencies are built.\n";
92 }
93 
WriteDependencies(const std::set<std::string> &,const std::string &,std::ostream &,std::ostream &)94 bool cmDepends::WriteDependencies(const std::set<std::string>& /*unused*/,
95                                   const std::string& /*unused*/,
96                                   std::ostream& /*unused*/,
97                                   std::ostream& /*unused*/)
98 {
99   // This should be implemented by the subclass.
100   return false;
101 }
102 
CheckDependencies(std::istream & internalDepends,const std::string & internalDependsFileName,DependencyMap & validDeps)103 bool cmDepends::CheckDependencies(std::istream& internalDepends,
104                                   const std::string& internalDependsFileName,
105                                   DependencyMap& validDeps)
106 {
107   // Read internal depends file time
108   cmFileTime internalDependsTime;
109   if (!this->FileTimeCache->Load(internalDependsFileName,
110                                  internalDependsTime)) {
111     return false;
112   }
113 
114   // Parse dependencies from the stream.  If any dependee is missing
115   // or newer than the depender then dependencies should be
116   // regenerated.
117   bool okay = true;
118   bool dependerExists = false;
119 
120   std::string line;
121   line.reserve(1024);
122   std::string depender;
123   std::string dependee;
124   cmFileTime dependerTime;
125   cmFileTime dependeeTime;
126   std::vector<std::string>* currentDependencies = nullptr;
127 
128   while (std::getline(internalDepends, line)) {
129     // Check if this an empty or a comment line
130     if (line.empty() || line.front() == '#') {
131       continue;
132     }
133     // Drop carriage return character at the end
134     if (line.back() == '\r') {
135       line.pop_back();
136       if (line.empty()) {
137         continue;
138       }
139     }
140     // Check if this a depender line
141     if (line.front() != ' ') {
142       depender = line;
143       dependerExists = this->FileTimeCache->Load(depender, dependerTime);
144       // If we erase validDeps[this->Depender] by overwriting it with an empty
145       // vector, we lose dependencies for dependers that have multiple
146       // entries. No need to initialize the entry, std::map will do so on first
147       // access.
148       currentDependencies = &validDeps[depender];
149       continue;
150     }
151 
152     // This is a dependee line
153     dependee = line.substr(1);
154 
155     // Add dependee to depender's list
156     if (currentDependencies != nullptr) {
157       currentDependencies->push_back(dependee);
158     }
159 
160     // Dependencies must be regenerated
161     // * if the dependee does not exist
162     // * if the depender exists and is older than the dependee.
163     // * if the depender does not exist, but the dependee is newer than the
164     //   depends file
165     bool regenerate = false;
166     bool dependeeExists = this->FileTimeCache->Load(dependee, dependeeTime);
167     if (!dependeeExists) {
168       // The dependee does not exist.
169       regenerate = true;
170 
171       // Print verbose output.
172       if (this->Verbose) {
173         cmSystemTools::Stdout(cmStrCat("Dependee \"", dependee,
174                                        "\" does not exist for depender \"",
175                                        depender, "\".\n"));
176       }
177     } else if (dependerExists) {
178       // The dependee and depender both exist.  Compare file times.
179       if (dependerTime.Older(dependeeTime)) {
180         // The depender is older than the dependee.
181         regenerate = true;
182 
183         // Print verbose output.
184         if (this->Verbose) {
185           cmSystemTools::Stdout(cmStrCat("Dependee \"", dependee,
186                                          "\" is newer than depender \"",
187                                          depender, "\".\n"));
188         }
189       }
190     } else {
191       // The dependee exists, but the depender doesn't. Regenerate if the
192       // internalDepends file is older than the dependee.
193       if (internalDependsTime.Older(dependeeTime)) {
194         // The depends-file is older than the dependee.
195         regenerate = true;
196 
197         // Print verbose output.
198         if (this->Verbose) {
199           cmSystemTools::Stdout(cmStrCat("Dependee \"", dependee,
200                                          "\" is newer than depends file \"",
201                                          internalDependsFileName, "\".\n"));
202         }
203       }
204     }
205 
206     if (regenerate) {
207       // Dependencies must be regenerated.
208       okay = false;
209 
210       // Remove the information of this depender from the map, it needs
211       // to be rescanned
212       if (currentDependencies != nullptr) {
213         validDeps.erase(depender);
214         currentDependencies = nullptr;
215       }
216 
217       // Remove the depender to be sure it is rebuilt.
218       if (dependerExists) {
219         cmSystemTools::RemoveFile(depender);
220         this->FileTimeCache->Remove(depender);
221         dependerExists = false;
222       }
223     }
224   }
225 
226   return okay;
227 }
228 
SetIncludePathFromLanguage(const std::string & lang)229 void cmDepends::SetIncludePathFromLanguage(const std::string& lang)
230 {
231   // Look for the new per "TARGET_" variant first:
232   std::string includePathVar =
233     cmStrCat("CMAKE_", lang, "_TARGET_INCLUDE_PATH");
234   cmMakefile* mf = this->LocalGenerator->GetMakefile();
235   cmValue includePath = mf->GetDefinition(includePathVar);
236   if (includePath) {
237     cmExpandList(*includePath, this->IncludePath);
238   } else {
239     // Fallback to the old directory level variable if no per-target var:
240     includePathVar = cmStrCat("CMAKE_", lang, "_INCLUDE_PATH");
241     includePath = mf->GetDefinition(includePathVar);
242     if (includePath) {
243       cmExpandList(*includePath, this->IncludePath);
244     }
245   }
246 }
247