Imported Upstream version 2.8.9
[platform/upstream/cmake.git] / Source / cmDepends.cxx
1 /*============================================================================
2   CMake - Cross Platform Makefile Generator
3   Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4
5   Distributed under the OSI-approved BSD License (the "License");
6   see accompanying file Copyright.txt for details.
7
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"
13
14 #include "cmLocalGenerator.h"
15 #include "cmMakefile.h"
16 #include "cmGeneratedFileStream.h"
17 #include "cmSystemTools.h"
18 #include "cmFileTimeComparison.h"
19 #include <string.h>
20
21 //----------------------------------------------------------------------------
22 cmDepends::cmDepends(cmLocalGenerator* lg, const char* targetDir):
23   CompileDirectory(),
24   LocalGenerator(lg),
25   Verbose(false),
26   FileComparison(0),
27   TargetDirectory(targetDir),
28   MaxPath(16384),
29   Dependee(new char[MaxPath]),
30   Depender(new char[MaxPath])
31 {
32 }
33
34 //----------------------------------------------------------------------------
35 cmDepends::~cmDepends()
36 {
37   delete [] this->Dependee;
38   delete [] this->Depender;
39 }
40
41 //----------------------------------------------------------------------------
42 bool cmDepends::Write(std::ostream &makeDepends,
43                       std::ostream &internalDepends)
44 {
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);
52
53   for(std::vector<std::string>::iterator si = pairs.begin();
54       si != pairs.end();)
55     {
56     // Get the source and object file.
57     std::string const& src = *si++;
58     if(si == pairs.end()) { break; }
59     std::string obj = *si++;
60
61     // Make sure the object file is relative to the top of the build tree.
62     obj = this->LocalGenerator->Convert(obj.c_str(),
63                                         cmLocalGenerator::HOME_OUTPUT,
64                                         cmLocalGenerator::MAKEFILE);
65
66     // Write the dependencies for this pair.
67     if(!this->WriteDependencies(src.c_str(), obj.c_str(),
68                                 makeDepends, internalDepends))
69       {
70       return false;
71       }
72     }
73
74   return this->Finalize(makeDepends, internalDepends);
75 }
76
77 //----------------------------------------------------------------------------
78 bool cmDepends::Finalize(std::ostream&,
79                          std::ostream&)
80 {
81   return true;
82 }
83
84 //----------------------------------------------------------------------------
85 bool cmDepends::Check(const char *makeFile, const char *internalFile,
86                       std::map<std::string, DependencyVector>& validDeps)
87 {
88   // Dependency checks must be done in proper working directory.
89   std::string oldcwd = ".";
90   if(this->CompileDirectory != ".")
91     {
92     // Get the CWD but do not call CollapseFullPath because
93     // we only need it to cd back, and the form does not matter
94     oldcwd = cmSystemTools::GetCurrentWorkingDirectory(false);
95     cmSystemTools::ChangeDirectory(this->CompileDirectory.c_str());
96     }
97
98   // Check whether dependencies must be regenerated.
99   bool okay = true;
100   std::ifstream fin(internalFile);
101   if(!(fin && this->CheckDependencies(fin, validDeps)))
102     {
103     // Clear all dependencies so they will be regenerated.
104     this->Clear(makeFile);
105     cmSystemTools::RemoveFile(internalFile);
106     okay = false;
107     }
108
109   // Restore working directory.
110   if(oldcwd != ".")
111     {
112     cmSystemTools::ChangeDirectory(oldcwd.c_str());
113     }
114
115   return okay;
116 }
117
118 //----------------------------------------------------------------------------
119 void cmDepends::Clear(const char *file)
120 {
121   // Print verbose output.
122   if(this->Verbose)
123     {
124     cmOStringStream msg;
125     msg << "Clearing dependencies in \"" << file << "\"." << std::endl;
126     cmSystemTools::Stdout(msg.str().c_str());
127     }
128
129   // Write an empty dependency file.
130   cmGeneratedFileStream depFileStream(file);
131   depFileStream
132     << "# Empty dependencies file\n"
133     << "# This may be replaced when dependencies are built." << std::endl;
134 }
135
136 //----------------------------------------------------------------------------
137 bool cmDepends::WriteDependencies(const char*, const char*,
138                                   std::ostream&, std::ostream&)
139 {
140   // This should be implemented by the subclass.
141   return false;
142 }
143
144 //----------------------------------------------------------------------------
145 bool cmDepends::CheckDependencies(std::istream& internalDepends,
146                             std::map<std::string, DependencyVector>& validDeps)
147 {
148   // Parse dependencies from the stream.  If any dependee is missing
149   // or newer than the depender then dependencies should be
150   // regenerated.
151   bool okay = true;
152   bool dependerExists = false;
153   DependencyVector* currentDependencies = 0;
154
155   while(internalDepends.getline(this->Dependee, this->MaxPath))
156     {
157     if ( this->Dependee[0] == 0 || this->Dependee[0] == '#' || 
158          this->Dependee[0] == '\r' )
159       {
160       continue;
161       }
162     size_t len = internalDepends.gcount()-1;
163     if ( this->Dependee[len-1] == '\r' )
164       {
165       len --;
166       this->Dependee[len] = 0;
167       }
168     if ( this->Dependee[0] != ' ' )
169       {
170       memcpy(this->Depender, this->Dependee, len+1);
171       // Calling FileExists() for the depender here saves in many cases 50%
172       // of the calls to FileExists() further down in the loop. E.g. for
173       // kdelibs/khtml this reduces the number of calls from 184k down to 92k,
174       // or the time for cmake -E cmake_depends from 0.3 s down to 0.21 s.
175       dependerExists = cmSystemTools::FileExists(this->Depender);
176       DependencyVector tmp;
177       validDeps[this->Depender] = tmp;
178       currentDependencies = &validDeps[this->Depender];
179       continue;
180       }
181     /*
182     // Parse the dependency line.
183     if(!this->ParseDependency(line.c_str()))
184       {
185       continue;
186       }
187       */
188
189     // Dependencies must be regenerated if the dependee does not exist
190     // or if the depender exists and is older than the dependee.
191     bool regenerate = false;
192     const char* dependee = this->Dependee+1;
193     const char* depender = this->Depender;
194     if (currentDependencies != 0)
195       {
196       currentDependencies->push_back(dependee);
197       }
198
199     if(!cmSystemTools::FileExists(dependee))
200       {
201       // The dependee does not exist.
202       regenerate = true;
203
204       // Print verbose output.
205       if(this->Verbose)
206         {
207         cmOStringStream msg;
208         msg << "Dependee \"" << dependee
209             << "\" does not exist for depender \""
210             << depender << "\"." << std::endl;
211         cmSystemTools::Stdout(msg.str().c_str());
212         }
213       }
214     else if(dependerExists)
215       {
216       // The dependee and depender both exist.  Compare file times.
217       int result = 0;
218       if((!this->FileComparison->FileTimeCompare(depender, dependee,
219                                              &result) || result < 0))
220         {
221         // The depender is older than the dependee.
222         regenerate = true;
223
224         // Print verbose output.
225         if(this->Verbose)
226           {
227           cmOStringStream msg;
228           msg << "Dependee \"" << dependee
229               << "\" is newer than depender \""
230               << depender << "\"." << std::endl;
231           cmSystemTools::Stdout(msg.str().c_str());
232           }
233         }
234       }
235     if(regenerate)
236       {
237       // Dependencies must be regenerated.
238       okay = false;
239
240       // Remove the information of this depender from the map, it needs
241       // to be rescanned
242       if (currentDependencies != 0)
243         {
244         validDeps.erase(this->Depender);
245         currentDependencies = 0;
246         }
247
248       // Remove the depender to be sure it is rebuilt.
249       if (dependerExists)
250         {
251         cmSystemTools::RemoveFile(depender);
252         dependerExists = false;
253         }
254       }
255     }
256
257   return okay;
258 }
259
260 //----------------------------------------------------------------------------
261 void cmDepends::SetIncludePathFromLanguage(const char* lang)
262 {
263   // Look for the new per "TARGET_" variant first:
264   const char * includePath = 0;
265   std::string includePathVar = "CMAKE_";
266   includePathVar += lang;
267   includePathVar += "_TARGET_INCLUDE_PATH";
268   cmMakefile* mf = this->LocalGenerator->GetMakefile();
269   includePath = mf->GetDefinition(includePathVar.c_str());
270   if(includePath)
271     {
272     cmSystemTools::ExpandListArgument(includePath, this->IncludePath);
273     }
274   else
275     {
276     // Fallback to the old directory level variable if no per-target var:
277     includePathVar = "CMAKE_";
278     includePathVar += lang;
279     includePathVar += "_INCLUDE_PATH";
280     includePath = mf->GetDefinition(includePathVar.c_str());
281     if(includePath)
282       {
283       cmSystemTools::ExpandListArgument(includePath, this->IncludePath);
284       }
285     }
286 }