cmSourceFile.cxx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  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 <utility>
  5. #include "cmGlobalGenerator.h"
  6. #include "cmListFileCache.h"
  7. #include "cmMakefile.h"
  8. #include "cmMessageType.h"
  9. #include "cmPolicies.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. bool generated, cmSourceFileLocationKind kind)
  17. : Location(mf, name, (!generated) ? kind : cmSourceFileLocationKind::Known)
  18. {
  19. if (generated) {
  20. this->MarkAsGenerated();
  21. }
  22. }
  23. std::string const& cmSourceFile::GetExtension() const
  24. {
  25. return this->Extension;
  26. }
  27. const std::string propTRUE = "1";
  28. const std::string propFALSE = "0";
  29. const std::string cmSourceFile::propLANGUAGE = "LANGUAGE";
  30. const std::string cmSourceFile::propLOCATION = "LOCATION";
  31. const std::string cmSourceFile::propGENERATED = "GENERATED";
  32. const std::string cmSourceFile::propCOMPILE_DEFINITIONS =
  33. "COMPILE_DEFINITIONS";
  34. const std::string cmSourceFile::propCOMPILE_OPTIONS = "COMPILE_OPTIONS";
  35. const std::string cmSourceFile::propINCLUDE_DIRECTORIES =
  36. "INCLUDE_DIRECTORIES";
  37. void cmSourceFile::SetObjectLibrary(std::string const& objlib)
  38. {
  39. this->ObjectLibrary = objlib;
  40. }
  41. std::string cmSourceFile::GetObjectLibrary() const
  42. {
  43. return this->ObjectLibrary;
  44. }
  45. std::string const& cmSourceFile::GetOrDetermineLanguage()
  46. {
  47. // If the language was set explicitly by the user then use it.
  48. if (cmProp lang = this->GetProperty(propLANGUAGE)) {
  49. // Assign to member in order to return a reference.
  50. this->Language = *lang;
  51. return this->Language;
  52. }
  53. // Perform computation needed to get the language if necessary.
  54. if (this->Language.empty()) {
  55. // If a known extension is given or a known full path is given then trust
  56. // that the current extension is sufficient to determine the language. This
  57. // will fail only if the user specifies a full path to the source but
  58. // leaves off the extension, which is kind of weird.
  59. if (this->FullPath.empty() && this->Location.ExtensionIsAmbiguous() &&
  60. this->Location.DirectoryIsAmbiguous()) {
  61. // Finalize the file location to get the extension and set the language.
  62. this->ResolveFullPath();
  63. } else {
  64. // Use the known extension to get the language if possible.
  65. std::string ext =
  66. cmSystemTools::GetFilenameLastExtension(this->Location.GetName());
  67. this->CheckLanguage(ext);
  68. }
  69. }
  70. // Use the language determined from the file extension.
  71. return this->Language;
  72. }
  73. std::string cmSourceFile::GetLanguage() const
  74. {
  75. // If the language was set explicitly by the user then use it.
  76. if (cmProp lang = this->GetProperty(propLANGUAGE)) {
  77. return *lang;
  78. }
  79. // Use the language determined from the file extension.
  80. return this->Language;
  81. }
  82. cmSourceFileLocation const& cmSourceFile::GetLocation() const
  83. {
  84. return this->Location;
  85. }
  86. std::string const& cmSourceFile::ResolveFullPath(std::string* error,
  87. std::string* cmp0115Warning)
  88. {
  89. if (this->FullPath.empty()) {
  90. if (this->FindFullPath(error, cmp0115Warning)) {
  91. this->CheckExtension();
  92. }
  93. }
  94. return this->FullPath;
  95. }
  96. std::string const& cmSourceFile::GetFullPath() const
  97. {
  98. return this->FullPath;
  99. }
  100. bool cmSourceFile::FindFullPath(std::string* error,
  101. std::string* cmp0115Warning)
  102. {
  103. // If the file is generated compute the location without checking on disk.
  104. // Note: We also check for a locally set GENERATED property, because
  105. // it might have been set before policy CMP0118 was set to NEW.
  106. if (this->GetIsGenerated(CheckScope::GlobalAndLocal)) {
  107. // The file is either already a full path or is relative to the
  108. // build directory for the target.
  109. this->Location.DirectoryUseBinary();
  110. this->FullPath = this->Location.GetFullPath();
  111. this->FindFullPathFailed = false;
  112. return true;
  113. }
  114. // If this method has already failed once do not try again.
  115. if (this->FindFullPathFailed) {
  116. return false;
  117. }
  118. // The file is not generated. It must exist on disk.
  119. cmMakefile const* makefile = this->Location.GetMakefile();
  120. // Location path
  121. std::string const& lPath = this->Location.GetFullPath();
  122. // List of extension lists
  123. std::vector<std::string> exts =
  124. makefile->GetCMakeInstance()->GetAllExtensions();
  125. auto cmp0115 = makefile->GetPolicyStatus(cmPolicies::CMP0115);
  126. auto cmp0118 = makefile->GetPolicyStatus(cmPolicies::CMP0118);
  127. bool const cmp0118new =
  128. cmp0118 != cmPolicies::OLD && cmp0118 != cmPolicies::WARN;
  129. // Tries to find the file in a given directory
  130. auto findInDir = [this, &exts, &lPath, cmp0115, cmp0115Warning, cmp0118new,
  131. makefile](std::string const& dir) -> bool {
  132. // Compute full path
  133. std::string const fullPath = cmSystemTools::CollapseFullPath(lPath, dir);
  134. // Try full path
  135. if (cmp0118new &&
  136. makefile->GetGlobalGenerator()->IsGeneratedFile(fullPath)) {
  137. this->IsGenerated = true;
  138. }
  139. if (this->IsGenerated || cmSystemTools::FileExists(fullPath)) {
  140. this->FullPath = fullPath;
  141. return true;
  142. }
  143. // This has to be an if statement due to a bug in Oracle Developer Studio.
  144. // See https://community.oracle.com/tech/developers/discussion/4476246/
  145. // for details.
  146. if (cmp0115 == cmPolicies::OLD || cmp0115 == cmPolicies::WARN) {
  147. // Try full path with extension
  148. for (std::string const& ext : exts) {
  149. if (!ext.empty()) {
  150. std::string extPath = cmStrCat(fullPath, '.', ext);
  151. if (cmp0118new &&
  152. makefile->GetGlobalGenerator()->IsGeneratedFile(extPath)) {
  153. this->IsGenerated = true;
  154. }
  155. if (this->IsGenerated || cmSystemTools::FileExists(extPath)) {
  156. this->FullPath = extPath;
  157. if (cmp0115 == cmPolicies::WARN) {
  158. std::string warning =
  159. cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0115),
  160. "\nFile:\n ", extPath);
  161. if (cmp0115Warning) {
  162. *cmp0115Warning = std::move(warning);
  163. } else {
  164. makefile->GetCMakeInstance()->IssueMessage(
  165. MessageType::AUTHOR_WARNING, warning);
  166. }
  167. }
  168. return true;
  169. }
  170. }
  171. }
  172. }
  173. // File not found
  174. return false;
  175. };
  176. // Try to find the file in various directories
  177. if (this->Location.DirectoryIsAmbiguous()) {
  178. if (findInDir(makefile->GetCurrentSourceDirectory()) ||
  179. findInDir(makefile->GetCurrentBinaryDirectory())) {
  180. return true;
  181. }
  182. } else {
  183. if (findInDir({})) {
  184. return true;
  185. }
  186. }
  187. // Compose error
  188. std::string err = cmStrCat("Cannot find source file:\n ", lPath);
  189. switch (cmp0115) {
  190. case cmPolicies::OLD:
  191. case cmPolicies::WARN:
  192. err = cmStrCat(err, "\nTried extensions");
  193. for (auto const& ext : exts) {
  194. err = cmStrCat(err, " .", ext);
  195. }
  196. break;
  197. case cmPolicies::REQUIRED_IF_USED:
  198. case cmPolicies::REQUIRED_ALWAYS:
  199. case cmPolicies::NEW:
  200. break;
  201. }
  202. if (error != nullptr) {
  203. *error = std::move(err);
  204. } else {
  205. makefile->IssueMessage(MessageType::FATAL_ERROR, err);
  206. }
  207. this->FindFullPathFailed = true;
  208. // File not found
  209. return false;
  210. }
  211. void cmSourceFile::CheckExtension()
  212. {
  213. // Compute the extension.
  214. std::string realExt =
  215. cmSystemTools::GetFilenameLastExtension(this->FullPath);
  216. if (!realExt.empty()) {
  217. // Store the extension without the leading '.'.
  218. this->Extension = realExt.substr(1);
  219. }
  220. // Look for object files.
  221. if (this->Extension == "obj" || this->Extension == "o" ||
  222. this->Extension == "lo") {
  223. this->SetProperty("EXTERNAL_OBJECT", "1");
  224. }
  225. // Try to identify the source file language from the extension.
  226. if (this->Language.empty()) {
  227. this->CheckLanguage(this->Extension);
  228. }
  229. }
  230. void cmSourceFile::CheckLanguage(std::string const& ext)
  231. {
  232. // Try to identify the source file language from the extension.
  233. cmMakefile const* mf = this->Location.GetMakefile();
  234. cmGlobalGenerator* gg = mf->GetGlobalGenerator();
  235. std::string l = gg->GetLanguageFromExtension(ext.c_str());
  236. if (!l.empty()) {
  237. this->Language = l;
  238. }
  239. }
  240. bool cmSourceFile::Matches(cmSourceFileLocation const& loc)
  241. {
  242. return this->Location.Matches(loc);
  243. }
  244. template <typename ValueType>
  245. void cmSourceFile::StoreProperty(const std::string& prop, ValueType value)
  246. {
  247. if (prop == propINCLUDE_DIRECTORIES) {
  248. this->IncludeDirectories.clear();
  249. if (value) {
  250. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  251. this->IncludeDirectories.emplace_back(value, lfbt);
  252. }
  253. } else if (prop == propCOMPILE_OPTIONS) {
  254. this->CompileOptions.clear();
  255. if (value) {
  256. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  257. this->CompileOptions.emplace_back(value, lfbt);
  258. }
  259. } else if (prop == propCOMPILE_DEFINITIONS) {
  260. this->CompileDefinitions.clear();
  261. if (value) {
  262. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  263. this->CompileDefinitions.emplace_back(value, lfbt);
  264. }
  265. } else {
  266. this->Properties.SetProperty(prop, value);
  267. }
  268. }
  269. void cmSourceFile::SetProperty(const std::string& prop, const char* value)
  270. {
  271. this->StoreProperty(prop, value);
  272. }
  273. void cmSourceFile::SetProperty(const std::string& prop, cmProp value)
  274. {
  275. this->StoreProperty(prop, value);
  276. }
  277. void cmSourceFile::AppendProperty(const std::string& prop,
  278. const std::string& value, bool asString)
  279. {
  280. if (prop == propINCLUDE_DIRECTORIES) {
  281. if (!value.empty()) {
  282. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  283. this->IncludeDirectories.emplace_back(value, lfbt);
  284. }
  285. } else if (prop == propCOMPILE_OPTIONS) {
  286. if (!value.empty()) {
  287. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  288. this->CompileOptions.emplace_back(value, lfbt);
  289. }
  290. } else if (prop == propCOMPILE_DEFINITIONS) {
  291. if (!value.empty()) {
  292. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  293. this->CompileDefinitions.emplace_back(value, lfbt);
  294. }
  295. } else {
  296. this->Properties.AppendProperty(prop, value, asString);
  297. }
  298. }
  299. cmProp cmSourceFile::GetPropertyForUser(const std::string& prop)
  300. {
  301. // This method is a consequence of design history and backwards
  302. // compatibility. GetProperty is (and should be) a const method.
  303. // Computed properties should not be stored back in the property map
  304. // but instead reference information already known. If they need to
  305. // cache information in a mutable ivar to provide the return string
  306. // safely then so be it.
  307. //
  308. // The LOCATION property is particularly problematic. The CMake
  309. // language has very loose restrictions on the names that will match
  310. // a given source file (for historical reasons). Implementing
  311. // lookups correctly with such loose naming requires the
  312. // cmSourceFileLocation class to commit to a particular full path to
  313. // the source file as late as possible. If the users requests the
  314. // LOCATION property we must commit now.
  315. if (prop == propLOCATION) {
  316. // Commit to a location.
  317. this->ResolveFullPath();
  318. }
  319. // Similarly, LANGUAGE can be determined by the file extension
  320. // if it is requested by the user.
  321. if (prop == propLANGUAGE) {
  322. // The pointer is valid until `this->Language` is modified.
  323. return cmProp(this->GetOrDetermineLanguage());
  324. }
  325. // Special handling for GENERATED property.
  326. if (prop == propGENERATED) {
  327. // We need to check policy CMP0118 in order to determine if we need to
  328. // possibly consider the value of a locally set GENERATED property, too.
  329. auto policyStatus =
  330. this->Location.GetMakefile()->GetPolicyStatus(cmPolicies::CMP0118);
  331. if (this->GetIsGenerated(
  332. (policyStatus == cmPolicies::WARN || policyStatus == cmPolicies::OLD)
  333. ? CheckScope::GlobalAndLocal
  334. : CheckScope::Global)) {
  335. return cmProp(propTRUE);
  336. }
  337. return cmProp(propFALSE);
  338. }
  339. // Perform the normal property lookup.
  340. return this->GetProperty(prop);
  341. }
  342. cmProp cmSourceFile::GetProperty(const std::string& prop) const
  343. {
  344. // Check for computed properties.
  345. if (prop == propLOCATION) {
  346. if (this->FullPath.empty()) {
  347. return nullptr;
  348. }
  349. return cmProp(this->FullPath);
  350. }
  351. // Check for the properties with backtraces.
  352. if (prop == propINCLUDE_DIRECTORIES) {
  353. if (this->IncludeDirectories.empty()) {
  354. return nullptr;
  355. }
  356. static std::string output;
  357. output = cmJoin(this->IncludeDirectories, ";");
  358. return cmProp(output);
  359. }
  360. if (prop == propCOMPILE_OPTIONS) {
  361. if (this->CompileOptions.empty()) {
  362. return nullptr;
  363. }
  364. static std::string output;
  365. output = cmJoin(this->CompileOptions, ";");
  366. return cmProp(output);
  367. }
  368. if (prop == propCOMPILE_DEFINITIONS) {
  369. if (this->CompileDefinitions.empty()) {
  370. return nullptr;
  371. }
  372. static std::string output;
  373. output = cmJoin(this->CompileDefinitions, ";");
  374. return cmProp(output);
  375. }
  376. cmProp retVal = this->Properties.GetPropertyValue(prop);
  377. if (!retVal) {
  378. cmMakefile const* mf = this->Location.GetMakefile();
  379. const bool chain =
  380. mf->GetState()->IsPropertyChained(prop, cmProperty::SOURCE_FILE);
  381. if (chain) {
  382. return mf->GetProperty(prop, chain);
  383. }
  384. return nullptr;
  385. }
  386. return retVal;
  387. }
  388. const std::string& cmSourceFile::GetSafeProperty(const std::string& prop) const
  389. {
  390. cmProp ret = this->GetProperty(prop);
  391. if (ret) {
  392. return *ret;
  393. }
  394. static std::string const s_empty;
  395. return s_empty;
  396. }
  397. bool cmSourceFile::GetPropertyAsBool(const std::string& prop) const
  398. {
  399. return cmIsOn(this->GetProperty(prop));
  400. }
  401. void cmSourceFile::MarkAsGenerated()
  402. {
  403. this->IsGenerated = true;
  404. const auto& mf = *this->Location.GetMakefile();
  405. mf.GetGlobalGenerator()->MarkAsGeneratedFile(this->ResolveFullPath());
  406. }
  407. bool cmSourceFile::GetIsGenerated(CheckScope checkScope) const
  408. {
  409. if (this->IsGenerated) {
  410. // Globally marked as generated!
  411. return true;
  412. }
  413. if (checkScope == CheckScope::GlobalAndLocal) {
  414. // Check locally stored properties.
  415. return this->GetPropertyAsBool(propGENERATED);
  416. }
  417. return false;
  418. }
  419. void cmSourceFile::SetProperties(cmPropertyMap properties)
  420. {
  421. this->Properties = std::move(properties);
  422. }
  423. cmCustomCommand* cmSourceFile::GetCustomCommand() const
  424. {
  425. return this->CustomCommand.get();
  426. }
  427. void cmSourceFile::SetCustomCommand(std::unique_ptr<cmCustomCommand> cc)
  428. {
  429. this->CustomCommand = std::move(cc);
  430. }