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 <cm/string_view>
  6. #include <cmext/string_view>
  7. #include "cmGlobalGenerator.h"
  8. #include "cmList.h"
  9. #include "cmListFileCache.h"
  10. #include "cmMakefile.h"
  11. #include "cmMessageType.h"
  12. #include "cmPolicies.h"
  13. #include "cmProperty.h"
  14. #include "cmState.h"
  15. #include "cmStringAlgorithms.h"
  16. #include "cmSystemTools.h"
  17. #include "cmValue.h"
  18. #include "cmake.h"
  19. cmSourceFile::cmSourceFile(cmMakefile* mf, const std::string& name,
  20. bool generated, cmSourceFileLocationKind kind)
  21. : Location(mf, name, (!generated) ? kind : cmSourceFileLocationKind::Known)
  22. {
  23. if (generated) {
  24. this->MarkAsGenerated();
  25. }
  26. }
  27. std::string const& cmSourceFile::GetExtension() const
  28. {
  29. return this->Extension;
  30. }
  31. const std::string propTRUE = "1";
  32. const std::string propFALSE = "0";
  33. const std::string cmSourceFile::propLANGUAGE = "LANGUAGE";
  34. const std::string cmSourceFile::propLOCATION = "LOCATION";
  35. const std::string cmSourceFile::propGENERATED = "GENERATED";
  36. const std::string cmSourceFile::propCOMPILE_DEFINITIONS =
  37. "COMPILE_DEFINITIONS";
  38. const std::string cmSourceFile::propCOMPILE_OPTIONS = "COMPILE_OPTIONS";
  39. const std::string cmSourceFile::propINCLUDE_DIRECTORIES =
  40. "INCLUDE_DIRECTORIES";
  41. void cmSourceFile::SetObjectLibrary(std::string const& objlib)
  42. {
  43. this->ObjectLibrary = objlib;
  44. }
  45. std::string cmSourceFile::GetObjectLibrary() const
  46. {
  47. return this->ObjectLibrary;
  48. }
  49. std::string const& cmSourceFile::GetOrDetermineLanguage()
  50. {
  51. // If the language was set explicitly by the user then use it.
  52. if (cmValue lang = this->GetProperty(propLANGUAGE)) {
  53. // Assign to member in order to return a reference.
  54. this->Language = *lang;
  55. return this->Language;
  56. }
  57. // Perform computation needed to get the language if necessary.
  58. if (this->Language.empty()) {
  59. // If a known extension is given or a known full path is given then trust
  60. // that the current extension is sufficient to determine the language. This
  61. // will fail only if the user specifies a full path to the source but
  62. // leaves off the extension, which is kind of weird.
  63. if (this->FullPath.empty() && this->Location.ExtensionIsAmbiguous() &&
  64. this->Location.DirectoryIsAmbiguous()) {
  65. // Finalize the file location to get the extension and set the language.
  66. this->ResolveFullPath();
  67. } else {
  68. // Use the known extension to get the language if possible.
  69. std::string ext =
  70. cmSystemTools::GetFilenameLastExtension(this->Location.GetName());
  71. this->CheckLanguage(ext);
  72. }
  73. }
  74. // Use the language determined from the file extension.
  75. return this->Language;
  76. }
  77. std::string cmSourceFile::GetLanguage() const
  78. {
  79. // If the language was set explicitly by the user then use it.
  80. if (cmValue lang = this->GetProperty(propLANGUAGE)) {
  81. return *lang;
  82. }
  83. // Use the language determined from the file extension.
  84. return this->Language;
  85. }
  86. cmSourceFileLocation const& cmSourceFile::GetLocation() const
  87. {
  88. return this->Location;
  89. }
  90. std::string const& cmSourceFile::ResolveFullPath(std::string* error,
  91. std::string* cmp0115Warning)
  92. {
  93. if (this->FullPath.empty()) {
  94. if (this->FindFullPath(error, cmp0115Warning)) {
  95. this->CheckExtension();
  96. }
  97. }
  98. return this->FullPath;
  99. }
  100. std::string const& cmSourceFile::GetFullPath() const
  101. {
  102. return this->FullPath;
  103. }
  104. bool cmSourceFile::FindFullPath(std::string* error,
  105. std::string* cmp0115Warning)
  106. {
  107. // If the file is generated compute the location without checking on disk.
  108. // Note: We also check for a locally set GENERATED property, because
  109. // it might have been set before policy CMP0118 was set to NEW.
  110. if (this->GetIsGenerated(CheckScope::GlobalAndLocal)) {
  111. // The file is either already a full path or is relative to the
  112. // build directory for the target.
  113. this->Location.DirectoryUseBinary();
  114. this->FullPath = this->Location.GetFullPath();
  115. this->FindFullPathFailed = false;
  116. return true;
  117. }
  118. // If this method has already failed once do not try again.
  119. if (this->FindFullPathFailed) {
  120. return false;
  121. }
  122. // The file is not generated. It must exist on disk.
  123. cmMakefile const* makefile = this->Location.GetMakefile();
  124. // Location path
  125. std::string const& lPath = this->Location.GetFullPath();
  126. // List of extension lists
  127. std::vector<std::string> exts =
  128. makefile->GetCMakeInstance()->GetAllExtensions();
  129. auto cmp0115 = makefile->GetPolicyStatus(cmPolicies::CMP0115);
  130. auto cmp0118 = makefile->GetPolicyStatus(cmPolicies::CMP0118);
  131. bool const cmp0118new =
  132. cmp0118 != cmPolicies::OLD && cmp0118 != cmPolicies::WARN;
  133. // Tries to find the file in a given directory
  134. auto findInDir = [this, &exts, &lPath, cmp0115, cmp0115Warning, cmp0118new,
  135. makefile](std::string const& dir) -> bool {
  136. // Compute full path
  137. std::string const fullPath = cmSystemTools::CollapseFullPath(lPath, dir);
  138. // Try full path
  139. if (cmp0118new &&
  140. makefile->GetGlobalGenerator()->IsGeneratedFile(fullPath)) {
  141. this->IsGenerated = true;
  142. }
  143. if (this->IsGenerated || cmSystemTools::FileExists(fullPath)) {
  144. this->FullPath = fullPath;
  145. return true;
  146. }
  147. // This has to be an if statement due to a bug in Oracle Developer Studio.
  148. // See https://community.oracle.com/tech/developers/discussion/4476246/
  149. // for details.
  150. if (cmp0115 == cmPolicies::OLD || cmp0115 == cmPolicies::WARN) {
  151. // Try full path with extension
  152. for (std::string const& ext : exts) {
  153. if (!ext.empty()) {
  154. std::string extPath = cmStrCat(fullPath, '.', ext);
  155. if (cmp0118new &&
  156. makefile->GetGlobalGenerator()->IsGeneratedFile(extPath)) {
  157. this->IsGenerated = true;
  158. }
  159. if (this->IsGenerated || cmSystemTools::FileExists(extPath)) {
  160. this->FullPath = extPath;
  161. if (cmp0115 == cmPolicies::WARN) {
  162. std::string warning =
  163. cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0115),
  164. "\nFile:\n ", extPath);
  165. if (cmp0115Warning) {
  166. *cmp0115Warning = std::move(warning);
  167. } else {
  168. makefile->GetCMakeInstance()->IssueMessage(
  169. MessageType::AUTHOR_WARNING, warning);
  170. }
  171. }
  172. return true;
  173. }
  174. }
  175. }
  176. }
  177. // File not found
  178. return false;
  179. };
  180. // Try to find the file in various directories
  181. if (this->Location.DirectoryIsAmbiguous()) {
  182. if (findInDir(makefile->GetCurrentSourceDirectory()) ||
  183. findInDir(makefile->GetCurrentBinaryDirectory())) {
  184. return true;
  185. }
  186. } else {
  187. if (findInDir({})) {
  188. return true;
  189. }
  190. }
  191. // Compose error
  192. std::string err = cmStrCat("Cannot find source file:\n ", lPath);
  193. switch (cmp0115) {
  194. case cmPolicies::OLD:
  195. case cmPolicies::WARN:
  196. err = cmStrCat(err, "\nTried extensions");
  197. for (auto const& ext : exts) {
  198. err = cmStrCat(err, " .", ext);
  199. }
  200. break;
  201. case cmPolicies::REQUIRED_IF_USED:
  202. case cmPolicies::REQUIRED_ALWAYS:
  203. case cmPolicies::NEW:
  204. break;
  205. }
  206. if (lPath == "FILE_SET"_s) {
  207. err += "\nHint: the FILE_SET keyword may only appear after a visibility "
  208. "specifier or another FILE_SET within the target_sources() "
  209. "command.";
  210. }
  211. if (error != nullptr) {
  212. *error = std::move(err);
  213. } else {
  214. makefile->IssueMessage(MessageType::FATAL_ERROR, err);
  215. }
  216. this->FindFullPathFailed = true;
  217. // File not found
  218. return false;
  219. }
  220. void cmSourceFile::CheckExtension()
  221. {
  222. // Compute the extension.
  223. std::string realExt =
  224. cmSystemTools::GetFilenameLastExtension(this->FullPath);
  225. if (!realExt.empty()) {
  226. // Store the extension without the leading '.'.
  227. this->Extension = realExt.substr(1);
  228. }
  229. // Look for object files.
  230. if (this->Extension == "obj" || this->Extension == "o" ||
  231. this->Extension == "lo") {
  232. this->SetProperty("EXTERNAL_OBJECT", "1");
  233. }
  234. // Try to identify the source file language from the extension.
  235. if (this->Language.empty()) {
  236. this->CheckLanguage(this->Extension);
  237. }
  238. }
  239. void cmSourceFile::CheckLanguage(std::string const& ext)
  240. {
  241. // Try to identify the source file language from the extension.
  242. cmMakefile const* mf = this->Location.GetMakefile();
  243. cmGlobalGenerator* gg = mf->GetGlobalGenerator();
  244. std::string l = gg->GetLanguageFromExtension(ext.c_str());
  245. if (!l.empty()) {
  246. this->Language = l;
  247. }
  248. }
  249. bool cmSourceFile::Matches(cmSourceFileLocation const& loc)
  250. {
  251. return this->Location.Matches(loc);
  252. }
  253. void cmSourceFile::SetProperty(const std::string& prop, cmValue value)
  254. {
  255. if (prop == propINCLUDE_DIRECTORIES) {
  256. this->IncludeDirectories.clear();
  257. if (value) {
  258. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  259. this->IncludeDirectories.emplace_back(value, lfbt);
  260. }
  261. } else if (prop == propCOMPILE_OPTIONS) {
  262. this->CompileOptions.clear();
  263. if (value) {
  264. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  265. this->CompileOptions.emplace_back(value, lfbt);
  266. }
  267. } else if (prop == propCOMPILE_DEFINITIONS) {
  268. this->CompileDefinitions.clear();
  269. if (value) {
  270. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  271. this->CompileDefinitions.emplace_back(value, lfbt);
  272. }
  273. } else {
  274. this->Properties.SetProperty(prop, value);
  275. }
  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. cmValue 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 cmValue(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 cmValue(propTRUE);
  336. }
  337. return cmValue(propFALSE);
  338. }
  339. // Perform the normal property lookup.
  340. return this->GetProperty(prop);
  341. }
  342. cmValue 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 cmValue(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 = cmList::to_string(this->IncludeDirectories);
  358. return cmValue(output);
  359. }
  360. if (prop == propCOMPILE_OPTIONS) {
  361. if (this->CompileOptions.empty()) {
  362. return nullptr;
  363. }
  364. static std::string output;
  365. output = cmList::to_string(this->CompileOptions);
  366. return cmValue(output);
  367. }
  368. if (prop == propCOMPILE_DEFINITIONS) {
  369. if (this->CompileDefinitions.empty()) {
  370. return nullptr;
  371. }
  372. static std::string output;
  373. output = cmList::to_string(this->CompileDefinitions);
  374. return cmValue(output);
  375. }
  376. cmValue 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. cmValue 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 this->GetProperty(prop).IsOn();
  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. }