1 /*============================================================================
2 CMake - Cross Platform Makefile Generator
3 Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
5 Distributed under the OSI-approved BSD License (the "License");
6 see accompanying file Copyright.txt for details.
8 This software is distributed WITHOUT ANY WARRANTY; without even the
9 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 See the License for more information.
11 ============================================================================*/
12 #include "cmDepends.h"
14 #include "cmLocalGenerator.h"
15 #include "cmMakefile.h"
16 #include "cmGeneratedFileStream.h"
17 #include "cmSystemTools.h"
18 #include "cmFileTimeComparison.h"
21 //----------------------------------------------------------------------------
22 cmDepends::cmDepends(cmLocalGenerator* lg, const char* targetDir):
27 TargetDirectory(targetDir),
29 Dependee(new char[MaxPath]),
30 Depender(new char[MaxPath])
34 //----------------------------------------------------------------------------
35 cmDepends::~cmDepends()
37 delete [] this->Dependee;
38 delete [] this->Depender;
41 //----------------------------------------------------------------------------
42 bool cmDepends::Write(std::ostream &makeDepends,
43 std::ostream &internalDepends)
45 // Lookup the set of sources to scan.
46 std::string srcLang = "CMAKE_DEPENDS_CHECK_";
47 srcLang += this->Language;
48 cmMakefile* mf = this->LocalGenerator->GetMakefile();
49 const char* srcStr = mf->GetSafeDefinition(srcLang.c_str());
50 std::vector<std::string> pairs;
51 cmSystemTools::ExpandListArgument(srcStr, pairs);
53 std::map<std::string, std::set<std::string> > dependencies;
54 for(std::vector<std::string>::iterator si = pairs.begin();
57 // Get the source and object file.
58 std::string const& src = *si++;
59 if(si == pairs.end()) { break; }
60 std::string obj = *si++;
62 // Make sure the object file is relative to the top of the build tree.
63 obj = this->LocalGenerator->Convert(obj.c_str(),
64 cmLocalGenerator::HOME_OUTPUT,
65 cmLocalGenerator::MAKEFILE);
66 dependencies[obj].insert(src);
68 for(std::map<std::string, std::set<std::string> >::const_iterator
69 it = dependencies.begin(); it != dependencies.end(); ++it)
72 // Write the dependencies for this pair.
73 if(!this->WriteDependencies(it->second, it->first,
74 makeDepends, internalDepends))
80 return this->Finalize(makeDepends, internalDepends);
83 //----------------------------------------------------------------------------
84 bool cmDepends::Finalize(std::ostream&,
90 //----------------------------------------------------------------------------
91 bool cmDepends::Check(const char *makeFile, const char *internalFile,
92 std::map<std::string, DependencyVector>& validDeps)
94 // Dependency checks must be done in proper working directory.
95 std::string oldcwd = ".";
96 if(this->CompileDirectory != ".")
98 // Get the CWD but do not call CollapseFullPath because
99 // we only need it to cd back, and the form does not matter
100 oldcwd = cmSystemTools::GetCurrentWorkingDirectory(false);
101 cmSystemTools::ChangeDirectory(this->CompileDirectory.c_str());
104 // Check whether dependencies must be regenerated.
106 std::ifstream fin(internalFile);
107 if(!(fin && this->CheckDependencies(fin, internalFile, validDeps)))
109 // Clear all dependencies so they will be regenerated.
110 this->Clear(makeFile);
111 cmSystemTools::RemoveFile(internalFile);
115 // Restore working directory.
118 cmSystemTools::ChangeDirectory(oldcwd.c_str());
124 //----------------------------------------------------------------------------
125 void cmDepends::Clear(const char *file)
127 // Print verbose output.
131 msg << "Clearing dependencies in \"" << file << "\"." << std::endl;
132 cmSystemTools::Stdout(msg.str().c_str());
135 // Write an empty dependency file.
136 cmGeneratedFileStream depFileStream(file);
138 << "# Empty dependencies file\n"
139 << "# This may be replaced when dependencies are built." << std::endl;
142 //----------------------------------------------------------------------------
143 bool cmDepends::WriteDependencies(
144 const std::set<std::string>&, const std::string&,
145 std::ostream&, std::ostream&)
147 // This should be implemented by the subclass.
151 //----------------------------------------------------------------------------
152 bool cmDepends::CheckDependencies(std::istream& internalDepends,
153 const char* internalDependsFileName,
154 std::map<std::string, DependencyVector>& validDeps)
156 // Parse dependencies from the stream. If any dependee is missing
157 // or newer than the depender then dependencies should be
160 bool dependerExists = false;
161 DependencyVector* currentDependencies = 0;
163 while(internalDepends.getline(this->Dependee, this->MaxPath))
165 if ( this->Dependee[0] == 0 || this->Dependee[0] == '#' ||
166 this->Dependee[0] == '\r' )
170 size_t len = internalDepends.gcount()-1;
171 if ( this->Dependee[len-1] == '\r' )
174 this->Dependee[len] = 0;
176 if ( this->Dependee[0] != ' ' )
178 memcpy(this->Depender, this->Dependee, len+1);
179 // Calling FileExists() for the depender here saves in many cases 50%
180 // of the calls to FileExists() further down in the loop. E.g. for
181 // kdelibs/khtml this reduces the number of calls from 184k down to 92k,
182 // or the time for cmake -E cmake_depends from 0.3 s down to 0.21 s.
183 dependerExists = cmSystemTools::FileExists(this->Depender);
184 // If we erase validDeps[this->Depender] by overwriting it with an empty
185 // vector, we lose dependencies for dependers that have multiple
186 // entries. No need to initialize the entry, std::map will do so on first
188 currentDependencies = &validDeps[this->Depender];
192 // Parse the dependency line.
193 if(!this->ParseDependency(line.c_str()))
199 // Dependencies must be regenerated
200 // * if the dependee does not exist
201 // * if the depender exists and is older than the dependee.
202 // * if the depender does not exist, but the dependee is newer than the
204 bool regenerate = false;
205 const char* dependee = this->Dependee+1;
206 const char* depender = this->Depender;
207 if (currentDependencies != 0)
209 currentDependencies->push_back(dependee);
212 if(!cmSystemTools::FileExists(dependee))
214 // The dependee does not exist.
217 // Print verbose output.
221 msg << "Dependee \"" << dependee
222 << "\" does not exist for depender \""
223 << depender << "\"." << std::endl;
224 cmSystemTools::Stdout(msg.str().c_str());
231 // The dependee and depender both exist. Compare file times.
233 if((!this->FileComparison->FileTimeCompare(depender, dependee,
234 &result) || result < 0))
236 // The depender is older than the dependee.
239 // Print verbose output.
243 msg << "Dependee \"" << dependee
244 << "\" is newer than depender \""
245 << depender << "\"." << std::endl;
246 cmSystemTools::Stdout(msg.str().c_str());
252 // The dependee exists, but the depender doesn't. Regenerate if the
253 // internalDepends file is older than the dependee.
255 if((!this->FileComparison->FileTimeCompare(internalDependsFileName,
256 dependee, &result) || result < 0))
258 // The depends-file is older than the dependee.
261 // Print verbose output.
265 msg << "Dependee \"" << dependee
266 << "\" is newer than depends file \""
267 << internalDependsFileName << "\"." << std::endl;
268 cmSystemTools::Stdout(msg.str().c_str());
275 // Dependencies must be regenerated.
278 // Remove the information of this depender from the map, it needs
280 if (currentDependencies != 0)
282 validDeps.erase(this->Depender);
283 currentDependencies = 0;
286 // Remove the depender to be sure it is rebuilt.
289 cmSystemTools::RemoveFile(depender);
290 dependerExists = false;
298 //----------------------------------------------------------------------------
299 void cmDepends::SetIncludePathFromLanguage(const char* lang)
301 // Look for the new per "TARGET_" variant first:
302 const char * includePath = 0;
303 std::string includePathVar = "CMAKE_";
304 includePathVar += lang;
305 includePathVar += "_TARGET_INCLUDE_PATH";
306 cmMakefile* mf = this->LocalGenerator->GetMakefile();
307 includePath = mf->GetDefinition(includePathVar.c_str());
310 cmSystemTools::ExpandListArgument(includePath, this->IncludePath);
314 // Fallback to the old directory level variable if no per-target var:
315 includePathVar = "CMAKE_";
316 includePathVar += lang;
317 includePathVar += "_INCLUDE_PATH";
318 includePath = mf->GetDefinition(includePathVar.c_str());
321 cmSystemTools::ExpandListArgument(includePath, this->IncludePath);