cmFindLibraryCommand.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 "cmFindLibraryCommand.h"
  4. #include "cmGlobalGenerator.h"
  5. #include "cmState.h"
  6. #include "cmSystemTools.h"
  7. #include "cmVersion.h"
  8. #include <cmsys/Directory.hxx>
  9. cmFindLibraryCommand::cmFindLibraryCommand()
  10. {
  11. this->EnvironmentPath = "LIB";
  12. this->NamesPerDirAllowed = true;
  13. }
  14. // cmFindLibraryCommand
  15. bool cmFindLibraryCommand::InitialPass(std::vector<std::string> const& argsIn,
  16. cmExecutionStatus&)
  17. {
  18. this->VariableDocumentation = "Path to a library.";
  19. this->CMakePathName = "LIBRARY";
  20. if (!this->ParseArguments(argsIn)) {
  21. return false;
  22. }
  23. if (this->AlreadyInCache) {
  24. // If the user specifies the entry on the command line without a
  25. // type we should add the type and docstring but keep the original
  26. // value.
  27. if (this->AlreadyInCacheWithoutMetaInfo) {
  28. this->Makefile->AddCacheDefinition(this->VariableName, "",
  29. this->VariableDocumentation.c_str(),
  30. cmStateEnums::FILEPATH);
  31. }
  32. return true;
  33. }
  34. if (this->Makefile->GetState()->GetGlobalPropertyAsBool(
  35. "FIND_LIBRARY_USE_LIB32_PATHS")) {
  36. // add special 32 bit paths if this is a 32 bit compile.
  37. if (this->Makefile->PlatformIs32Bit()) {
  38. this->AddArchitecturePaths("32");
  39. }
  40. }
  41. if (this->Makefile->GetState()->GetGlobalPropertyAsBool(
  42. "FIND_LIBRARY_USE_LIB64_PATHS")) {
  43. // add special 64 bit paths if this is a 64 bit compile.
  44. if (this->Makefile->PlatformIs64Bit()) {
  45. this->AddArchitecturePaths("64");
  46. }
  47. }
  48. std::string library = this->FindLibrary();
  49. if (library != "") {
  50. // Save the value in the cache
  51. this->Makefile->AddCacheDefinition(this->VariableName, library.c_str(),
  52. this->VariableDocumentation.c_str(),
  53. cmStateEnums::FILEPATH);
  54. return true;
  55. }
  56. std::string notfound = this->VariableName + "-NOTFOUND";
  57. this->Makefile->AddCacheDefinition(this->VariableName, notfound.c_str(),
  58. this->VariableDocumentation.c_str(),
  59. cmStateEnums::FILEPATH);
  60. return true;
  61. }
  62. void cmFindLibraryCommand::AddArchitecturePaths(const char* suffix)
  63. {
  64. std::vector<std::string> original;
  65. original.swap(this->SearchPaths);
  66. for (std::vector<std::string>::const_iterator i = original.begin();
  67. i != original.end(); ++i) {
  68. this->AddArchitecturePath(*i, 0, suffix);
  69. }
  70. }
  71. void cmFindLibraryCommand::AddArchitecturePath(
  72. std::string const& dir, std::string::size_type start_pos, const char* suffix,
  73. bool fresh)
  74. {
  75. std::string::size_type pos = dir.find("lib/", start_pos);
  76. if (pos != std::string::npos) {
  77. std::string cur_dir = dir.substr(0, pos + 3);
  78. // Follow "lib<suffix>".
  79. std::string next_dir = cur_dir + suffix;
  80. if (cmSystemTools::FileIsDirectory(next_dir)) {
  81. next_dir += dir.substr(pos + 3);
  82. std::string::size_type next_pos = pos + 3 + strlen(suffix) + 1;
  83. this->AddArchitecturePath(next_dir, next_pos, suffix);
  84. }
  85. // Follow "lib".
  86. if (cmSystemTools::FileIsDirectory(cur_dir)) {
  87. this->AddArchitecturePath(dir, pos + 3 + 1, suffix, false);
  88. }
  89. }
  90. if (fresh) {
  91. // Check for <dir><suffix>/.
  92. std::string cur_dir = dir + suffix + "/";
  93. if (cmSystemTools::FileIsDirectory(cur_dir)) {
  94. this->SearchPaths.push_back(cur_dir);
  95. }
  96. // Now add the original unchanged path
  97. if (cmSystemTools::FileIsDirectory(dir)) {
  98. this->SearchPaths.push_back(dir);
  99. }
  100. }
  101. }
  102. std::string cmFindLibraryCommand::FindLibrary()
  103. {
  104. std::string library;
  105. if (this->SearchFrameworkFirst || this->SearchFrameworkOnly) {
  106. library = this->FindFrameworkLibrary();
  107. }
  108. if (library.empty() && !this->SearchFrameworkOnly) {
  109. library = this->FindNormalLibrary();
  110. }
  111. if (library.empty() && this->SearchFrameworkLast) {
  112. library = this->FindFrameworkLibrary();
  113. }
  114. return library;
  115. }
  116. struct cmFindLibraryHelper
  117. {
  118. cmFindLibraryHelper(cmMakefile* mf);
  119. // Context information.
  120. cmMakefile* Makefile;
  121. cmGlobalGenerator* GG;
  122. // List of valid prefixes and suffixes.
  123. std::vector<std::string> Prefixes;
  124. std::vector<std::string> Suffixes;
  125. std::string PrefixRegexStr;
  126. std::string SuffixRegexStr;
  127. // Keep track of the best library file found so far.
  128. typedef std::vector<std::string>::size_type size_type;
  129. std::string BestPath;
  130. // Support for OpenBSD shared library naming: lib<name>.so.<major>.<minor>
  131. bool OpenBSD;
  132. // Current names under consideration.
  133. struct Name
  134. {
  135. bool TryRaw;
  136. std::string Raw;
  137. cmsys::RegularExpression Regex;
  138. Name()
  139. : TryRaw(false)
  140. {
  141. }
  142. };
  143. std::vector<Name> Names;
  144. // Current full path under consideration.
  145. std::string TestPath;
  146. void RegexFromLiteral(std::string& out, std::string const& in);
  147. void RegexFromList(std::string& out, std::vector<std::string> const& in);
  148. size_type GetPrefixIndex(std::string const& prefix)
  149. {
  150. return std::find(this->Prefixes.begin(), this->Prefixes.end(), prefix) -
  151. this->Prefixes.begin();
  152. }
  153. size_type GetSuffixIndex(std::string const& suffix)
  154. {
  155. return std::find(this->Suffixes.begin(), this->Suffixes.end(), suffix) -
  156. this->Suffixes.begin();
  157. }
  158. bool HasValidSuffix(std::string const& name);
  159. void AddName(std::string const& name);
  160. void SetName(std::string const& name);
  161. bool CheckDirectory(std::string const& path);
  162. bool CheckDirectoryForName(std::string const& path, Name& name);
  163. };
  164. cmFindLibraryHelper::cmFindLibraryHelper(cmMakefile* mf)
  165. : Makefile(mf)
  166. {
  167. this->GG = this->Makefile->GetGlobalGenerator();
  168. // Collect the list of library name prefixes/suffixes to try.
  169. const char* prefixes_list =
  170. this->Makefile->GetRequiredDefinition("CMAKE_FIND_LIBRARY_PREFIXES");
  171. const char* suffixes_list =
  172. this->Makefile->GetRequiredDefinition("CMAKE_FIND_LIBRARY_SUFFIXES");
  173. cmSystemTools::ExpandListArgument(prefixes_list, this->Prefixes, true);
  174. cmSystemTools::ExpandListArgument(suffixes_list, this->Suffixes, true);
  175. this->RegexFromList(this->PrefixRegexStr, this->Prefixes);
  176. this->RegexFromList(this->SuffixRegexStr, this->Suffixes);
  177. // Check whether to use OpenBSD-style library version comparisons.
  178. this->OpenBSD = this->Makefile->GetState()->GetGlobalPropertyAsBool(
  179. "FIND_LIBRARY_USE_OPENBSD_VERSIONING");
  180. }
  181. void cmFindLibraryHelper::RegexFromLiteral(std::string& out,
  182. std::string const& in)
  183. {
  184. for (std::string::const_iterator ci = in.begin(); ci != in.end(); ++ci) {
  185. char ch = *ci;
  186. if (ch == '[' || ch == ']' || ch == '(' || ch == ')' || ch == '\\' ||
  187. ch == '.' || ch == '*' || ch == '+' || ch == '?' || ch == '-' ||
  188. ch == '^' || ch == '$') {
  189. out += "\\";
  190. }
  191. #if defined(_WIN32) || defined(__APPLE__)
  192. out += tolower(ch);
  193. #else
  194. out += ch;
  195. #endif
  196. }
  197. }
  198. void cmFindLibraryHelper::RegexFromList(std::string& out,
  199. std::vector<std::string> const& in)
  200. {
  201. // Surround the list in parens so the '|' does not apply to anything
  202. // else and the result can be checked after matching.
  203. out += "(";
  204. const char* sep = "";
  205. for (std::vector<std::string>::const_iterator si = in.begin();
  206. si != in.end(); ++si) {
  207. // Separate from previous item.
  208. out += sep;
  209. sep = "|";
  210. // Append this item.
  211. this->RegexFromLiteral(out, *si);
  212. }
  213. out += ")";
  214. }
  215. bool cmFindLibraryHelper::HasValidSuffix(std::string const& name)
  216. {
  217. for (std::vector<std::string>::const_iterator si = this->Suffixes.begin();
  218. si != this->Suffixes.end(); ++si) {
  219. std::string suffix = *si;
  220. if (name.length() <= suffix.length()) {
  221. continue;
  222. }
  223. // Check if the given name ends in a valid library suffix.
  224. if (name.substr(name.size() - suffix.length()) == suffix) {
  225. return true;
  226. }
  227. // Check if a valid library suffix is somewhere in the name,
  228. // this may happen e.g. for versioned shared libraries: libfoo.so.2
  229. suffix += ".";
  230. if (name.find(suffix) != name.npos) {
  231. return true;
  232. }
  233. }
  234. return false;
  235. }
  236. void cmFindLibraryHelper::AddName(std::string const& name)
  237. {
  238. Name entry;
  239. // Consider checking the raw name too.
  240. entry.TryRaw = this->HasValidSuffix(name);
  241. entry.Raw = name;
  242. // Build a regular expression to match library names.
  243. std::string regex = "^";
  244. regex += this->PrefixRegexStr;
  245. this->RegexFromLiteral(regex, name);
  246. regex += this->SuffixRegexStr;
  247. if (this->OpenBSD) {
  248. regex += "(\\.[0-9]+\\.[0-9]+)?";
  249. }
  250. regex += "$";
  251. entry.Regex.compile(regex.c_str());
  252. this->Names.push_back(entry);
  253. }
  254. void cmFindLibraryHelper::SetName(std::string const& name)
  255. {
  256. this->Names.clear();
  257. this->AddName(name);
  258. }
  259. bool cmFindLibraryHelper::CheckDirectory(std::string const& path)
  260. {
  261. for (std::vector<Name>::iterator i = this->Names.begin();
  262. i != this->Names.end(); ++i) {
  263. if (this->CheckDirectoryForName(path, *i)) {
  264. return true;
  265. }
  266. }
  267. return false;
  268. }
  269. bool cmFindLibraryHelper::CheckDirectoryForName(std::string const& path,
  270. Name& name)
  271. {
  272. // If the original library name provided by the user matches one of
  273. // the suffixes, try it first. This allows users to search
  274. // specifically for a static library on some platforms (on MS tools
  275. // one cannot tell just from the library name whether it is a static
  276. // library or an import library).
  277. if (name.TryRaw) {
  278. this->TestPath = path;
  279. this->TestPath += name.Raw;
  280. if (cmSystemTools::FileExists(this->TestPath.c_str(), true)) {
  281. this->BestPath = cmSystemTools::CollapseFullPath(this->TestPath);
  282. cmSystemTools::ConvertToUnixSlashes(this->BestPath);
  283. return true;
  284. }
  285. }
  286. // No library file has yet been found.
  287. size_type bestPrefix = this->Prefixes.size();
  288. size_type bestSuffix = this->Suffixes.size();
  289. unsigned int bestMajor = 0;
  290. unsigned int bestMinor = 0;
  291. // Search for a file matching the library name regex.
  292. std::string dir = path;
  293. cmSystemTools::ConvertToUnixSlashes(dir);
  294. std::set<std::string> const& files = this->GG->GetDirectoryContent(dir);
  295. for (std::set<std::string>::const_iterator fi = files.begin();
  296. fi != files.end(); ++fi) {
  297. std::string const& origName = *fi;
  298. #if defined(_WIN32) || defined(__APPLE__)
  299. std::string testName = cmSystemTools::LowerCase(origName);
  300. #else
  301. std::string const& testName = origName;
  302. #endif
  303. if (name.Regex.find(testName)) {
  304. this->TestPath = path;
  305. this->TestPath += origName;
  306. if (!cmSystemTools::FileIsDirectory(this->TestPath)) {
  307. // This is a matching file. Check if it is better than the
  308. // best name found so far. Earlier prefixes are preferred,
  309. // followed by earlier suffixes. For OpenBSD, shared library
  310. // version extensions are compared.
  311. size_type prefix = this->GetPrefixIndex(name.Regex.match(1));
  312. size_type suffix = this->GetSuffixIndex(name.Regex.match(2));
  313. unsigned int major = 0;
  314. unsigned int minor = 0;
  315. if (this->OpenBSD) {
  316. sscanf(name.Regex.match(3).c_str(), ".%u.%u", &major, &minor);
  317. }
  318. if (this->BestPath.empty() || prefix < bestPrefix ||
  319. (prefix == bestPrefix && suffix < bestSuffix) ||
  320. (prefix == bestPrefix && suffix == bestSuffix &&
  321. (major > bestMajor ||
  322. (major == bestMajor && minor > bestMinor)))) {
  323. this->BestPath = this->TestPath;
  324. bestPrefix = prefix;
  325. bestSuffix = suffix;
  326. bestMajor = major;
  327. bestMinor = minor;
  328. }
  329. }
  330. }
  331. }
  332. // Use the best candidate found in this directory, if any.
  333. return !this->BestPath.empty();
  334. }
  335. std::string cmFindLibraryCommand::FindNormalLibrary()
  336. {
  337. if (this->NamesPerDir) {
  338. return this->FindNormalLibraryNamesPerDir();
  339. }
  340. return this->FindNormalLibraryDirsPerName();
  341. }
  342. std::string cmFindLibraryCommand::FindNormalLibraryNamesPerDir()
  343. {
  344. // Search for all names in each directory.
  345. cmFindLibraryHelper helper(this->Makefile);
  346. for (std::vector<std::string>::const_iterator ni = this->Names.begin();
  347. ni != this->Names.end(); ++ni) {
  348. helper.AddName(*ni);
  349. }
  350. // Search every directory.
  351. for (std::vector<std::string>::const_iterator p = this->SearchPaths.begin();
  352. p != this->SearchPaths.end(); ++p) {
  353. if (helper.CheckDirectory(*p)) {
  354. return helper.BestPath;
  355. }
  356. }
  357. // Couldn't find the library.
  358. return "";
  359. }
  360. std::string cmFindLibraryCommand::FindNormalLibraryDirsPerName()
  361. {
  362. // Search the entire path for each name.
  363. cmFindLibraryHelper helper(this->Makefile);
  364. for (std::vector<std::string>::const_iterator ni = this->Names.begin();
  365. ni != this->Names.end(); ++ni) {
  366. // Switch to searching for this name.
  367. helper.SetName(*ni);
  368. // Search every directory.
  369. for (std::vector<std::string>::const_iterator p =
  370. this->SearchPaths.begin();
  371. p != this->SearchPaths.end(); ++p) {
  372. if (helper.CheckDirectory(*p)) {
  373. return helper.BestPath;
  374. }
  375. }
  376. }
  377. // Couldn't find the library.
  378. return "";
  379. }
  380. std::string cmFindLibraryCommand::FindFrameworkLibrary()
  381. {
  382. if (this->NamesPerDir) {
  383. return this->FindFrameworkLibraryNamesPerDir();
  384. }
  385. return this->FindFrameworkLibraryDirsPerName();
  386. }
  387. std::string cmFindLibraryCommand::FindFrameworkLibraryNamesPerDir()
  388. {
  389. std::string fwPath;
  390. // Search for all names in each search path.
  391. for (std::vector<std::string>::const_iterator di = this->SearchPaths.begin();
  392. di != this->SearchPaths.end(); ++di) {
  393. for (std::vector<std::string>::const_iterator ni = this->Names.begin();
  394. ni != this->Names.end(); ++ni) {
  395. fwPath = *di;
  396. fwPath += *ni;
  397. fwPath += ".framework";
  398. if (cmSystemTools::FileIsDirectory(fwPath)) {
  399. return cmSystemTools::CollapseFullPath(fwPath);
  400. }
  401. }
  402. }
  403. // No framework found.
  404. return "";
  405. }
  406. std::string cmFindLibraryCommand::FindFrameworkLibraryDirsPerName()
  407. {
  408. std::string fwPath;
  409. // Search for each name in all search paths.
  410. for (std::vector<std::string>::const_iterator ni = this->Names.begin();
  411. ni != this->Names.end(); ++ni) {
  412. for (std::vector<std::string>::const_iterator di =
  413. this->SearchPaths.begin();
  414. di != this->SearchPaths.end(); ++di) {
  415. fwPath = *di;
  416. fwPath += *ni;
  417. fwPath += ".framework";
  418. if (cmSystemTools::FileIsDirectory(fwPath)) {
  419. return cmSystemTools::CollapseFullPath(fwPath);
  420. }
  421. }
  422. }
  423. // No framework found.
  424. return "";
  425. }