diff --git a/lld/include/lld/ReaderWriter/MachOLinkingContext.h b/lld/include/lld/ReaderWriter/MachOLinkingContext.h index 65cfc9eff062..5f24b2eee000 100644 --- a/lld/include/lld/ReaderWriter/MachOLinkingContext.h +++ b/lld/include/lld/ReaderWriter/MachOLinkingContext.h @@ -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 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 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 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 _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 diff --git a/lld/lib/Driver/DarwinLdDriver.cpp b/lld/lib/Driver/DarwinLdDriver.cpp index fe255fbc0df7..a3452210169f 100644 --- a/lld/lib/Driver/DarwinLdDriver.cpp +++ b/lld/lib/Driver/DarwinLdDriver.cpp @@ -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 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 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( new MachOFileNode(inputPath, globalWholeArchive))); diff --git a/lld/lib/Driver/DarwinLdOptions.td b/lld/lib/Driver/DarwinLdOptions.td index b9f7b1baed2d..0bdd5b6423a5 100644 --- a/lld/lib/Driver/DarwinLdOptions.td +++ b/lld/lib/Driver/DarwinLdOptions.td @@ -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; +def F : JoinedOrSeparate<["-"], "F">, + HelpText<"Add directory to framework search path">, Group; 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">, diff --git a/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp b/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp index 427b59a8b70a..a354e51a3df6 100644 --- a/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp +++ b/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp @@ -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 MachOLinkingContext::searchDirForLibrary(StringRef path, StringRef libName) const { @@ -379,6 +409,19 @@ ErrorOr MachOLinkingContext::searchLibrary(StringRef libName) const { return make_error_code(llvm::errc::no_such_file_or_directory); } + +ErrorOr 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. diff --git a/lld/test/mach-o/framework-user-paths.yaml b/lld/test/mach-o/framework-user-paths.yaml new file mode 100644 index 000000000000..a93f718e6f93 --- /dev/null +++ b/lld/test/mach-o/framework-user-paths.yaml @@ -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