cmMakeDepend.cxx 10.1 KB
Newer Older
1
2
3
/*============================================================================
  CMake - Cross Platform Makefile Generator
  Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4

5
6
  Distributed under the OSI-approved BSD License (the "License");
  see accompanying file Copyright.txt for details.
7

8
9
10
11
  This software is distributed WITHOUT ANY WARRANTY; without even the
  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the License for more information.
============================================================================*/
12
#include "cmMakeDepend.h"
13
#include "cmSystemTools.h"
14
#include "cmGeneratorExpression.h"
15

16
#include <cmsys/RegularExpression.hxx>
17
#include <cmsys/FStream.hxx>
18
19
20
21
22

void cmDependInformation::AddDependencies(cmDependInformation* info)
{
  if(this != info)
    {
Ken Martin's avatar
Ken Martin committed
23
    this->DependencySet.insert(info);
24
25
26
    }
}

27
28
cmMakeDepend::cmMakeDepend()
{
Ken Martin's avatar
Ken Martin committed
29
30
31
  this->Verbose = false;
  this->IncludeFileRegularExpression.compile("^.*$");
  this->ComplainFileRegularExpression.compile("^$");
32
33
34
}


35
cmMakeDepend::~cmMakeDepend()
36
37
{
  for(DependInformationMapType::iterator i =
Ken Martin's avatar
Ken Martin committed
38
39
        this->DependInformationMap.begin();
      i != this->DependInformationMap.end(); ++i)
40
    {
41
    delete i->second;
42
43
44
45
46
    }
}


// Set the makefile that depends will be made from.
47
// The pointer is kept so the cmSourceFile array can
48
// be updated with the depend information in the cmMakefile.
49

50
void cmMakeDepend::SetMakefile(cmMakefile* makefile)
51
{
Ken Martin's avatar
Ken Martin committed
52
  this->Makefile = makefile;
53
54

  // Now extract the include file regular expression from the makefile.
Ken Martin's avatar
Ken Martin committed
55
56
57
58
  this->IncludeFileRegularExpression.compile(
    this->Makefile->IncludeFileRegularExpression.c_str());
  this->ComplainFileRegularExpression.compile(
    this->Makefile->ComplainFileRegularExpression.c_str());
59
60

  // Now extract any include paths from the targets
61
62
  std::set<std::string> uniqueIncludes;
  std::vector<std::string> orderedAndUniqueIncludes;
63
64
  cmTargets &targets = this->Makefile->GetTargets();
  for (cmTargets::iterator l = targets.begin();
65
       l != targets.end(); ++l)
66
    {
67
68
69
70
71
72
73
74
75
76
77
78
    const char *incDirProp = l->second.GetProperty("INCLUDE_DIRECTORIES");
    if (!incDirProp)
      {
      continue;
      }

    std::string incDirs = cmGeneratorExpression::Preprocess(incDirProp,
                      cmGeneratorExpression::StripAllGeneratorExpressions);

    std::vector<std::string> includes;
    cmSystemTools::ExpandListArgument(incDirs.c_str(), includes);

79
80
81
82
83
    for(std::vector<std::string>::const_iterator j = includes.begin();
        j != includes.end(); ++j)
      {
      std::string path = *j;
      this->Makefile->ExpandVariablesInString(path);
84
85
86
87
      if(uniqueIncludes.insert(path).second)
        {
        orderedAndUniqueIncludes.push_back(path);
        }
88
      }
89
    }
90
91
92
93
94
95
96
97

  for(std::vector<std::string>::const_iterator
    it = orderedAndUniqueIncludes.begin();
    it != orderedAndUniqueIncludes.end();
    ++it)
    {
    this->AddSearchPath(it->c_str());
    }
98
99
100
}


101
const cmDependInformation* cmMakeDepend::FindDependencies(const char* file)
102
{
103
  cmDependInformation* info = this->GetDependInformation(file,0);
104
105
  this->GenerateDependInformation(info);
  return info;
106
107
}

108
void cmMakeDepend::GenerateDependInformation(cmDependInformation* info)
109
{
110
  // If dependencies are already done, stop now.
Ken Martin's avatar
Ken Martin committed
111
  if(info->DependDone)
112
    {
113
    return;
114
    }
115
  else
116
    {
117
    // Make sure we don't visit the same file more than once.
Ken Martin's avatar
Ken Martin committed
118
    info->DependDone = true;
119
    }
Ken Martin's avatar
Ken Martin committed
120
  const char* path = info->FullPath.c_str();
121
122
  if(!path)
    {
Andy Cedilnik's avatar
Andy Cedilnik committed
123
124
    cmSystemTools::Error(
      "Attempt to find dependencies for file without path!");
125
126
    return;
    }
127

128
  bool found = false;
129

130
  // If the file exists, use it to find dependency information.
131
  if(cmSystemTools::FileExists(path, true))
132
133
    {
    // Use the real file to find its dependencies.
134
    this->DependWalk(info);
135
    found = true;
136
    }
137

Andy Cedilnik's avatar
Andy Cedilnik committed
138

139
140
  // See if the cmSourceFile for it has any files specified as
  // dependency hints.
141
  if(info->SourceFile != 0)
142
    {
143

144
    // Get the cmSourceFile corresponding to this.
145
    const cmSourceFile& cFile = *(info->SourceFile);
146
147
    // See if there are any hints for finding dependencies for the missing
    // file.
148
    if(!cFile.GetDepends().empty())
149
      {
150
      // Dependency hints have been given.  Use them to begin the
151
      // recursion.
152
      for(std::vector<std::string>::const_iterator file =
Andy Cedilnik's avatar
Andy Cedilnik committed
153
            cFile.GetDepends().begin(); file != cFile.GetDepends().end();
154
          ++file)
155
156
157
        {
        this->AddDependency(info, file->c_str());
        }
Andy Cedilnik's avatar
Andy Cedilnik committed
158

159
      // Found dependency information.  We are done.
160
      found = true;
161
      }
162
    }
163
164
165
166

  if(!found)
    {
    // Try to find the file amongst the sources
Ken Martin's avatar
Ken Martin committed
167
168
    cmSourceFile *srcFile = this->Makefile->GetSource
      (cmSystemTools::GetFilenameWithoutExtension(path).c_str());
169
    if (srcFile)
170
      {
171
      if (srcFile->GetFullPath() == path)
172
        {
173
174
175
176
177
        found=true;
        }
      else
        {
        //try to guess which include path to use
178
        for(std::vector<std::string>::iterator t =
Ken Martin's avatar
Ken Martin committed
179
180
              this->IncludeDirectories.begin();
            t != this->IncludeDirectories.end(); ++t)
181
182
          {
          std::string incpath = *t;
183
184
185
186
          if (incpath.size() && incpath[incpath.size() - 1] != '/')
            {
            incpath = incpath + "/";
            }
187
188
          incpath = incpath + path;
          if (srcFile->GetFullPath() == incpath)
189
            {
190
            // set the path to the guessed path
191
            info->FullPath = incpath;
192
            found=true;
193
194
195
196
197
            }
          }
        }
      }
    }
Andy Cedilnik's avatar
Andy Cedilnik committed
198

199
  if(!found)
200
    {
201
    // Couldn't find any dependency information.
Ken Martin's avatar
Ken Martin committed
202
    if(this->ComplainFileRegularExpression.find(info->IncludeName.c_str()))
203
204
205
206
207
208
209
      {
      cmSystemTools::Error("error cannot find dependencies for ", path);
      }
    else
      {
      // Destroy the name of the file so that it won't be output as a
      // dependency.
Ken Martin's avatar
Ken Martin committed
210
      info->FullPath = "";
211
      }
212
    }
213
214
215
216
}

// This function actually reads the file specified and scans it for
// #include directives
217
void cmMakeDepend::DependWalk(cmDependInformation* info)
218
{
Ken Martin's avatar
Ken Martin committed
219
220
  cmsys::RegularExpression includeLine
    ("^[ \t]*#[ \t]*include[ \t]*[<\"]([^\">]+)[\">]");
221
  cmsys::ifstream fin(info->FullPath.c_str());
222
223
  if(!fin)
    {
Ken Martin's avatar
Ken Martin committed
224
    cmSystemTools::Error("Cannot open ", info->FullPath.c_str());
225
226
    return;
    }
227
228

  // TODO: Write real read loop (see cmSystemTools::CopyFile).
229
230
  std::string line;
  while( cmSystemTools::GetLineFromStream(fin, line) )
231
    {
232
    if(includeLine.find(line.c_str()))
233
234
      {
      // extract the file being included
235
      std::string includeFile = includeLine.match(1);
236
      // see if the include matches the regular expression
Ken Martin's avatar
Ken Martin committed
237
      if(!this->IncludeFileRegularExpression.find(includeFile))
238
        {
Ken Martin's avatar
Ken Martin committed
239
        if(this->Verbose)
240
          {
241
242
243
          std::string message = "Skipping ";
          message += includeFile;
          message += " for file ";
Ken Martin's avatar
Ken Martin committed
244
          message += info->FullPath.c_str();
245
246
247
248
          cmSystemTools::Error(message.c_str(), 0);
          }
        continue;
        }
Andy Cedilnik's avatar
Andy Cedilnik committed
249

250
251
      // Add this file and all its dependencies.
      this->AddDependency(info, includeFile.c_str());
252
253
      }
    }
254
255
256
257
258
}


void cmMakeDepend::AddDependency(cmDependInformation* info, const char* file)
{
259
  cmDependInformation* dependInfo =
Ken Martin's avatar
Ken Martin committed
260
    this->GetDependInformation(file, info->PathOnly.c_str());
261
262
  this->GenerateDependInformation(dependInfo);
  info->AddDependencies(dependInfo);
263
264
}

265
266
cmDependInformation* cmMakeDepend::GetDependInformation(const char* file,
                                                        const char *extraPath)
267
{
268
  // Get the full path for the file so that lookup is unambiguous.
269
  std::string fullPath = this->FullPath(file, extraPath);
Andy Cedilnik's avatar
Andy Cedilnik committed
270

271
  // Try to find the file's instance of cmDependInformation.
Ken Martin's avatar
Ken Martin committed
272
273
274
  DependInformationMapType::const_iterator result =
    this->DependInformationMap.find(fullPath);
  if(result != this->DependInformationMap.end())
275
    {
276
277
278
279
280
281
282
    // Found an instance, return it.
    return result->second;
    }
  else
    {
    // Didn't find an instance.  Create a new one and save it.
    cmDependInformation* info = new cmDependInformation;
Ken Martin's avatar
Ken Martin committed
283
284
285
286
    info->FullPath = fullPath;
    info->PathOnly = cmSystemTools::GetFilenamePath(fullPath.c_str());
    info->IncludeName = file;
    this->DependInformationMap[fullPath] = info;
287
    return info;
288
289
290
291
    }
}


Ken Martin's avatar
Ken Martin committed
292
// find the full path to fname by searching the this->IncludeDirectories array
293
std::string cmMakeDepend::FullPath(const char* fname, const char *extraPath)
294
{
Ken Martin's avatar
Ken Martin committed
295
  DirectoryToFileToPathMapType::iterator m;
296
297
  if(extraPath)
    {
Ken Martin's avatar
Ken Martin committed
298
    m = this->DirectoryToFileToPathMap.find(extraPath);
299
300
301
    }
  else
    {
Ken Martin's avatar
Ken Martin committed
302
    m = this->DirectoryToFileToPathMap.find("");
303
    }
304

Ken Martin's avatar
Ken Martin committed
305
  if(m != this->DirectoryToFileToPathMap.end())
306
    {
Ken Martin's avatar
Ken Martin committed
307
308
    FileToPathMapType& map = m->second;
    FileToPathMapType::iterator p = map.find(fname);
309
310
311
312
313
314
    if(p != map.end())
      {
      return p->second;
      }
    }

315
  if(cmSystemTools::FileExists(fname, true))
316
    {
317
    std::string fp = cmSystemTools::CollapseFullPath(fname);
Ken Martin's avatar
Ken Martin committed
318
    this->DirectoryToFileToPathMap[extraPath? extraPath: ""][fname] = fp;
319
    return fp;
320
    }
321

Ken Martin's avatar
Ken Martin committed
322
323
  for(std::vector<std::string>::iterator i = this->IncludeDirectories.begin();
      i != this->IncludeDirectories.end(); ++i)
324
325
    {
    std::string path = *i;
326
327
328
329
    if (path.size() && path[path.size() - 1] != '/')
      {
      path = path + "/";
      }
330
    path = path + fname;
331
    if(cmSystemTools::FileExists(path.c_str(), true)
332
       && !cmSystemTools::FileIsDirectory(path.c_str()))
333
      {
334
      std::string fp = cmSystemTools::CollapseFullPath(path.c_str());
Ken Martin's avatar
Ken Martin committed
335
      this->DirectoryToFileToPathMap[extraPath? extraPath: ""][fname] = fp;
336
      return fp;
337
338
      }
    }
339

340
341
342
  if (extraPath)
    {
    std::string path = extraPath;
343
344
345
346
    if (path.size() && path[path.size() - 1] != '/')
      {
      path = path + "/";
      }
347
    path = path + fname;
348
    if(cmSystemTools::FileExists(path.c_str(), true)
349
       && !cmSystemTools::FileIsDirectory(path.c_str()))
350
      {
351
      std::string fp = cmSystemTools::CollapseFullPath(path.c_str());
Ken Martin's avatar
Ken Martin committed
352
      this->DirectoryToFileToPathMap[extraPath][fname] = fp;
353
      return fp;
354
355
      }
    }
Andy Cedilnik's avatar
Andy Cedilnik committed
356

357
  // Couldn't find the file.
358
359
360
361
  return std::string(fname);
}

// Add a directory to the search path
362
void cmMakeDepend::AddSearchPath(const std::string& path)
363
{
Ken Martin's avatar
Ken Martin committed
364
  this->IncludeDirectories.push_back(path);
365
}