cmSourceFile.cxx 9.12 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
#include "cmSourceFile.h"
4

5
6
#include <array>
#include <utility>
7

8
#include "cmCustomCommand.h"
9
#include "cmGlobalGenerator.h"
10
#include "cmMakefile.h"
11
#include "cmMessageType.h"
12
#include "cmProperty.h"
13
#include "cmState.h"
14
15
#include "cmSystemTools.h"
#include "cmake.h"
16

17
18
19
cmSourceFile::cmSourceFile(cmMakefile* mf, const std::string& name,
                           cmSourceFileLocationKind kind)
  : Location(mf, name, kind)
20
{
21
}
22

23
24
cmSourceFile::~cmSourceFile()
{
Daniel Pfeifer's avatar
Daniel Pfeifer committed
25
  this->SetCustomCommand(nullptr);
26
}
27

28
29
30
31
std::string const& cmSourceFile::GetExtension() const
{
  return this->Extension;
}
32

33
const std::string cmSourceFile::propLANGUAGE = "LANGUAGE";
34
35
const std::string cmSourceFile::propLOCATION = "LOCATION";
const std::string cmSourceFile::propGENERATED = "GENERATED";
36

37
38
39
40
41
42
43
44
45
46
void cmSourceFile::SetObjectLibrary(std::string const& objlib)
{
  this->ObjectLibrary = objlib;
}

std::string cmSourceFile::GetObjectLibrary() const
{
  return this->ObjectLibrary;
}

47
std::string cmSourceFile::GetLanguage()
48
{
49
  // If the language was set explicitly by the user then use it.
50
  if (const char* lang = this->GetProperty(propLANGUAGE)) {
51
    return lang;
52
  }
53
54

  // Perform computation needed to get the language if necessary.
55
  if (this->FullPath.empty() && this->Language.empty()) {
56
57
58
59
60
    // If a known extension is given or a known full path is given
    // then trust that the current extension is sufficient to
    // determine the language.  This will fail only if the user
    // specifies a full path to the source but leaves off the
    // extension, which is kind of weird.
61
62
    if (this->Location.ExtensionIsAmbiguous() &&
        this->Location.DirectoryIsAmbiguous()) {
63
64
65
      // Finalize the file location to get the extension and set the
      // language.
      this->GetFullPath();
66
    } else {
67
68
69
70
      // Use the known extension to get the language if possible.
      std::string ext =
        cmSystemTools::GetFilenameLastExtension(this->Location.GetName());
      this->CheckLanguage(ext);
71
    }
72
  }
73
74
75
76

  // Now try to determine the language.
  return static_cast<cmSourceFile const*>(this)->GetLanguage();
}
77

78
std::string cmSourceFile::GetLanguage() const
79
80
{
  // If the language was set explicitly by the user then use it.
81
  if (const char* lang = this->GetProperty(propLANGUAGE)) {
82
    return lang;
83
  }
84

85
  // If the language was determined from the source file extension use it.
86
  if (!this->Language.empty()) {
87
    return this->Language;
88
  }
89

90
  // The language is not known.
91
  return "";
92
93
}

94
95
cmSourceFileLocation const& cmSourceFile::GetLocation() const
{
96
  return this->Location;
97
98
}

99
std::string const& cmSourceFile::GetFullPath(std::string* error)
100
{
101
102
  if (this->FullPath.empty()) {
    if (this->FindFullPath(error)) {
103
      this->CheckExtension();
104
    }
105
  }
106
107
108
109
110
111
112
113
  return this->FullPath;
}

std::string const& cmSourceFile::GetFullPath() const
{
  return this->FullPath;
}

114
bool cmSourceFile::FindFullPath(std::string* error)
115
{
116
  // If the file is generated compute the location without checking on disk.
117
  if (this->GetIsGenerated()) {
118
119
120
    // The file is either already a full path or is relative to the
    // build directory for the target.
    this->Location.DirectoryUseBinary();
121
    this->FullPath = this->Location.GetFullPath();
122
    return true;
123
  }
124

125
126
127
128
129
  // If this method has already failed once do not try again.
  if (this->FindFullPathFailed) {
    return false;
  }

130
  // The file is not generated.  It must exist on disk.
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
  cmMakefile const* makefile = this->Location.GetMakefile();
  // Location path
  std::string const lPath = this->Location.GetFullPath();
  // List of extension lists
  std::array<std::vector<std::string> const*, 2> const extsLists = {
    { &makefile->GetCMakeInstance()->GetSourceExtensions(),
      &makefile->GetCMakeInstance()->GetHeaderExtensions() }
  };

  // Tries to find the file in a given directory
  auto findInDir = [this, &extsLists, &lPath](std::string const& dir) -> bool {
    // Compute full path
    std::string const fullPath = cmSystemTools::CollapseFullPath(lPath, dir);
    // Try full path
    if (cmSystemTools::FileExists(fullPath)) {
      this->FullPath = fullPath;
147
      return true;
148
    }
149
150
151
152
153
154
155
156
157
158
159
160
    // Try full path with extension
    for (auto exts : extsLists) {
      for (std::string const& ext : *exts) {
        if (!ext.empty()) {
          std::string extPath = fullPath;
          extPath += '.';
          extPath += ext;
          if (cmSystemTools::FileExists(extPath)) {
            this->FullPath = extPath;
            return true;
          }
        }
161
      }
162
    }
163
164
165
166
167
168
169
170
171
172
173
174
175
    // File not found
    return false;
  };

  // Try to find the file in various directories
  if (this->Location.DirectoryIsAmbiguous()) {
    if (findInDir(makefile->GetCurrentSourceDirectory()) ||
        findInDir(makefile->GetCurrentBinaryDirectory())) {
      return true;
    }
  } else {
    if (findInDir({})) {
      return true;
Bill Hoffman's avatar
Bill Hoffman committed
176
    }
177
  }
Bill Hoffman's avatar
Bill Hoffman committed
178

179
180
181
182
183
184
185
186
187
188
  // Compose error
  std::string err;
  err += "Cannot find source file:\n  ";
  err += lPath;
  err += "\nTried extensions";
  for (auto exts : extsLists) {
    for (std::string const& ext : *exts) {
      err += " .";
      err += ext;
    }
189
  }
190
191
  if (error != nullptr) {
    *error = std::move(err);
192
  } else {
193
    makefile->IssueMessage(MessageType::FATAL_ERROR, err);
194
  }
195
196
  this->FindFullPathFailed = true;

197
  // File not found
198
  return false;
199
200
}

201
void cmSourceFile::CheckExtension()
202
{
203
204
205
  // Compute the extension.
  std::string realExt =
    cmSystemTools::GetFilenameLastExtension(this->FullPath);
206
  if (!realExt.empty()) {
207
208
    // Store the extension without the leading '.'.
    this->Extension = realExt.substr(1);
209
  }
210
211

  // Look for object files.
212
213
  if (this->Extension == "obj" || this->Extension == "o" ||
      this->Extension == "lo") {
214
    this->SetProperty("EXTERNAL_OBJECT", "1");
215
  }
216
217

  // Try to identify the source file language from the extension.
218
  if (this->Language.empty()) {
219
    this->CheckLanguage(this->Extension);
220
  }
221
222
223
224
225
}

void cmSourceFile::CheckLanguage(std::string const& ext)
{
  // Try to identify the source file language from the extension.
226
  cmMakefile const* mf = this->Location.GetMakefile();
227
  cmGlobalGenerator* gg = mf->GetGlobalGenerator();
228
  std::string l = gg->GetLanguageFromExtension(ext.c_str());
229
  if (!l.empty()) {
230
    this->Language = l;
231
  }
232
}
233

234
bool cmSourceFile::Matches(cmSourceFileLocation const& loc)
235
{
236
  return this->Location.Matches(loc);
237
238
}

239
void cmSourceFile::SetProperty(const std::string& prop, const char* value)
240
{
241
  this->Properties.SetProperty(prop, value);
242
243
244
245
246

  // Update IsGenerated flag
  if (prop == propGENERATED) {
    this->IsGenerated = cmSystemTools::IsOn(value);
  }
247
248
}

249
void cmSourceFile::AppendProperty(const std::string& prop, const char* value,
250
                                  bool asString)
251
{
252
  this->Properties.AppendProperty(prop, value, asString);
253
254
255
256
257

  // Update IsGenerated flag
  if (prop == propGENERATED) {
    this->IsGenerated = this->GetPropertyAsBool(propGENERATED);
  }
258
259
}

260
const char* cmSourceFile::GetPropertyForUser(const std::string& prop)
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
{
  // This method is a consequence of design history and backwards
  // compatibility.  GetProperty is (and should be) a const method.
  // Computed properties should not be stored back in the property map
  // but instead reference information already known.  If they need to
  // cache information in a mutable ivar to provide the return string
  // safely then so be it.
  //
  // The LOCATION property is particularly problematic.  The CMake
  // language has very loose restrictions on the names that will match
  // a given source file (for historical reasons).  Implementing
  // lookups correctly with such loose naming requires the
  // cmSourceFileLocation class to commit to a particular full path to
  // the source file as late as possible.  If the users requests the
  // LOCATION property we must commit now.
276
  if (prop == propLOCATION) {
277
278
    // Commit to a location.
    this->GetFullPath();
279
  }
280
281
282
283
284

  // Perform the normal property lookup.
  return this->GetProperty(prop);
}

285
const char* cmSourceFile::GetProperty(const std::string& prop) const
286
{
287
  // Check for computed properties.
288
  if (prop == propLOCATION) {
289
    if (this->FullPath.empty()) {
Daniel Pfeifer's avatar
Daniel Pfeifer committed
290
      return nullptr;
Ken Martin's avatar
Ken Martin committed
291
    }
292
    return this->FullPath.c_str();
293
  }
Ken Martin's avatar
Ken Martin committed
294

295
296
  const char* retVal = this->Properties.GetPropertyValue(prop);
  if (!retVal) {
297
    cmMakefile const* mf = this->Location.GetMakefile();
298
299
300
    const bool chain =
      mf->GetState()->IsPropertyChained(prop, cmProperty::SOURCE_FILE);
    if (chain) {
301
      return mf->GetProperty(prop, chain);
302
    }
303
  }
304

305
  return retVal;
306
307
}

308
309
310
311
312
313
314
315
316
const char* cmSourceFile::GetSafeProperty(const std::string& prop) const
{
  const char* ret = this->GetProperty(prop);
  if (!ret) {
    return "";
  }
  return ret;
}

317
bool cmSourceFile::GetPropertyAsBool(const std::string& prop) const
318
{
319
  return cmSystemTools::IsOn(this->GetProperty(prop));
320
}
321

322
cmCustomCommand* cmSourceFile::GetCustomCommand()
323
{
324
  return this->CustomCommand;
325
}
326

327
cmCustomCommand const* cmSourceFile::GetCustomCommand() const
328
{
329
  return this->CustomCommand;
330
331
}

332
void cmSourceFile::SetCustomCommand(cmCustomCommand* cc)
333
{
334
335
336
  cmCustomCommand* old = this->CustomCommand;
  this->CustomCommand = cc;
  delete old;
337
}