cmSourceFile.cxx 12 KB

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