cmSourceFile.cxx 9.1 KB

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