cmSourceFile.cxx 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmSourceFile.h"
  4. #include <array>
  5. #include <utility>
  6. #include "cmGlobalGenerator.h"
  7. #include "cmMakefile.h"
  8. #include "cmMessageType.h"
  9. #include "cmProperty.h"
  10. #include "cmState.h"
  11. #include "cmStringAlgorithms.h"
  12. #include "cmSystemTools.h"
  13. #include "cmake.h"
  14. cmSourceFile::cmSourceFile(cmMakefile* mf, const std::string& name,
  15. cmSourceFileLocationKind kind)
  16. : Location(mf, name, kind)
  17. {
  18. }
  19. std::string const& cmSourceFile::GetExtension() const
  20. {
  21. return this->Extension;
  22. }
  23. const std::string cmSourceFile::propLANGUAGE = "LANGUAGE";
  24. const std::string cmSourceFile::propLOCATION = "LOCATION";
  25. const std::string cmSourceFile::propGENERATED = "GENERATED";
  26. void cmSourceFile::SetObjectLibrary(std::string const& objlib)
  27. {
  28. this->ObjectLibrary = objlib;
  29. }
  30. std::string cmSourceFile::GetObjectLibrary() const
  31. {
  32. return this->ObjectLibrary;
  33. }
  34. std::string const& cmSourceFile::GetOrDetermineLanguage()
  35. {
  36. // If the language was set explicitly by the user then use it.
  37. if (const char* lang = this->GetProperty(propLANGUAGE)) {
  38. // Assign to member in order to return a reference.
  39. this->Language = lang;
  40. return this->Language;
  41. }
  42. // Perform computation needed to get the language if necessary.
  43. if (this->FullPath.empty() && this->Language.empty()) {
  44. // If a known extension is given or a known full path is given
  45. // then trust that the current extension is sufficient to
  46. // determine the language. This will fail only if the user
  47. // specifies a full path to the source but leaves off the
  48. // extension, which is kind of weird.
  49. if (this->Location.ExtensionIsAmbiguous() &&
  50. this->Location.DirectoryIsAmbiguous()) {
  51. // Finalize the file location to get the extension and set the
  52. // language.
  53. this->ResolveFullPath();
  54. } else {
  55. // Use the known extension to get the language if possible.
  56. std::string ext =
  57. cmSystemTools::GetFilenameLastExtension(this->Location.GetName());
  58. this->CheckLanguage(ext);
  59. }
  60. }
  61. // Use the language determined from the file extension.
  62. return this->Language;
  63. }
  64. std::string cmSourceFile::GetLanguage() const
  65. {
  66. // If the language was set explicitly by the user then use it.
  67. if (const char* lang = this->GetProperty(propLANGUAGE)) {
  68. return lang;
  69. }
  70. // Use the language determined from the file extension.
  71. return this->Language;
  72. }
  73. cmSourceFileLocation const& cmSourceFile::GetLocation() const
  74. {
  75. return this->Location;
  76. }
  77. std::string const& cmSourceFile::ResolveFullPath(std::string* error)
  78. {
  79. if (this->FullPath.empty()) {
  80. if (this->FindFullPath(error)) {
  81. this->CheckExtension();
  82. }
  83. }
  84. return this->FullPath;
  85. }
  86. std::string const& cmSourceFile::GetFullPath() const
  87. {
  88. return this->FullPath;
  89. }
  90. bool cmSourceFile::FindFullPath(std::string* error)
  91. {
  92. // If the file is generated compute the location without checking on disk.
  93. if (this->GetIsGenerated()) {
  94. // The file is either already a full path or is relative to the
  95. // build directory for the target.
  96. this->Location.DirectoryUseBinary();
  97. this->FullPath = this->Location.GetFullPath();
  98. return true;
  99. }
  100. // If this method has already failed once do not try again.
  101. if (this->FindFullPathFailed) {
  102. return false;
  103. }
  104. // The file is not generated. It must exist on disk.
  105. cmMakefile const* makefile = this->Location.GetMakefile();
  106. // Location path
  107. std::string const lPath = this->Location.GetFullPath();
  108. // List of extension lists
  109. std::array<std::vector<std::string> const*, 2> const extsLists = {
  110. { &makefile->GetCMakeInstance()->GetSourceExtensions(),
  111. &makefile->GetCMakeInstance()->GetHeaderExtensions() }
  112. };
  113. // Tries to find the file in a given directory
  114. auto findInDir = [this, &extsLists, &lPath](std::string const& dir) -> bool {
  115. // Compute full path
  116. std::string const fullPath = cmSystemTools::CollapseFullPath(lPath, dir);
  117. // Try full path
  118. if (cmSystemTools::FileExists(fullPath)) {
  119. this->FullPath = fullPath;
  120. return true;
  121. }
  122. // Try full path with extension
  123. for (auto exts : extsLists) {
  124. for (std::string const& ext : *exts) {
  125. if (!ext.empty()) {
  126. std::string extPath = cmStrCat(fullPath, '.', ext);
  127. if (cmSystemTools::FileExists(extPath)) {
  128. this->FullPath = extPath;
  129. return true;
  130. }
  131. }
  132. }
  133. }
  134. // File not found
  135. return false;
  136. };
  137. // Try to find the file in various directories
  138. if (this->Location.DirectoryIsAmbiguous()) {
  139. if (findInDir(makefile->GetCurrentSourceDirectory()) ||
  140. findInDir(makefile->GetCurrentBinaryDirectory())) {
  141. return true;
  142. }
  143. } else {
  144. if (findInDir({})) {
  145. return true;
  146. }
  147. }
  148. // Compose error
  149. std::string err =
  150. cmStrCat("Cannot find source file:\n ", lPath, "\nTried extensions");
  151. for (auto exts : extsLists) {
  152. for (std::string const& ext : *exts) {
  153. err += " .";
  154. err += ext;
  155. }
  156. }
  157. if (error != nullptr) {
  158. *error = std::move(err);
  159. } else {
  160. makefile->IssueMessage(MessageType::FATAL_ERROR, err);
  161. }
  162. this->FindFullPathFailed = true;
  163. // File not found
  164. return false;
  165. }
  166. void cmSourceFile::CheckExtension()
  167. {
  168. // Compute the extension.
  169. std::string realExt =
  170. cmSystemTools::GetFilenameLastExtension(this->FullPath);
  171. if (!realExt.empty()) {
  172. // Store the extension without the leading '.'.
  173. this->Extension = realExt.substr(1);
  174. }
  175. // Look for object files.
  176. if (this->Extension == "obj" || this->Extension == "o" ||
  177. this->Extension == "lo") {
  178. this->SetProperty("EXTERNAL_OBJECT", "1");
  179. }
  180. // Try to identify the source file language from the extension.
  181. if (this->Language.empty()) {
  182. this->CheckLanguage(this->Extension);
  183. }
  184. }
  185. void cmSourceFile::CheckLanguage(std::string const& ext)
  186. {
  187. // Try to identify the source file language from the extension.
  188. cmMakefile const* mf = this->Location.GetMakefile();
  189. cmGlobalGenerator* gg = mf->GetGlobalGenerator();
  190. std::string l = gg->GetLanguageFromExtension(ext.c_str());
  191. if (!l.empty()) {
  192. this->Language = l;
  193. }
  194. }
  195. bool cmSourceFile::Matches(cmSourceFileLocation const& loc)
  196. {
  197. return this->Location.Matches(loc);
  198. }
  199. void cmSourceFile::SetProperty(const std::string& prop, const char* value)
  200. {
  201. this->Properties.SetProperty(prop, value);
  202. // Update IsGenerated flag
  203. if (prop == propGENERATED) {
  204. this->IsGenerated = cmIsOn(value);
  205. }
  206. }
  207. void cmSourceFile::AppendProperty(const std::string& prop, const char* value,
  208. bool asString)
  209. {
  210. this->Properties.AppendProperty(prop, value, asString);
  211. // Update IsGenerated flag
  212. if (prop == propGENERATED) {
  213. this->IsGenerated = this->GetPropertyAsBool(propGENERATED);
  214. }
  215. }
  216. const char* cmSourceFile::GetPropertyForUser(const std::string& prop)
  217. {
  218. // This method is a consequence of design history and backwards
  219. // compatibility. GetProperty is (and should be) a const method.
  220. // Computed properties should not be stored back in the property map
  221. // but instead reference information already known. If they need to
  222. // cache information in a mutable ivar to provide the return string
  223. // safely then so be it.
  224. //
  225. // The LOCATION property is particularly problematic. The CMake
  226. // language has very loose restrictions on the names that will match
  227. // a given source file (for historical reasons). Implementing
  228. // lookups correctly with such loose naming requires the
  229. // cmSourceFileLocation class to commit to a particular full path to
  230. // the source file as late as possible. If the users requests the
  231. // LOCATION property we must commit now.
  232. if (prop == propLOCATION) {
  233. // Commit to a location.
  234. this->ResolveFullPath();
  235. }
  236. // Similarly, LANGUAGE can be determined by the file extension
  237. // if it is requested by the user.
  238. if (prop == propLANGUAGE) {
  239. // The c_str pointer is valid until `this->Language` is modified.
  240. return this->GetOrDetermineLanguage().c_str();
  241. }
  242. // Perform the normal property lookup.
  243. return this->GetProperty(prop);
  244. }
  245. const char* cmSourceFile::GetProperty(const std::string& prop) const
  246. {
  247. // Check for computed properties.
  248. if (prop == propLOCATION) {
  249. if (this->FullPath.empty()) {
  250. return nullptr;
  251. }
  252. return this->FullPath.c_str();
  253. }
  254. const char* retVal = this->Properties.GetPropertyValue(prop);
  255. if (!retVal) {
  256. cmMakefile const* mf = this->Location.GetMakefile();
  257. const bool chain =
  258. mf->GetState()->IsPropertyChained(prop, cmProperty::SOURCE_FILE);
  259. if (chain) {
  260. return mf->GetProperty(prop, chain);
  261. }
  262. }
  263. return retVal;
  264. }
  265. const char* cmSourceFile::GetSafeProperty(const std::string& prop) const
  266. {
  267. const char* ret = this->GetProperty(prop);
  268. if (!ret) {
  269. return "";
  270. }
  271. return ret;
  272. }
  273. bool cmSourceFile::GetPropertyAsBool(const std::string& prop) const
  274. {
  275. return cmIsOn(this->GetProperty(prop));
  276. }
  277. void cmSourceFile::SetProperties(cmPropertyMap properties)
  278. {
  279. this->Properties = std::move(properties);
  280. this->IsGenerated = this->GetPropertyAsBool(propGENERATED);
  281. }
  282. cmCustomCommand* cmSourceFile::GetCustomCommand() const
  283. {
  284. return this->CustomCommand.get();
  285. }
  286. void cmSourceFile::SetCustomCommand(std::unique_ptr<cmCustomCommand> cc)
  287. {
  288. this->CustomCommand = std::move(cc);
  289. }