[mach-o] Support -F and -framework options in darwin driver

Darwin has a packaging mechanism for shared libraries and headers called
frameworks.  A directory Foo.framework contains a shared library binary file
"Foo" and a subdirectory "Headers". Most OS frameworks are all in one
directory /System/Library/Frameworks/.  As a linking convenience, the linker
option "-framework Foo" means search the framework directories specified
with -F (analogous to -L) looking for a shared library Foo.framework/Foo.

llvm-svn: 215680
This commit is contained in:
Nick Kledzik 2014-08-14 22:20:41 +00:00
parent e8d2a9d755
commit 2d835dad0a
5 changed files with 138 additions and 8 deletions

View File

@ -85,7 +85,8 @@ public:
bool printAtoms() const { return _printAtoms; }
bool testingLibResolution() const { return _testingLibResolution; }
const StringRefVector &searchDirs() const { return _searchDirs; }
void addSysLibRoot(StringRef sysPath) { _syslibRoots.push_back(sysPath); }
const StringRefVector &frameworkDirs() const { return _frameworkDirs; }
void setSysLibRoots(const StringRefVector &paths);
const StringRefVector &sysLibRoots() const { return _syslibRoots; }
/// \brief Checks whether a given path on the filesystem exists.
@ -112,10 +113,18 @@ public:
ErrorOr<StringRef> searchDirForLibrary(StringRef path,
StringRef libName) const;
/// \brief Iterates through all search path entries lookinf for libName (as
/// \brief Iterates through all search path entries looking for libName (as
/// specified by -lFoo).
ErrorOr<StringRef> searchLibrary(StringRef libName) const;
/// Add a framework search path. Internally, this method may be prepended
/// the path with syslibroot.
void addFrameworkSearchDir(StringRef fwPath, bool isSystemPath = false);
/// \brief Iterates through all framework directories looking for
/// Foo.framework/Foo (when fwName = "Foo").
ErrorOr<StringRef> findPathForFramework(StringRef fwName) const;
/// \brief The dylib's binary compatibility version, in the raw uint32 format.
///
/// When building a dynamic library, this is the compatibility version that
@ -231,6 +240,7 @@ private:
std::set<StringRef> _existingPaths; // For testing only.
StringRefVector _searchDirs;
StringRefVector _syslibRoots;
StringRefVector _frameworkDirs;
HeaderFileType _outputMachOType; // e.g MH_EXECUTE
bool _outputMachOTypeStatic; // Disambiguate static vs dynamic prog
bool _doNothing; // for -help and -v which just print info

View File

@ -321,8 +321,14 @@ bool DarwinLdDriver::parse(int argc, const char *argv[],
// skipped.
// 3. If the last -syslibroot is "/", all of them are ignored entirely.
// 4. If { syslibroots } x path == {}, the original path is kept.
std::vector<StringRef> sysLibRoots;
for (auto syslibRoot : parsedArgs->filtered(OPT_syslibroot)) {
ctx.addSysLibRoot(syslibRoot->getValue());
sysLibRoots.push_back(syslibRoot->getValue());
}
if (!sysLibRoots.empty()) {
// Ignore all if last -syslibroot is "/".
if (sysLibRoots.back() != "/")
ctx.setSysLibRoots(sysLibRoots);
}
// Paths specified with -L come first, and are not considered system paths for
@ -331,10 +337,17 @@ bool DarwinLdDriver::parse(int argc, const char *argv[],
ctx.addModifiedSearchDir(libPath->getValue());
}
// Process -F directories (where to look for frameworks).
for (auto fwPath : parsedArgs->filtered(OPT_F)) {
ctx.addFrameworkSearchDir(fwPath->getValue());
}
// -Z suppresses the standard search paths.
if (!parsedArgs->hasArg(OPT_Z)) {
ctx.addModifiedSearchDir("/usr/lib", true);
ctx.addModifiedSearchDir("/usr/local/lib", true);
ctx.addFrameworkSearchDir("/Library/Frameworks", true);
ctx.addFrameworkSearchDir("/System/Library/Frameworks", true);
}
// Now that we've constructed the final set of search paths, print out what
@ -344,6 +357,10 @@ bool DarwinLdDriver::parse(int argc, const char *argv[],
for (auto path : ctx.searchDirs()) {
diagnostics << " " << path << '\n';
}
diagnostics << "Framework search paths:\n";
for (auto path : ctx.frameworkDirs()) {
diagnostics << " " << path << '\n';
}
}
// Handle input files
@ -370,6 +387,21 @@ bool DarwinLdDriver::parse(int argc, const char *argv[],
inputPath = resolvedPath.get();
break;
}
case OPT_framework: {
ErrorOr<StringRef> fullPath = ctx.findPathForFramework(arg->getValue());
if (!fullPath) {
diagnostics << "Unable to find -framework " << arg->getValue() << "\n";
return false;
} else if (ctx.testingLibResolution()) {
// Test may be running on Windows. Canonicalize the path
// separator to '/' to get consistent outputs for tests.
std::string path = fullPath.get();
std::replace(path.begin(), path.end(), '\\', '/');
diagnostics << "Found framework " << path << '\n';
}
inputPath = fullPath.get();
break;
}
}
inputGraph->addInputElement(std::unique_ptr<InputElement>(
new MachOFileNode(inputPath, globalWholeArchive)));

View File

@ -66,6 +66,8 @@ def bundle_loader : Separate<["-"], "bundle_loader">,
def grp_libs : OptionGroup<"libs">, HelpText<"LIBRARY OPTIONS">;
def L : JoinedOrSeparate<["-"], "L">,
HelpText<"Add directory to library search path">, Group<grp_libs>;
def F : JoinedOrSeparate<["-"], "F">,
HelpText<"Add directory to framework search path">, Group<grp_libs>;
def Z : Flag<["-"], "Z">,
HelpText<"Do not search standard directories for libraries or frameworks">;
def all_load : Flag<["-"], "all_load">,
@ -78,6 +80,8 @@ def syslibroot : Separate<["-"], "syslibroot">,
// Input options
def l : Joined<["-"], "l">,
HelpText<"Base name of library searched for in -L directories">;
def framework : Separate<["-"], "framework">,
HelpText<"Base name of framework searched for in -F directories">;
// test case options
def print_atoms : Flag<["-"], "print_atoms">,

View File

@ -307,15 +307,16 @@ bool MachOLinkingContext::pathExists(StringRef path) const {
return _existingPaths.find(key) != _existingPaths.end();
}
void MachOLinkingContext::setSysLibRoots(const StringRefVector &paths) {
_syslibRoots = paths;
}
void MachOLinkingContext::addModifiedSearchDir(StringRef libPath,
bool isSystemPath) {
bool addedModifiedPath = false;
// Two cases to consider here:
// + If the last -syslibroot is "/", all of them are ignored (don't ask).
// + -syslibroot only applies to absolute paths.
if (!_syslibRoots.empty() && _syslibRoots.back() != "/" &&
libPath.startswith("/")) {
// -syslibroot only applies to absolute paths.
if (libPath.startswith("/")) {
for (auto syslibRoot : _syslibRoots) {
SmallString<256> path(syslibRoot);
llvm::sys::path::append(path, libPath);
@ -338,6 +339,35 @@ void MachOLinkingContext::addModifiedSearchDir(StringRef libPath,
}
}
void MachOLinkingContext::addFrameworkSearchDir(StringRef fwPath,
bool isSystemPath) {
bool pathAdded = false;
// -syslibroot only used with to absolute framework search paths.
if (fwPath.startswith("/")) {
for (auto syslibRoot : _syslibRoots) {
SmallString<256> path(syslibRoot);
llvm::sys::path::append(path, fwPath);
if (pathExists(path)) {
_frameworkDirs.push_back(path.str().copy(_allocator));
pathAdded = true;
}
}
}
// If fwPath found in any -syslibroot, then done.
if (pathAdded)
return;
// If only one -syslibroot, system paths not in that SDK are suppressed.
if (isSystemPath && (_syslibRoots.size() == 1))
return;
// Only use raw fwPath if that directory exists.
if (pathExists(fwPath))
_frameworkDirs.push_back(fwPath);
}
ErrorOr<StringRef>
MachOLinkingContext::searchDirForLibrary(StringRef path,
StringRef libName) const {
@ -379,6 +409,19 @@ ErrorOr<StringRef> MachOLinkingContext::searchLibrary(StringRef libName) const {
return make_error_code(llvm::errc::no_such_file_or_directory);
}
ErrorOr<StringRef> MachOLinkingContext::findPathForFramework(StringRef fwName) const{
SmallString<256> fullPath;
for (StringRef dir : frameworkDirs()) {
fullPath.assign(dir);
llvm::sys::path::append(fullPath, Twine(fwName) + ".framework", fwName);
if (pathExists(fullPath))
return fullPath.str().copy(_allocator);
}
return make_error_code(llvm::errc::no_such_file_or_directory);
}
bool MachOLinkingContext::validateImpl(raw_ostream &diagnostics) {
// TODO: if -arch not specified, look at arch of first .o file.

View File

@ -0,0 +1,41 @@
#
# Test framework and SDK search paths.
# myFrameworks is not an absolute path, so it should not by found in SDK
# /Custom/Frameworks should be found in SDK
# /opt/Frameworks should not be found in SDK
# /System/Library/Frameworks is implicit and should be in SDK
#
# RUN: lld -flavor darwin -arch x86_64 -r -test_libresolution \
# RUN: -path_exists myFrameworks \
# RUN: -path_exists myFrameworks/my.framework/my \
# RUN: -path_exists /opt/Frameworks \
# RUN: -path_exists /opt/Frameworks/other.framework/other \
# RUN: -path_exists /Custom/Frameworks \
# RUN: -path_exists /Custom/Frameworks/Bar.framework/Bar \
# RUN: -path_exists /System/Library/Frameworks \
# RUN: -path_exists /System/Library/Frameworks/Foo.framework/Foo \
# RUN: -path_exists /SDK/myFrameworks \
# RUN: -path_exists /SDK/myFrameworks/my.framework/my \
# RUN: -path_exists /SDK/Custom/Frameworks \
# RUN: -path_exists /SDK/Custom/Frameworks/Bar.framework/Bar \
# RUN: -path_exists /SDK/System/Library/Frameworks \
# RUN: -path_exists /SDK/System/Library/Frameworks/Foo.framework/Foo \
# RUN: -syslibroot /SDK \
# RUN: -FmyFrameworks \
# RUN: -F/Custom/Frameworks \
# RUN: -F/opt/Frameworks \
# RUN: -framework my \
# RUN: -framework Bar \
# RUN: -framework Foo \
# RUN: -framework other \
# RUN: 2>&1 | FileCheck %s
# CHECK: Framework search paths:
# CHECK-NEXT: myFrameworks
# CHECK-NEXT: /SDK/Custom/Frameworks
# CHECK-NEXT: /opt/Frameworks
# CHECK-NEXT: /SDK/System/Library/Frameworks
# CHECK: Found framework myFrameworks/my.framework/my
# CHECK: Found framework /SDK/Custom/Frameworks/Bar.framework/Bar
# CHECK: Found framework /SDK/System/Library/Frameworks/Foo.framework/Foo
# CHECK: Found framework /opt/Frameworks/other.framework/other