cmSourceFile.cxx 15 KB

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