cmDepends.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 13
#include "cmDepends.h"

14 15
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
16 17
#include "cmGeneratedFileStream.h"
#include "cmSystemTools.h"
18
#include "cmFileTimeComparison.h"
Bill Hoffman's avatar
Bill Hoffman committed
19
#include <string.h>
20
#include <cmsys/FStream.hxx>
21

22
//----------------------------------------------------------------------------
23
cmDepends::cmDepends(cmLocalGenerator* lg, const char* targetDir):
24
  CompileDirectory(),
25
  LocalGenerator(lg),
26 27
  Verbose(false),
  FileComparison(0),
28
  TargetDirectory(targetDir),
29
  MaxPath(16384),
Ken Martin's avatar
Ken Martin committed
30 31
  Dependee(new char[MaxPath]),
  Depender(new char[MaxPath])
32
{
33 34 35 36 37
}

//----------------------------------------------------------------------------
cmDepends::~cmDepends()
{
Ken Martin's avatar
Ken Martin committed
38 39
  delete [] this->Dependee;
  delete [] this->Depender;
40 41
}

42
//----------------------------------------------------------------------------
43 44
bool cmDepends::Write(std::ostream &makeDepends,
                      std::ostream &internalDepends)
45
{
46 47 48 49
  // Lookup the set of sources to scan.
  std::string srcLang = "CMAKE_DEPENDS_CHECK_";
  srcLang += this->Language;
  cmMakefile* mf = this->LocalGenerator->GetMakefile();
Stephen Kelly's avatar
Stephen Kelly committed
50
  const char* srcStr = mf->GetSafeDefinition(srcLang);
51 52 53
  std::vector<std::string> pairs;
  cmSystemTools::ExpandListArgument(srcStr, pairs);

54
  std::map<std::string, std::set<std::string> > dependencies;
55 56 57 58 59 60
  for(std::vector<std::string>::iterator si = pairs.begin();
      si != pairs.end();)
    {
    // Get the source and object file.
    std::string const& src = *si++;
    if(si == pairs.end()) { break; }
61
    std::string const& obj = *si++;
62 63 64 65 66
    dependencies[obj].insert(src);
    }
  for(std::map<std::string, std::set<std::string> >::const_iterator
      it = dependencies.begin(); it != dependencies.end(); ++it)
    {
67 68

    // Write the dependencies for this pair.
69
    if(!this->WriteDependencies(it->second, it->first,
70 71 72 73 74 75
                                makeDepends, internalDepends))
      {
      return false;
      }
    }

76 77 78 79 80 81 82
  return this->Finalize(makeDepends, internalDepends);
}

//----------------------------------------------------------------------------
bool cmDepends::Finalize(std::ostream&,
                         std::ostream&)
{
83
  return true;
84 85 86
}

//----------------------------------------------------------------------------
87 88
bool cmDepends::Check(const char *makeFile, const char *internalFile,
                      std::map<std::string, DependencyVector>& validDeps)
89
{
90 91
  // Dependency checks must be done in proper working directory.
  std::string oldcwd = ".";
Ken Martin's avatar
Ken Martin committed
92
  if(this->CompileDirectory != ".")
93
    {
94 95 96
    // Get the CWD but do not call CollapseFullPath because
    // we only need it to cd back, and the form does not matter
    oldcwd = cmSystemTools::GetCurrentWorkingDirectory(false);
97
    cmSystemTools::ChangeDirectory(this->CompileDirectory);
98 99
    }

100
  // Check whether dependencies must be regenerated.
101
  bool okay = true;
102
  cmsys::ifstream fin(internalFile);
103
  if(!(fin && this->CheckDependencies(fin, internalFile, validDeps)))
104 105
    {
    // Clear all dependencies so they will be regenerated.
106
    this->Clear(makeFile);
107 108
    cmSystemTools::RemoveFile(internalFile);
    okay = false;
109
    }
110 111 112 113

  // Restore working directory.
  if(oldcwd != ".")
    {
114
    cmSystemTools::ChangeDirectory(oldcwd);
115
    }
116 117

  return okay;
118 119 120
}

//----------------------------------------------------------------------------
121
void cmDepends::Clear(const char *file)
122
{
123
  // Print verbose output.
Ken Martin's avatar
Ken Martin committed
124
  if(this->Verbose)
125
    {
126
    std::ostringstream msg;
127
    msg << "Clearing dependencies in \"" << file << "\"." << std::endl;
128 129 130
    cmSystemTools::Stdout(msg.str().c_str());
    }

131
  // Write an empty dependency file.
132
  cmGeneratedFileStream depFileStream(file);
133
  depFileStream
134
    << "# Empty dependencies file\n"
135 136 137
    << "# This may be replaced when dependencies are built." << std::endl;
}

138
//----------------------------------------------------------------------------
139 140 141
bool cmDepends::WriteDependencies(
    const std::set<std::string>&, const std::string&,
    std::ostream&, std::ostream&)
142 143 144 145 146
{
  // This should be implemented by the subclass.
  return false;
}

147
//----------------------------------------------------------------------------
148
bool cmDepends::CheckDependencies(std::istream& internalDepends,
149
                                  const char* internalDependsFileName,
150
                            std::map<std::string, DependencyVector>& validDeps)
151 152 153 154 155
{
  // Parse dependencies from the stream.  If any dependee is missing
  // or newer than the depender then dependencies should be
  // regenerated.
  bool okay = true;
156
  bool dependerExists = false;
157 158
  DependencyVector* currentDependencies = 0;

Ken Martin's avatar
Ken Martin committed
159
  while(internalDepends.getline(this->Dependee, this->MaxPath))
160
    {
161
    if ( this->Dependee[0] == 0 || this->Dependee[0] == '#' ||
Ken Martin's avatar
Ken Martin committed
162
         this->Dependee[0] == '\r' )
163 164 165 166
      {
      continue;
      }
    size_t len = internalDepends.gcount()-1;
Ken Martin's avatar
Ken Martin committed
167
    if ( this->Dependee[len-1] == '\r' )
168 169
      {
      len --;
Ken Martin's avatar
Ken Martin committed
170
      this->Dependee[len] = 0;
171
      }
Ken Martin's avatar
Ken Martin committed
172
    if ( this->Dependee[0] != ' ' )
173
      {
Ken Martin's avatar
Ken Martin committed
174
      memcpy(this->Depender, this->Dependee, len+1);
175 176 177 178 179
      // Calling FileExists() for the depender here saves in many cases 50%
      // of the calls to FileExists() further down in the loop. E.g. for
      // kdelibs/khtml this reduces the number of calls from 184k down to 92k,
      // or the time for cmake -E cmake_depends from 0.3 s down to 0.21 s.
      dependerExists = cmSystemTools::FileExists(this->Depender);
180 181 182 183
      // If we erase validDeps[this->Depender] by overwriting it with an empty
      // vector, we lose dependencies for dependers that have multiple
      // entries. No need to initialize the entry, std::map will do so on first
      // access.
184
      currentDependencies = &validDeps[this->Depender];
185 186 187 188 189 190 191 192 193 194
      continue;
      }
    /*
    // Parse the dependency line.
    if(!this->ParseDependency(line.c_str()))
      {
      continue;
      }
      */

195 196 197 198 199
    // Dependencies must be regenerated
    // * if the dependee does not exist
    // * if the depender exists and is older than the dependee.
    // * if the depender does not exist, but the dependee is newer than the
    //   depends file
200
    bool regenerate = false;
Ken Martin's avatar
Ken Martin committed
201 202
    const char* dependee = this->Dependee+1;
    const char* depender = this->Depender;
203 204 205 206 207
    if (currentDependencies != 0)
      {
      currentDependencies->push_back(dependee);
      }

208 209 210 211 212 213
    if(!cmSystemTools::FileExists(dependee))
      {
      // The dependee does not exist.
      regenerate = true;

      // Print verbose output.
Ken Martin's avatar
Ken Martin committed
214
      if(this->Verbose)
215
        {
216
        std::ostringstream msg;
217 218 219 220 221 222
        msg << "Dependee \"" << dependee
            << "\" does not exist for depender \""
            << depender << "\"." << std::endl;
        cmSystemTools::Stdout(msg.str().c_str());
        }
      }
223
    else
224
      {
225
      if(dependerExists)
226
        {
227 228 229 230 231 232 233
        // The dependee and depender both exist.  Compare file times.
        int result = 0;
        if((!this->FileComparison->FileTimeCompare(depender, dependee,
                                              &result) || result < 0))
          {
          // The depender is older than the dependee.
          regenerate = true;
234

235 236 237
          // Print verbose output.
          if(this->Verbose)
            {
238
            std::ostringstream msg;
239 240 241 242 243 244 245 246 247 248 249 250
            msg << "Dependee \"" << dependee
                << "\" is newer than depender \""
                << depender << "\"." << std::endl;
            cmSystemTools::Stdout(msg.str().c_str());
            }
          }
        }
      else
        {
        // The dependee exists, but the depender doesn't. Regenerate if the
        // internalDepends file is older than the dependee.
        int result = 0;
Alexander Neundorf's avatar
Alexander Neundorf committed
251 252
        if((!this->FileComparison->FileTimeCompare(internalDependsFileName,
                                             dependee, &result) || result < 0))
253
          {
254 255 256 257 258 259
          // The depends-file is older than the dependee.
          regenerate = true;

          // Print verbose output.
          if(this->Verbose)
            {
260
            std::ostringstream msg;
261 262 263 264 265
            msg << "Dependee \"" << dependee
                << "\" is newer than depends file \""
                << internalDependsFileName << "\"." << std::endl;
            cmSystemTools::Stdout(msg.str().c_str());
            }
266 267 268 269 270 271 272 273
          }
        }
      }
    if(regenerate)
      {
      // Dependencies must be regenerated.
      okay = false;

274 275 276 277 278 279 280 281
      // Remove the information of this depender from the map, it needs
      // to be rescanned
      if (currentDependencies != 0)
        {
        validDeps.erase(this->Depender);
        currentDependencies = 0;
        }

282
      // Remove the depender to be sure it is rebuilt.
283 284 285 286 287
      if (dependerExists)
        {
        cmSystemTools::RemoveFile(depender);
        dependerExists = false;
        }
288 289 290 291 292 293
      }
    }

  return okay;
}

294
//----------------------------------------------------------------------------
295
void cmDepends::SetIncludePathFromLanguage(const std::string& lang)
296
{
297
  // Look for the new per "TARGET_" variant first:
298
  const char * includePath = 0;
299 300
  std::string includePathVar = "CMAKE_";
  includePathVar += lang;
301
  includePathVar += "_TARGET_INCLUDE_PATH";
302
  cmMakefile* mf = this->LocalGenerator->GetMakefile();
Stephen Kelly's avatar
Stephen Kelly committed
303
  includePath = mf->GetDefinition(includePathVar);
304
  if(includePath)
305 306 307
    {
    cmSystemTools::ExpandListArgument(includePath, this->IncludePath);
    }
308 309 310 311 312 313
  else
    {
    // Fallback to the old directory level variable if no per-target var:
    includePathVar = "CMAKE_";
    includePathVar += lang;
    includePathVar += "_INCLUDE_PATH";
Stephen Kelly's avatar
Stephen Kelly committed
314
    includePath = mf->GetDefinition(includePathVar);
315
    if(includePath)
316 317 318 319
      {
      cmSystemTools::ExpandListArgument(includePath, this->IncludePath);
      }
    }
320
}