Updated formatting of documentation plus a little reorganization.
[cmake.git] / Source / cmFindLibraryCommand.cxx
blob5a493ac6ebce23fcaf79ce7fdef7ee95716a07ca
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmFindLibraryCommand.cxx,v $
5 Language: C++
6 Date: $Date: 2008-09-23 17:34:23 $
7 Version: $Revision: 1.63 $
9 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
17 #include "cmFindLibraryCommand.h"
18 #include "cmCacheManager.h"
19 #include <cmsys/Directory.hxx>
20 #include <cmsys/stl/algorithm>
22 cmFindLibraryCommand::cmFindLibraryCommand()
24 cmSystemTools::ReplaceString(this->GenericDocumentation,
25 "FIND_XXX", "find_library");
26 cmSystemTools::ReplaceString(this->GenericDocumentation,
27 "CMAKE_XXX_PATH", "CMAKE_LIBRARY_PATH");
28 cmSystemTools::ReplaceString(this->GenericDocumentation,
29 "CMAKE_XXX_MAC_PATH",
30 "CMAKE_FRAMEWORK_PATH");
31 cmSystemTools::ReplaceString(this->GenericDocumentation,
32 "CMAKE_SYSTEM_XXX_MAC_PATH",
33 "CMAKE_SYSTEM_FRAMEWORK_PATH");
34 cmSystemTools::ReplaceString(this->GenericDocumentation,
35 "XXX_SYSTEM", "LIB");
36 cmSystemTools::ReplaceString(this->GenericDocumentation,
37 "CMAKE_SYSTEM_XXX_PATH",
38 "CMAKE_SYSTEM_LIBRARY_PATH");
39 cmSystemTools::ReplaceString(this->GenericDocumentation,
40 "SEARCH_XXX_DESC", "library");
41 cmSystemTools::ReplaceString(this->GenericDocumentation,
42 "SEARCH_XXX", "library");
43 cmSystemTools::ReplaceString(this->GenericDocumentation,
44 "XXX_SUBDIR", "lib");
45 cmSystemTools::ReplaceString(this->GenericDocumentation,
46 "CMAKE_FIND_ROOT_PATH_MODE_XXX",
47 "CMAKE_FIND_ROOT_PATH_MODE_LIBRARY");
49 this->EnvironmentPath = "LIB";
50 this->GenericDocumentation +=
51 "\n"
52 "If the library found is a framework, then VAR will be set to "
53 "the full path to the framework <fullPath>/A.framework. "
54 "When a full path to a framework is used as a library, "
55 "CMake will use a -framework A, and a -F<fullPath> to "
56 "link the framework to the target. ";
59 // cmFindLibraryCommand
60 bool cmFindLibraryCommand
61 ::InitialPass(std::vector<std::string> const& argsIn, cmExecutionStatus &)
63 this->VariableDocumentation = "Path to a library.";
64 this->CMakePathName = "LIBRARY";
65 if(!this->ParseArguments(argsIn))
67 return false;
69 if(this->AlreadyInCache)
71 // If the user specifies the entry on the command line without a
72 // type we should add the type and docstring but keep the original
73 // value.
74 if(this->AlreadyInCacheWithoutMetaInfo)
76 this->Makefile->AddCacheDefinition(this->VariableName.c_str(), "",
77 this->VariableDocumentation.c_str(),
78 cmCacheManager::FILEPATH);
80 return true;
83 if(const char* abi_name =
84 this->Makefile->GetDefinition("CMAKE_INTERNAL_PLATFORM_ABI"))
86 std::string abi = abi_name;
87 if(abi.find("ELF N32") != abi.npos)
89 // Convert lib to lib32.
90 this->AddArchitecturePaths("32");
94 if(this->Makefile->GetCMakeInstance()
95 ->GetPropertyAsBool("FIND_LIBRARY_USE_LIB64_PATHS"))
97 // add special 64 bit paths if this is a 64 bit compile.
98 this->AddLib64Paths();
101 std::string library = this->FindLibrary();
102 if(library != "")
104 // Save the value in the cache
105 this->Makefile->AddCacheDefinition(this->VariableName.c_str(),
106 library.c_str(),
107 this->VariableDocumentation.c_str(),
108 cmCacheManager::FILEPATH);
109 return true;
111 std::string notfound = this->VariableName + "-NOTFOUND";
112 this->Makefile->AddCacheDefinition(this->VariableName.c_str(),
113 notfound.c_str(),
114 this->VariableDocumentation.c_str(),
115 cmCacheManager::FILEPATH);
116 return true;
119 //----------------------------------------------------------------------------
120 void cmFindLibraryCommand::AddArchitecturePaths(const char* suffix)
122 std::vector<std::string> newPaths;
123 bool found = false;
124 std::string subpath = "lib";
125 subpath += suffix;
126 subpath += "/";
127 for(std::vector<std::string>::iterator i = this->SearchPaths.begin();
128 i != this->SearchPaths.end(); ++i)
130 // Try replacing lib/ with lib<suffix>/
131 std::string s = *i;
132 cmSystemTools::ReplaceString(s, "lib/", subpath.c_str());
133 if((s != *i) && cmSystemTools::FileIsDirectory(s.c_str()))
135 found = true;
136 newPaths.push_back(s);
139 // Now look for lib<suffix>
140 s = *i;
141 s += suffix;
142 if(cmSystemTools::FileIsDirectory(s.c_str()))
144 found = true;
145 newPaths.push_back(s);
147 // now add the original unchanged path
148 if(cmSystemTools::FileIsDirectory(i->c_str()))
150 newPaths.push_back(*i);
154 // If any new paths were found replace the original set.
155 if(found)
157 this->SearchPaths = newPaths;
161 void cmFindLibraryCommand::AddLib64Paths()
163 if(!this->Makefile->GetLocalGenerator()->GetGlobalGenerator()->
164 GetLanguageEnabled("C"))
166 return;
168 std::string voidsize =
169 this->Makefile->GetSafeDefinition("CMAKE_SIZEOF_VOID_P");
170 int size = atoi(voidsize.c_str());
171 if(size != 8)
173 return;
175 std::vector<std::string> path64;
176 bool found64 = false;
177 for(std::vector<std::string>::iterator i = this->SearchPaths.begin();
178 i != this->SearchPaths.end(); ++i)
180 std::string s = *i;
181 std::string s2 = *i;
182 cmSystemTools::ReplaceString(s, "lib/", "lib64/");
183 // try to replace lib with lib64 and see if it is there,
184 // then prepend it to the path
185 // Note that all paths have trailing slashes.
186 if((s != *i) && cmSystemTools::FileIsDirectory(s.c_str()))
188 path64.push_back(s);
189 found64 = true;
191 // now just add a 64 to the path name and if it is there,
192 // add it to the path
193 s2 += "64/";
194 if(cmSystemTools::FileIsDirectory(s2.c_str()))
196 found64 = true;
197 path64.push_back(s2);
199 // now add the original unchanged path
200 if(cmSystemTools::FileIsDirectory(i->c_str()))
202 path64.push_back(*i);
205 // now replace the SearchPaths with the 64 bit converted path
206 // if any 64 bit paths were discovered
207 if(found64)
209 this->SearchPaths = path64;
213 //----------------------------------------------------------------------------
214 std::string cmFindLibraryCommand::FindLibrary()
216 std::string library;
217 if(this->SearchFrameworkFirst || this->SearchFrameworkOnly)
219 library = this->FindFrameworkLibrary();
221 if(library.empty() && !this->SearchFrameworkOnly)
223 library = this->FindNormalLibrary();
225 if(library.empty() && this->SearchFrameworkLast)
227 library = this->FindFrameworkLibrary();
229 return library;
232 //----------------------------------------------------------------------------
233 struct cmFindLibraryHelper
235 cmFindLibraryHelper(cmMakefile* mf);
237 // Context information.
238 cmMakefile* Makefile;
239 cmGlobalGenerator* GG;
241 // List of valid prefixes and suffixes.
242 std::vector<std::string> Prefixes;
243 std::vector<std::string> Suffixes;
244 std::string PrefixRegexStr;
245 std::string SuffixRegexStr;
247 // Keep track of the best library file found so far.
248 typedef std::vector<std::string>::size_type size_type;
249 std::string BestPath;
250 size_type BestPrefix;
251 size_type BestSuffix;
253 // Support for OpenBSD shared library naming: lib<name>.so.<major>.<minor>
254 bool OpenBSD;
255 unsigned int BestMajor;
256 unsigned int BestMinor;
258 // Current name under consideration.
259 cmsys::RegularExpression NameRegex;
260 bool TryRawName;
261 std::string RawName;
263 // Current full path under consideration.
264 std::string TestPath;
266 void RegexFromLiteral(std::string& out, std::string const& in);
267 void RegexFromList(std::string& out, std::vector<std::string> const& in);
268 size_type GetPrefixIndex(std::string const& prefix)
270 return cmsys_stl::find(this->Prefixes.begin(), this->Prefixes.end(),
271 prefix) - this->Prefixes.begin();
273 size_type GetSuffixIndex(std::string const& suffix)
275 return cmsys_stl::find(this->Suffixes.begin(), this->Suffixes.end(),
276 suffix) - this->Suffixes.begin();
278 bool HasValidSuffix(std::string const& name);
279 void SetName(std::string const& name);
280 bool CheckDirectory(std::string const& path);
283 //----------------------------------------------------------------------------
284 cmFindLibraryHelper::cmFindLibraryHelper(cmMakefile* mf):
285 Makefile(mf)
287 this->GG = this->Makefile->GetLocalGenerator()->GetGlobalGenerator();
289 // Collect the list of library name prefixes/suffixes to try.
290 const char* prefixes_list =
291 this->Makefile->GetRequiredDefinition("CMAKE_FIND_LIBRARY_PREFIXES");
292 const char* suffixes_list =
293 this->Makefile->GetRequiredDefinition("CMAKE_FIND_LIBRARY_SUFFIXES");
294 cmSystemTools::ExpandListArgument(prefixes_list, this->Prefixes, true);
295 cmSystemTools::ExpandListArgument(suffixes_list, this->Suffixes, true);
296 this->RegexFromList(this->PrefixRegexStr, this->Prefixes);
297 this->RegexFromList(this->SuffixRegexStr, this->Suffixes);
299 // Check whether to use OpenBSD-style library version comparisons.
300 this->OpenBSD =
301 this->Makefile->GetCMakeInstance()
302 ->GetPropertyAsBool("FIND_LIBRARY_USE_OPENBSD_VERSIONING");
304 this->TryRawName = false;
306 // No library file has yet been found.
307 this->BestPrefix = this->Prefixes.size();
308 this->BestSuffix = this->Suffixes.size();
309 this->BestMajor = 0;
310 this->BestMinor = 0;
313 //----------------------------------------------------------------------------
314 void cmFindLibraryHelper::RegexFromLiteral(std::string& out,
315 std::string const& in)
317 for(std::string::const_iterator ci = in.begin(); ci != in.end(); ++ci)
319 char ch = *ci;
320 if(ch == '[' || ch == ']' || ch == '(' || ch == ')' || ch == '\\' ||
321 ch == '.' || ch == '*' || ch == '+' || ch == '?' || ch == '-' ||
322 ch == '^' || ch == '$')
324 out += "\\";
326 #if defined(_WIN32) || defined(__APPLE__)
327 out += tolower(ch);
328 #else
329 out += ch;
330 #endif
334 //----------------------------------------------------------------------------
335 void cmFindLibraryHelper::RegexFromList(std::string& out,
336 std::vector<std::string> const& in)
338 // Surround the list in parens so the '|' does not apply to anything
339 // else and the result can be checked after matching.
340 out += "(";
341 const char* sep = "";
342 for(std::vector<std::string>::const_iterator si = in.begin();
343 si != in.end(); ++si)
345 // Separate from previous item.
346 out += sep;
347 sep = "|";
349 // Append this item.
350 this->RegexFromLiteral(out, *si);
352 out += ")";
355 //----------------------------------------------------------------------------
356 bool cmFindLibraryHelper::HasValidSuffix(std::string const& name)
358 // Check if the given name ends in a valid library suffix.
359 for(std::vector<std::string>::const_iterator si = this->Suffixes.begin();
360 si != this->Suffixes.end(); ++si)
362 std::string const& suffix = *si;
363 if(name.length() > suffix.length() &&
364 name.substr(name.size()-suffix.length()) == suffix)
366 return true;
369 return false;
372 //----------------------------------------------------------------------------
373 void cmFindLibraryHelper::SetName(std::string const& name)
375 // Consider checking the raw name too.
376 this->TryRawName = this->HasValidSuffix(name);
377 this->RawName = name;
379 // Build a regular expression to match library names.
380 std::string regex = "^";
381 regex += this->PrefixRegexStr;
382 this->RegexFromLiteral(regex, name);
383 regex += this->SuffixRegexStr;
384 if(this->OpenBSD)
386 regex += "(\\.[0-9]+\\.[0-9]+)?";
388 regex += "$";
389 this->NameRegex.compile(regex.c_str());
392 //----------------------------------------------------------------------------
393 bool cmFindLibraryHelper::CheckDirectory(std::string const& path)
395 // If the original library name provided by the user matches one of
396 // the suffixes, try it first. This allows users to search
397 // specifically for a static library on some platforms (on MS tools
398 // one cannot tell just from the library name whether it is a static
399 // library or an import library).
400 if(this->TryRawName)
402 this->TestPath = path;
403 this->TestPath += this->RawName;
404 if(cmSystemTools::FileExists(this->TestPath.c_str(), true))
406 this->BestPath =
407 cmSystemTools::CollapseFullPath(this->TestPath.c_str());
408 cmSystemTools::ConvertToUnixSlashes(this->BestPath);
409 return true;
413 // Search for a file matching the library name regex.
414 std::string dir = path;
415 cmSystemTools::ConvertToUnixSlashes(dir);
416 std::set<cmStdString> const& files = this->GG->GetDirectoryContent(dir);
417 for(std::set<cmStdString>::const_iterator fi = files.begin();
418 fi != files.end(); ++fi)
420 std::string const& origName = *fi;
421 #if defined(_WIN32) || defined(__APPLE__)
422 std::string testName = cmSystemTools::LowerCase(origName);
423 #else
424 std::string const& testName = origName;
425 #endif
426 if(this->NameRegex.find(testName))
428 this->TestPath = path;
429 this->TestPath += origName;
430 if(!cmSystemTools::FileIsDirectory(this->TestPath.c_str()))
432 // This is a matching file. Check if it is better than the
433 // best name found so far. Earlier prefixes are preferred,
434 // followed by earlier suffixes. For OpenBSD, shared library
435 // version extensions are compared.
436 size_type prefix = this->GetPrefixIndex(this->NameRegex.match(1));
437 size_type suffix = this->GetSuffixIndex(this->NameRegex.match(2));
438 unsigned int major = 0;
439 unsigned int minor = 0;
440 if(this->OpenBSD)
442 sscanf(this->NameRegex.match(3).c_str(), ".%u.%u", &major, &minor);
444 if(this->BestPath.empty() || prefix < this->BestPrefix ||
445 (prefix == this->BestPrefix && suffix < this->BestSuffix) ||
446 (prefix == this->BestPrefix && suffix == this->BestSuffix &&
447 (major > this->BestMajor ||
448 (major == this->BestMajor && minor > this->BestMinor))))
450 this->BestPath = this->TestPath;
451 this->BestPrefix = prefix;
452 this->BestSuffix = suffix;
453 this->BestMajor = major;
454 this->BestMinor = minor;
460 // Use the best candidate found in this directory, if any.
461 return !this->BestPath.empty();
464 //----------------------------------------------------------------------------
465 std::string cmFindLibraryCommand::FindNormalLibrary()
467 // Search the entire path for each name.
468 cmFindLibraryHelper helper(this->Makefile);
469 for(std::vector<std::string>::const_iterator ni = this->Names.begin();
470 ni != this->Names.end() ; ++ni)
472 // Switch to searching for this name.
473 std::string const& name = *ni;
474 helper.SetName(name);
476 // Search every directory.
477 for(std::vector<std::string>::const_iterator
478 p = this->SearchPaths.begin();
479 p != this->SearchPaths.end(); ++p)
481 if(helper.CheckDirectory(*p))
483 return helper.BestPath;
487 // Couldn't find the library.
488 return "";
491 //----------------------------------------------------------------------------
492 std::string cmFindLibraryCommand::FindFrameworkLibrary()
494 // Search for a framework of each name in the entire search path.
495 for(std::vector<std::string>::const_iterator ni = this->Names.begin();
496 ni != this->Names.end() ; ++ni)
498 // Search the paths for a framework with this name.
499 std::string fwName = *ni;
500 fwName += ".framework";
501 std::string fwPath = cmSystemTools::FindDirectory(fwName.c_str(),
502 this->SearchPaths,
503 true);
504 if(!fwPath.empty())
506 return fwPath;
510 // No framework found.
511 return "";