cmSourceFile.cxx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  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. 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 (cmProp 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->Language.empty()) {
  50. // If a known extension is given or a known full path is given then trust
  51. // that the current extension is sufficient to determine the language. This
  52. // will fail only if the user specifies a full path to the source but
  53. // leaves off the extension, which is kind of weird.
  54. if (this->FullPath.empty() && this->Location.ExtensionIsAmbiguous() &&
  55. this->Location.DirectoryIsAmbiguous()) {
  56. // Finalize the file location to get the extension and set the language.
  57. this->ResolveFullPath();
  58. } else {
  59. // Use the known extension to get the language if possible.
  60. std::string ext =
  61. cmSystemTools::GetFilenameLastExtension(this->Location.GetName());
  62. this->CheckLanguage(ext);
  63. }
  64. }
  65. // Use the language determined from the file extension.
  66. return this->Language;
  67. }
  68. std::string cmSourceFile::GetLanguage() const
  69. {
  70. // If the language was set explicitly by the user then use it.
  71. if (cmProp lang = this->GetProperty(propLANGUAGE)) {
  72. return *lang;
  73. }
  74. // Use the language determined from the file extension.
  75. return this->Language;
  76. }
  77. cmSourceFileLocation const& cmSourceFile::GetLocation() const
  78. {
  79. return this->Location;
  80. }
  81. std::string const& cmSourceFile::ResolveFullPath(std::string* error,
  82. std::string* cmp0115Warning)
  83. {
  84. if (this->FullPath.empty()) {
  85. if (this->FindFullPath(error, cmp0115Warning)) {
  86. this->CheckExtension();
  87. }
  88. }
  89. return this->FullPath;
  90. }
  91. std::string const& cmSourceFile::GetFullPath() const
  92. {
  93. return this->FullPath;
  94. }
  95. bool cmSourceFile::FindFullPath(std::string* error,
  96. std::string* cmp0115Warning)
  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::vector<std::string> exts =
  116. makefile->GetCMakeInstance()->GetAllExtensions();
  117. auto cmp0115 = makefile->GetPolicyStatus(cmPolicies::CMP0115);
  118. // Tries to find the file in a given directory
  119. auto findInDir = [this, &exts, &lPath, cmp0115, cmp0115Warning,
  120. makefile](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. // This has to be an if statement due to a bug in Oracle Developer Studio.
  129. // See https://community.oracle.com/tech/developers/discussion/4476246/
  130. // for details.
  131. if (cmp0115 == cmPolicies::OLD || cmp0115 == cmPolicies::WARN) {
  132. // Try full path with extension
  133. for (std::string const& ext : exts) {
  134. if (!ext.empty()) {
  135. std::string extPath = cmStrCat(fullPath, '.', ext);
  136. if (cmSystemTools::FileExists(extPath)) {
  137. this->FullPath = extPath;
  138. if (cmp0115 == cmPolicies::WARN) {
  139. std::string warning =
  140. cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0115),
  141. "\nFile:\n ", extPath);
  142. if (cmp0115Warning) {
  143. *cmp0115Warning = std::move(warning);
  144. } else {
  145. makefile->GetCMakeInstance()->IssueMessage(
  146. MessageType::AUTHOR_WARNING, warning);
  147. }
  148. }
  149. return true;
  150. }
  151. }
  152. }
  153. }
  154. // File not found
  155. return false;
  156. };
  157. // Try to find the file in various directories
  158. if (this->Location.DirectoryIsAmbiguous()) {
  159. if (findInDir(makefile->GetCurrentSourceDirectory()) ||
  160. findInDir(makefile->GetCurrentBinaryDirectory())) {
  161. return true;
  162. }
  163. } else {
  164. if (findInDir({})) {
  165. return true;
  166. }
  167. }
  168. // Compose error
  169. std::string err = cmStrCat("Cannot find source file:\n ", lPath);
  170. switch (cmp0115) {
  171. case cmPolicies::OLD:
  172. case cmPolicies::WARN:
  173. err = cmStrCat(err, "\nTried extensions");
  174. for (auto const& ext : exts) {
  175. err = cmStrCat(err, " .", ext);
  176. }
  177. break;
  178. case cmPolicies::REQUIRED_IF_USED:
  179. case cmPolicies::REQUIRED_ALWAYS:
  180. case cmPolicies::NEW:
  181. break;
  182. }
  183. if (error != nullptr) {
  184. *error = std::move(err);
  185. } else {
  186. makefile->IssueMessage(MessageType::FATAL_ERROR, err);
  187. }
  188. this->FindFullPathFailed = true;
  189. // File not found
  190. return false;
  191. }
  192. void cmSourceFile::CheckExtension()
  193. {
  194. // Compute the extension.
  195. std::string realExt =
  196. cmSystemTools::GetFilenameLastExtension(this->FullPath);
  197. if (!realExt.empty()) {
  198. // Store the extension without the leading '.'.
  199. this->Extension = realExt.substr(1);
  200. }
  201. // Look for object files.
  202. if (this->Extension == "obj" || this->Extension == "o" ||
  203. this->Extension == "lo") {
  204. this->SetProperty("EXTERNAL_OBJECT", "1");
  205. }
  206. // Try to identify the source file language from the extension.
  207. if (this->Language.empty()) {
  208. this->CheckLanguage(this->Extension);
  209. }
  210. }
  211. void cmSourceFile::CheckLanguage(std::string const& ext)
  212. {
  213. // Try to identify the source file language from the extension.
  214. cmMakefile const* mf = this->Location.GetMakefile();
  215. cmGlobalGenerator* gg = mf->GetGlobalGenerator();
  216. std::string l = gg->GetLanguageFromExtension(ext.c_str());
  217. if (!l.empty()) {
  218. this->Language = l;
  219. }
  220. }
  221. bool cmSourceFile::Matches(cmSourceFileLocation const& loc)
  222. {
  223. return this->Location.Matches(loc);
  224. }
  225. void cmSourceFile::SetProperty(const std::string& prop, const char* value)
  226. {
  227. if (prop == propINCLUDE_DIRECTORIES) {
  228. this->IncludeDirectories.clear();
  229. if (value) {
  230. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  231. this->IncludeDirectories.emplace_back(value, lfbt);
  232. }
  233. } else if (prop == propCOMPILE_OPTIONS) {
  234. this->CompileOptions.clear();
  235. if (value) {
  236. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  237. this->CompileOptions.emplace_back(value, lfbt);
  238. }
  239. } else if (prop == propCOMPILE_DEFINITIONS) {
  240. this->CompileDefinitions.clear();
  241. if (value) {
  242. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  243. this->CompileDefinitions.emplace_back(value, lfbt);
  244. }
  245. } else {
  246. this->Properties.SetProperty(prop, value);
  247. }
  248. // Update IsGenerated flag
  249. if (prop == propGENERATED) {
  250. this->IsGenerated = cmIsOn(value);
  251. }
  252. }
  253. void cmSourceFile::AppendProperty(const std::string& prop,
  254. const std::string& value, bool asString)
  255. {
  256. if (prop == propINCLUDE_DIRECTORIES) {
  257. if (!value.empty()) {
  258. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  259. this->IncludeDirectories.emplace_back(value, lfbt);
  260. }
  261. } else if (prop == propCOMPILE_OPTIONS) {
  262. if (!value.empty()) {
  263. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  264. this->CompileOptions.emplace_back(value, lfbt);
  265. }
  266. } else if (prop == propCOMPILE_DEFINITIONS) {
  267. if (!value.empty()) {
  268. cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
  269. this->CompileDefinitions.emplace_back(value, lfbt);
  270. }
  271. } else {
  272. this->Properties.AppendProperty(prop, value, asString);
  273. }
  274. // Update IsGenerated flag
  275. if (prop == propGENERATED) {
  276. this->IsGenerated = this->GetPropertyAsBool(propGENERATED);
  277. }
  278. }
  279. cmProp cmSourceFile::GetPropertyForUser(const std::string& prop)
  280. {
  281. // This method is a consequence of design history and backwards
  282. // compatibility. GetProperty is (and should be) a const method.
  283. // Computed properties should not be stored back in the property map
  284. // but instead reference information already known. If they need to
  285. // cache information in a mutable ivar to provide the return string
  286. // safely then so be it.
  287. //
  288. // The LOCATION property is particularly problematic. The CMake
  289. // language has very loose restrictions on the names that will match
  290. // a given source file (for historical reasons). Implementing
  291. // lookups correctly with such loose naming requires the
  292. // cmSourceFileLocation class to commit to a particular full path to
  293. // the source file as late as possible. If the users requests the
  294. // LOCATION property we must commit now.
  295. if (prop == propLOCATION) {
  296. // Commit to a location.
  297. this->ResolveFullPath();
  298. }
  299. // Similarly, LANGUAGE can be determined by the file extension
  300. // if it is requested by the user.
  301. if (prop == propLANGUAGE) {
  302. // The pointer is valid until `this->Language` is modified.
  303. return &this->GetOrDetermineLanguage();
  304. }
  305. // Perform the normal property lookup.
  306. return this->GetProperty(prop);
  307. }
  308. cmProp cmSourceFile::GetProperty(const std::string& prop) const
  309. {
  310. // Check for computed properties.
  311. if (prop == propLOCATION) {
  312. if (this->FullPath.empty()) {
  313. return nullptr;
  314. }
  315. return &this->FullPath;
  316. }
  317. // Check for the properties with backtraces.
  318. if (prop == propINCLUDE_DIRECTORIES) {
  319. if (this->IncludeDirectories.empty()) {
  320. return nullptr;
  321. }
  322. static std::string output;
  323. output = cmJoin(this->IncludeDirectories, ";");
  324. return &output;
  325. }
  326. if (prop == propCOMPILE_OPTIONS) {
  327. if (this->CompileOptions.empty()) {
  328. return nullptr;
  329. }
  330. static std::string output;
  331. output = cmJoin(this->CompileOptions, ";");
  332. return &output;
  333. }
  334. if (prop == propCOMPILE_DEFINITIONS) {
  335. if (this->CompileDefinitions.empty()) {
  336. return nullptr;
  337. }
  338. static std::string output;
  339. output = cmJoin(this->CompileDefinitions, ";");
  340. return &output;
  341. }
  342. cmProp retVal = this->Properties.GetPropertyValue(prop);
  343. if (!retVal) {
  344. cmMakefile const* mf = this->Location.GetMakefile();
  345. const bool chain =
  346. mf->GetState()->IsPropertyChained(prop, cmProperty::SOURCE_FILE);
  347. if (chain) {
  348. return mf->GetProperty(prop, chain);
  349. }
  350. return nullptr;
  351. }
  352. return retVal;
  353. }
  354. const std::string& cmSourceFile::GetSafeProperty(const std::string& prop) const
  355. {
  356. cmProp ret = this->GetProperty(prop);
  357. if (ret) {
  358. return *ret;
  359. }
  360. static std::string const s_empty;
  361. return s_empty;
  362. }
  363. bool cmSourceFile::GetPropertyAsBool(const std::string& prop) const
  364. {
  365. return cmIsOn(this->GetProperty(prop));
  366. }
  367. void cmSourceFile::SetProperties(cmPropertyMap properties)
  368. {
  369. this->Properties = std::move(properties);
  370. this->IsGenerated = this->GetPropertyAsBool(propGENERATED);
  371. }
  372. cmCustomCommand* cmSourceFile::GetCustomCommand() const
  373. {
  374. return this->CustomCommand.get();
  375. }
  376. void cmSourceFile::SetCustomCommand(std::unique_ptr<cmCustomCommand> cc)
  377. {
  378. this->CustomCommand = std::move(cc);
  379. }