cmSourceFile.cxx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  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. void cmSourceFile::SetProperty(const std::string& prop, const char* value)
  245. {
  246. if (prop == propINCLUDE_DIRECTORIES) {
  247. this->IncludeDirectories.clear();
  248. if (value) {
  249. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  250. this->IncludeDirectories.emplace_back(value, lfbt);
  251. }
  252. } else if (prop == propCOMPILE_OPTIONS) {
  253. this->CompileOptions.clear();
  254. if (value) {
  255. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  256. this->CompileOptions.emplace_back(value, lfbt);
  257. }
  258. } else if (prop == propCOMPILE_DEFINITIONS) {
  259. this->CompileDefinitions.clear();
  260. if (value) {
  261. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  262. this->CompileDefinitions.emplace_back(value, lfbt);
  263. }
  264. } else {
  265. this->Properties.SetProperty(prop, value);
  266. }
  267. }
  268. void cmSourceFile::AppendProperty(const std::string& prop,
  269. const std::string& value, bool asString)
  270. {
  271. if (prop == propINCLUDE_DIRECTORIES) {
  272. if (!value.empty()) {
  273. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  274. this->IncludeDirectories.emplace_back(value, lfbt);
  275. }
  276. } else if (prop == propCOMPILE_OPTIONS) {
  277. if (!value.empty()) {
  278. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  279. this->CompileOptions.emplace_back(value, lfbt);
  280. }
  281. } else if (prop == propCOMPILE_DEFINITIONS) {
  282. if (!value.empty()) {
  283. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  284. this->CompileDefinitions.emplace_back(value, lfbt);
  285. }
  286. } else {
  287. this->Properties.AppendProperty(prop, value, asString);
  288. }
  289. }
  290. cmProp cmSourceFile::GetPropertyForUser(const std::string& prop)
  291. {
  292. // This method is a consequence of design history and backwards
  293. // compatibility. GetProperty is (and should be) a const method.
  294. // Computed properties should not be stored back in the property map
  295. // but instead reference information already known. If they need to
  296. // cache information in a mutable ivar to provide the return string
  297. // safely then so be it.
  298. //
  299. // The LOCATION property is particularly problematic. The CMake
  300. // language has very loose restrictions on the names that will match
  301. // a given source file (for historical reasons). Implementing
  302. // lookups correctly with such loose naming requires the
  303. // cmSourceFileLocation class to commit to a particular full path to
  304. // the source file as late as possible. If the users requests the
  305. // LOCATION property we must commit now.
  306. if (prop == propLOCATION) {
  307. // Commit to a location.
  308. this->ResolveFullPath();
  309. }
  310. // Similarly, LANGUAGE can be determined by the file extension
  311. // if it is requested by the user.
  312. if (prop == propLANGUAGE) {
  313. // The pointer is valid until `this->Language` is modified.
  314. return cmProp(this->GetOrDetermineLanguage());
  315. }
  316. // Special handling for GENERATED property.
  317. if (prop == propGENERATED) {
  318. // We need to check policy CMP0118 in order to determine if we need to
  319. // possibly consider the value of a locally set GENERATED property, too.
  320. auto policyStatus =
  321. this->Location.GetMakefile()->GetPolicyStatus(cmPolicies::CMP0118);
  322. if (this->GetIsGenerated(
  323. (policyStatus == cmPolicies::WARN || policyStatus == cmPolicies::OLD)
  324. ? CheckScope::GlobalAndLocal
  325. : CheckScope::Global)) {
  326. return cmProp(propTRUE);
  327. }
  328. return cmProp(propFALSE);
  329. }
  330. // Perform the normal property lookup.
  331. return this->GetProperty(prop);
  332. }
  333. cmProp cmSourceFile::GetProperty(const std::string& prop) const
  334. {
  335. // Check for computed properties.
  336. if (prop == propLOCATION) {
  337. if (this->FullPath.empty()) {
  338. return nullptr;
  339. }
  340. return cmProp(this->FullPath);
  341. }
  342. // Check for the properties with backtraces.
  343. if (prop == propINCLUDE_DIRECTORIES) {
  344. if (this->IncludeDirectories.empty()) {
  345. return nullptr;
  346. }
  347. static std::string output;
  348. output = cmJoin(this->IncludeDirectories, ";");
  349. return cmProp(output);
  350. }
  351. if (prop == propCOMPILE_OPTIONS) {
  352. if (this->CompileOptions.empty()) {
  353. return nullptr;
  354. }
  355. static std::string output;
  356. output = cmJoin(this->CompileOptions, ";");
  357. return cmProp(output);
  358. }
  359. if (prop == propCOMPILE_DEFINITIONS) {
  360. if (this->CompileDefinitions.empty()) {
  361. return nullptr;
  362. }
  363. static std::string output;
  364. output = cmJoin(this->CompileDefinitions, ";");
  365. return cmProp(output);
  366. }
  367. cmProp retVal = this->Properties.GetPropertyValue(prop);
  368. if (!retVal) {
  369. cmMakefile const* mf = this->Location.GetMakefile();
  370. const bool chain =
  371. mf->GetState()->IsPropertyChained(prop, cmProperty::SOURCE_FILE);
  372. if (chain) {
  373. return mf->GetProperty(prop, chain);
  374. }
  375. return nullptr;
  376. }
  377. return retVal;
  378. }
  379. const std::string& cmSourceFile::GetSafeProperty(const std::string& prop) const
  380. {
  381. cmProp ret = this->GetProperty(prop);
  382. if (ret) {
  383. return *ret;
  384. }
  385. static std::string const s_empty;
  386. return s_empty;
  387. }
  388. bool cmSourceFile::GetPropertyAsBool(const std::string& prop) const
  389. {
  390. return cmIsOn(this->GetProperty(prop));
  391. }
  392. void cmSourceFile::MarkAsGenerated()
  393. {
  394. this->IsGenerated = true;
  395. const auto& mf = *this->Location.GetMakefile();
  396. mf.GetGlobalGenerator()->MarkAsGeneratedFile(this->ResolveFullPath());
  397. }
  398. bool cmSourceFile::GetIsGenerated(CheckScope checkScope) const
  399. {
  400. if (this->IsGenerated) {
  401. // Globally marked as generated!
  402. return true;
  403. }
  404. if (checkScope == CheckScope::GlobalAndLocal) {
  405. // Check locally stored properties.
  406. return this->GetPropertyAsBool(propGENERATED);
  407. }
  408. return false;
  409. }
  410. void cmSourceFile::SetProperties(cmPropertyMap properties)
  411. {
  412. this->Properties = std::move(properties);
  413. }
  414. cmCustomCommand* cmSourceFile::GetCustomCommand() const
  415. {
  416. return this->CustomCommand.get();
  417. }
  418. void cmSourceFile::SetCustomCommand(std::unique_ptr<cmCustomCommand> cc)
  419. {
  420. this->CustomCommand = std::move(cc);
  421. }