cmDepends.cxx 8.26 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3 4
#include "cmDepends.h"

5 6
#include "cmFileTimeComparison.h"
#include "cmGeneratedFileStream.h"
7 8
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
9
#include "cmSystemTools.h"
10

11
#include "cmsys/FStream.hxx"
12
#include <sstream>
13
#include <string.h>
14
#include <utility>
15

wahikihiki's avatar
wahikihiki committed
16
cmDepends::cmDepends(cmLocalGenerator* lg, std::string targetDir)
17
  : LocalGenerator(lg)
wahikihiki's avatar
wahikihiki committed
18
  , TargetDirectory(std::move(targetDir))
19 20
  , Dependee(new char[MaxPath])
  , Depender(new char[MaxPath])
21
{
22 23 24 25
}

cmDepends::~cmDepends()
{
26 27
  delete[] this->Dependee;
  delete[] this->Depender;
28 29
}

30
bool cmDepends::Write(std::ostream& makeDepends, std::ostream& internalDepends)
31
{
32 33 34 35
  // Lookup the set of sources to scan.
  std::string srcLang = "CMAKE_DEPENDS_CHECK_";
  srcLang += this->Language;
  cmMakefile* mf = this->LocalGenerator->GetMakefile();
36
  std::string const& srcStr = mf->GetSafeDefinition(srcLang);
37 38 39
  std::vector<std::string> pairs;
  cmSystemTools::ExpandListArgument(srcStr, pairs);

40
  std::map<std::string, std::set<std::string>> dependencies;
41 42
  for (std::vector<std::string>::iterator si = pairs.begin();
       si != pairs.end();) {
43 44
    // Get the source and object file.
    std::string const& src = *si++;
45 46 47
    if (si == pairs.end()) {
      break;
    }
48
    std::string const& obj = *si++;
49
    dependencies[obj].insert(src);
50
  }
51
  for (auto const& d : dependencies) {
52 53

    // Write the dependencies for this pair.
54
    if (!this->WriteDependencies(d.second, d.first, makeDepends,
55
                                 internalDepends)) {
56 57
      return false;
    }
58
  }
59

60 61 62
  return this->Finalize(makeDepends, internalDepends);
}

63
bool cmDepends::Finalize(std::ostream& /*unused*/, std::ostream& /*unused*/)
64
{
65
  return true;
66 67
}

68 69
bool cmDepends::Check(const std::string& makeFile,
                      const std::string& internalFile,
70
                      std::map<std::string, DependencyVector>& validDeps)
71 72
{
  // Check whether dependencies must be regenerated.
73
  bool okay = true;
74
  cmsys::ifstream fin(internalFile.c_str());
75
  if (!(fin && this->CheckDependencies(fin, internalFile, validDeps))) {
76
    // Clear all dependencies so they will be regenerated.
77
    this->Clear(makeFile);
78 79
    cmSystemTools::RemoveFile(internalFile);
    okay = false;
80
  }
81

82
  return okay;
83 84
}

85
void cmDepends::Clear(const std::string& file)
86
{
87
  // Print verbose output.
88
  if (this->Verbose) {
89
    std::ostringstream msg;
90
    msg << "Clearing dependencies in \"" << file << "\"." << std::endl;
91
    cmSystemTools::Stdout(msg.str());
92
  }
93

94
  // Write an empty dependency file.
95
  cmGeneratedFileStream depFileStream(file);
96 97 98
  depFileStream << "# Empty dependencies file\n"
                << "# This may be replaced when dependencies are built."
                << std::endl;
99 100
}

101 102 103 104
bool cmDepends::WriteDependencies(const std::set<std::string>& /*unused*/,
                                  const std::string& /*unused*/,
                                  std::ostream& /*unused*/,
                                  std::ostream& /*unused*/)
105 106 107 108 109
{
  // This should be implemented by the subclass.
  return false;
}

110
bool cmDepends::CheckDependencies(
111
  std::istream& internalDepends, const std::string& internalDependsFileName,
112
  std::map<std::string, DependencyVector>& validDeps)
113 114 115 116 117
{
  // Parse dependencies from the stream.  If any dependee is missing
  // or newer than the depender then dependencies should be
  // regenerated.
  bool okay = true;
118
  bool dependerExists = false;
Daniel Pfeifer's avatar
Daniel Pfeifer committed
119
  DependencyVector* currentDependencies = nullptr;
120

121 122 123
  while (internalDepends.getline(this->Dependee, this->MaxPath)) {
    if (this->Dependee[0] == 0 || this->Dependee[0] == '#' ||
        this->Dependee[0] == '\r') {
124
      continue;
125 126 127 128
    }
    size_t len = internalDepends.gcount() - 1;
    if (this->Dependee[len - 1] == '\r') {
      len--;
129
      this->Dependee[len] = 0;
130 131 132
    }
    if (this->Dependee[0] != ' ') {
      memcpy(this->Depender, this->Dependee, len + 1);
133 134 135 136 137
      // 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);
138 139 140 141
      // 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.
142
      currentDependencies = &validDeps[this->Depender];
143
      continue;
144
    }
145 146 147 148 149 150 151 152
    /*
    // Parse the dependency line.
    if(!this->ParseDependency(line.c_str()))
      {
      continue;
      }
      */

153 154 155 156 157
    // 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
158
    bool regenerate = false;
159
    const char* dependee = this->Dependee + 1;
160
    const char* depender = this->Depender;
Daniel Pfeifer's avatar
Daniel Pfeifer committed
161
    if (currentDependencies != nullptr) {
162
      currentDependencies->push_back(dependee);
163
    }
164

165
    if (!cmSystemTools::FileExists(dependee)) {
166 167 168 169
      // The dependee does not exist.
      regenerate = true;

      // Print verbose output.
170
      if (this->Verbose) {
171
        std::ostringstream msg;
172
        msg << "Dependee \"" << dependee << "\" does not exist for depender \""
173
            << depender << "\"." << std::endl;
174
        cmSystemTools::Stdout(msg.str());
175
      }
176 177
    } else {
      if (dependerExists) {
178 179
        // The dependee and depender both exist.  Compare file times.
        int result = 0;
180 181 182
        if ((!this->FileComparison->FileTimeCompare(depender, dependee,
                                                    &result) ||
             result < 0)) {
183 184
          // The depender is older than the dependee.
          regenerate = true;
185

186
          // Print verbose output.
187
          if (this->Verbose) {
188
            std::ostringstream msg;
189
            msg << "Dependee \"" << dependee << "\" is newer than depender \""
190
                << depender << "\"." << std::endl;
191
            cmSystemTools::Stdout(msg.str());
192 193
          }
        }
194
      } else {
195 196 197
        // The dependee exists, but the depender doesn't. Regenerate if the
        // internalDepends file is older than the dependee.
        int result = 0;
198 199
        if ((!this->FileComparison->FileTimeCompare(
               internalDependsFileName.c_str(), dependee, &result) ||
200
             result < 0)) {
201 202 203 204
          // The depends-file is older than the dependee.
          regenerate = true;

          // Print verbose output.
205
          if (this->Verbose) {
206
            std::ostringstream msg;
207 208 209
            msg << "Dependee \"" << dependee
                << "\" is newer than depends file \""
                << internalDependsFileName << "\"." << std::endl;
210
            cmSystemTools::Stdout(msg.str());
211 212 213
          }
        }
      }
214 215
    }
    if (regenerate) {
216 217 218
      // Dependencies must be regenerated.
      okay = false;

219 220
      // Remove the information of this depender from the map, it needs
      // to be rescanned
Daniel Pfeifer's avatar
Daniel Pfeifer committed
221
      if (currentDependencies != nullptr) {
222
        validDeps.erase(this->Depender);
Daniel Pfeifer's avatar
Daniel Pfeifer committed
223
        currentDependencies = nullptr;
224
      }
225

226
      // Remove the depender to be sure it is rebuilt.
227
      if (dependerExists) {
228 229
        cmSystemTools::RemoveFile(depender);
        dependerExists = false;
230 231
      }
    }
232
  }
233 234 235 236

  return okay;
}

237
void cmDepends::SetIncludePathFromLanguage(const std::string& lang)
238
{
239
  // Look for the new per "TARGET_" variant first:
Daniel Pfeifer's avatar
Daniel Pfeifer committed
240
  const char* includePath = nullptr;
241 242
  std::string includePathVar = "CMAKE_";
  includePathVar += lang;
243
  includePathVar += "_TARGET_INCLUDE_PATH";
244
  cmMakefile* mf = this->LocalGenerator->GetMakefile();
Stephen Kelly's avatar
Stephen Kelly committed
245
  includePath = mf->GetDefinition(includePathVar);
246
  if (includePath) {
247
    cmSystemTools::ExpandListArgument(includePath, this->IncludePath);
248
  } else {
249 250 251 252
    // 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
253
    includePath = mf->GetDefinition(includePathVar);
254
    if (includePath) {
255 256
      cmSystemTools::ExpandListArgument(includePath, this->IncludePath);
    }
257
  }
258
}