diff options
author | Brad King <brad.king@kitware.com> | 2022-02-16 14:36:25 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2022-02-16 14:36:38 (GMT) |
commit | 82acb050187af820e482c01ac782ec95223f41de (patch) | |
tree | 734b44d4dfff5251e3a14cd3c9ccf97a67d895a8 | |
parent | fa8231ffac409f957e25e59fb0bde6999ea37f47 (diff) | |
parent | a2cfa2da4f53a5e9c78e6cf7332c6a308ef38af2 (diff) | |
download | CMake-82acb050187af820e482c01ac782ec95223f41de.zip CMake-82acb050187af820e482c01ac782ec95223f41de.tar.gz CMake-82acb050187af820e482c01ac782ec95223f41de.tar.bz2 |
Merge topic 'LINK_LIBRARY-framework'
a2cfa2da4f GenEx/LINK_LIBRARY: Add features for framework support on Apple
40178f3c90 cmGlobalGenerator: Add helper to split framework path
Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !6967
-rw-r--r-- | Help/release/dev/Apple-link-framework.rst | 11 | ||||
-rw-r--r-- | Help/variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt | 40 | ||||
-rw-r--r-- | Modules/Compiler/Clang-CUDA.cmake | 15 | ||||
-rw-r--r-- | Modules/Platform/Apple-Apple-Swift.cmake | 14 | ||||
-rw-r--r-- | Modules/Platform/Apple-NVIDIA-CUDA.cmake | 14 | ||||
-rw-r--r-- | Modules/Platform/Darwin.cmake | 13 | ||||
-rw-r--r-- | Source/cmComputeLinkInformation.cxx | 96 | ||||
-rw-r--r-- | Source/cmComputeLinkInformation.h | 6 | ||||
-rw-r--r-- | Source/cmGlobalGenerator.cxx | 42 | ||||
-rw-r--r-- | Source/cmGlobalGenerator.h | 7 | ||||
-rw-r--r-- | Source/cmGlobalXCodeGenerator.cxx | 96 | ||||
-rw-r--r-- | Source/cmGlobalXCodeGenerator.h | 2 | ||||
-rw-r--r-- | Tests/RunCMake/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake | 18 | ||||
-rw-r--r-- | Tests/RunCMake/target_link_libraries-LINK_LIBRARY/apple_framework.cmake | 61 | ||||
-rw-r--r-- | Tests/RunCMake/target_link_libraries-LINK_LIBRARY/foo.h | 9 | ||||
-rw-r--r-- | Tests/RunCMake/target_link_libraries-LINK_LIBRARY/foo.mm | 7 | ||||
-rw-r--r-- | Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main.mm | 14 |
18 files changed, 381 insertions, 85 deletions
diff --git a/Help/release/dev/Apple-link-framework.rst b/Help/release/dev/Apple-link-framework.rst new file mode 100644 index 0000000..e194c15 --- /dev/null +++ b/Help/release/dev/Apple-link-framework.rst @@ -0,0 +1,11 @@ +Apple-link-framework +-------------------- + +* The :genex:`LINK_LIBRARY` generator expression gained the ability to link + frameworks in various ways when targeting ``Apple`` platforms. The following + new features were added: + + * ``FRAMEWORK`` + * ``NEEDED_FRAMEWORK`` + * ``REEXPORT_FRAMEWORK`` + * ``WEAK_FRAMEWORK`` diff --git a/Help/variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt b/Help/variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt index dd22e14..d4fdf76 100644 --- a/Help/variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt +++ b/Help/variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt @@ -3,3 +3,43 @@ * ``DEFAULT``: This feature enables default link expression. This is mainly useful with :prop_tgt:`LINK_LIBRARY_OVERRIDE` and :prop_tgt:`LINK_LIBRARY_OVERRIDE_<LIBRARY>` target properties. + +**Features available in Apple environments** + +It is assumed that the linker used is the one provided by `XCode` or is +compatible with it. + +* ``FRAMEWORK``: This option tells the linker to search for the specified + framework (use linker option ``-framework``). +* ``NEEDED_FRAMEWORK``: This is the same as the ``FRAMEWORK`` feature but means + to really link with the framework even if no symbols are used from it (use + linker option ``-needed_framework``). +* ``REEXPORT_FRAMEWORK``: This is the same as the ``FRAMEWORK`` feature but + also specifies that all symbols in that framework should be available to + clients linking to the library being created (use linker option + ``-reexport_framework``). +* ``WEAK_FRAMEWORK``: This is the same as the ``FRAMEWORK`` feature but forces + the framework and all references to it to be marked as weak imports (use + linker option ``-weak_framework``). + +Features for framework linking have a special handling in ``CMake``: the +framework can be specified as a ``CMake`` framework target or file path. In +the later case, if the path includes a directory part, this one will be +specified as framework search path at link step. + +.. code-block:: cmake + + add_library(lib SHARED ...) + target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:NEEDED_FRAMEWORK,/path/to/my_framework>") + + # at link step we will have: + # -F/path/to -needed_framework my_framework + +.. note:: + + The expected formats for the file path, with optional parts specified as + ``()?``, are: + + * (/path/to/)?FwName(.framework)? + * (/path/to/)?FwName.framework/FwName + * (/path/to/)?FwName.framework/Versions/\*/FwName diff --git a/Modules/Compiler/Clang-CUDA.cmake b/Modules/Compiler/Clang-CUDA.cmake index 219897e..44eb35f 100644 --- a/Modules/Compiler/Clang-CUDA.cmake +++ b/Modules/Compiler/Clang-CUDA.cmake @@ -38,3 +38,18 @@ set(CMAKE_CUDA_RUNTIME_LIBRARY_LINK_OPTIONS_NONE "") if(UNIX) list(APPEND CMAKE_CUDA_RUNTIME_LIBRARY_LINK_OPTIONS_STATIC "rt" "pthread" "dl") endif() + +if(APPLE) + # Defines host link features for frameworks + set(CMAKE_CUDA_LINK_USING_FRAMEWORK "LINKER:-framework,<LIBRARY>") + set(CMAKE_CUDA_LINK_USING_FRAMEWORK_SUPPORTED TRUE) + + set(CMAKE_CUDA_LINK_USING_NEEDED_FRAMEWORK "LINKER:-needed_framework,<LIBRARY>") + set(CMAKE_CUDA_LINK_USING_NEEDED_FRAMEWORK_SUPPORTED TRUE) + + set(CMAKE_CUDA_LINK_USING_REEXPORT_FRAMEWORK "LINKER:-reexport_framework,<LIBRARY>") + set(CMAKE_CUDA_LINK_USING_REEXPORT_FRAMEWORK_SUPPORTED TRUE) + + set(CMAKE_CUDA_LINK_USING_WEAK_FRAMEWORK "LINKER:-weak_framework,<LIBRARY>") + set(CMAKE_CUDA_LINK_USING_WEAK_FRAMEWORK_SUPPORTED TRUE) +endif() diff --git a/Modules/Platform/Apple-Apple-Swift.cmake b/Modules/Platform/Apple-Apple-Swift.cmake index 7ca3e36..255411e 100644 --- a/Modules/Platform/Apple-Apple-Swift.cmake +++ b/Modules/Platform/Apple-Apple-Swift.cmake @@ -1 +1,15 @@ set(CMAKE_Swift_SYSROOT_FLAG "-sdk") + + +# Defines host link features for frameworks +set(CMAKE_Swift_LINK_USING_FRAMEWORK "LINKER:-framework,<LIBRARY>") +set(CMAKE_Swift_LINK_USING_FRAMEWORK_SUPPORTED TRUE) + +set(CMAKE_Swift_LINK_USING_NEEDED_FRAMEWORK "LINKER:-needed_framework,<LIBRARY>") +set(CMAKE_Swift_LINK_USING_NEEDED_FRAMEWORK_SUPPORTED TRUE) + +set(CMAKE_Swift_LINK_USING_REEXPORT_FRAMEWORK "LINKER:-reexport_framework,<LIBRARY>") +set(CMAKE_Swift_LINK_USING_REEXPORT_FRAMEWORK_SUPPORTED TRUE) + +set(CMAKE_Swift_LINK_USING_WEAK_FRAMEWORK "LINKER:-weak_framework,<LIBRARY>") +set(CMAKE_Swift_LINK_USING_WEAK_FRAMEWORK_SUPPORTED TRUE) diff --git a/Modules/Platform/Apple-NVIDIA-CUDA.cmake b/Modules/Platform/Apple-NVIDIA-CUDA.cmake index 35e759a..ba669e1 100644 --- a/Modules/Platform/Apple-NVIDIA-CUDA.cmake +++ b/Modules/Platform/Apple-NVIDIA-CUDA.cmake @@ -17,3 +17,17 @@ set(CMAKE_SHARED_MODULE_CREATE_CUDA_FLAGS "-shared -Wl,-headerpad_max_install_na set(CMAKE_CUDA_CREATE_SHARED_LIBRARY "<CMAKE_CUDA_HOST_LINK_LAUNCHER> <CMAKE_SHARED_LIBRARY_CUDA_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CUDA_FLAGS> -o <TARGET> <SONAME_FLAG> <TARGET_INSTALLNAME_DIR><TARGET_SONAME> <OBJECTS> <LINK_LIBRARIES>${__IMPLICIT_LINKS}") set(CMAKE_CUDA_CREATE_SHARED_MODULE "<CMAKE_CUDA_HOST_LINK_LAUNCHER> <CMAKE_SHARED_LIBRARY_CUDA_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CUDA_FLAGS> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>${__IMPLICIT_LINKS}") + + +# Defines host link features for frameworks +set(CMAKE_CUDA_LINK_USING_FRAMEWORK "LINKER:-framework,<LIBRARY>") +set(CMAKE_CUDA_LINK_USING_FRAMEWORK_SUPPORTED TRUE) + +set(CMAKE_CUDA_LINK_USING_NEEDED_FRAMEWORK "LINKER:-needed_framework,<LIBRARY>") +set(CMAKE_CUDA_LINK_USING_NEEDED_FRAMEWORK_SUPPORTED TRUE) + +set(CMAKE_CUDA_LINK_USING_REEXPORT_FRAMEWORK "LINKER:-reexport_framework,<LIBRARY>") +set(CMAKE_CUDA_LINK_USING_REEXPORT_FRAMEWORK_SUPPORTED TRUE) + +set(CMAKE_CUDA_LINK_USING_WEAK_FRAMEWORK "LINKER:-weak_framework,<LIBRARY>") +set(CMAKE_CUDA_LINK_USING_WEAK_FRAMEWORK_SUPPORTED TRUE) diff --git a/Modules/Platform/Darwin.cmake b/Modules/Platform/Darwin.cmake index 839dc81..d7b615c 100644 --- a/Modules/Platform/Darwin.cmake +++ b/Modules/Platform/Darwin.cmake @@ -106,6 +106,19 @@ foreach(lang C CXX Fortran OBJC OBJCXX) # Set default framework search path flag for languages known to use a # preprocessor that may find headers in frameworks. set(CMAKE_${lang}_FRAMEWORK_SEARCH_FLAG -F) + + # Defines link features for frameworks + set(CMAKE_${lang}_LINK_USING_FRAMEWORK "LINKER:-framework,<LIBRARY>") + set(CMAKE_${lang}_LINK_USING_FRAMEWORK_SUPPORTED TRUE) + + set(CMAKE_${lang}_LINK_USING_NEEDED_FRAMEWORK "LINKER:-needed_framework,<LIBRARY>") + set(CMAKE_${lang}_LINK_USING_NEEDED_FRAMEWORK_SUPPORTED TRUE) + + set(CMAKE_${lang}_LINK_USING_REEXPORT_FRAMEWORK "LINKER:-reexport_framework,<LIBRARY>") + set(CMAKE_${lang}_LINK_USING_REEXPORT_FRAMEWORK_SUPPORTED TRUE) + + set(CMAKE_${lang}_LINK_USING_WEAK_FRAMEWORK "LINKER:-weak_framework,<LIBRARY>") + set(CMAKE_${lang}_LINK_USING_WEAK_FRAMEWORK_SUPPORTED TRUE) endforeach() # default to searching for frameworks first diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index 15e9d60..cf8c9a1 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -8,7 +8,9 @@ #include <utility> #include <cm/memory> +#include <cm/optional> #include <cmext/algorithm> +#include <cmext/string_view> #include "cmComputeLinkDepends.h" #include "cmGeneratorTarget.h" @@ -18,7 +20,6 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmOrderDirectories.h" -#include "cmOutputConverter.h" #include "cmPlaceholderExpander.h" #include "cmPolicies.h" #include "cmState.h" @@ -1044,12 +1045,14 @@ void cmComputeLinkInformation::AddItem(LinkEntry const& entry) } } else { // This is not a CMake target. Use the name given. - if (cmSystemTools::FileIsFullPath(item.Value)) { - if (cmSystemTools::IsPathToFramework(item.Value) && - this->Makefile->IsOn("APPLE")) { - // This is a framework. - this->AddFrameworkItem(entry); - } else if (cmSystemTools::FileIsDirectory(item.Value)) { + if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s) || + (entry.Feature == DEFAULT && + cmSystemTools::IsPathToFramework(item.Value) && + this->Makefile->IsOn("APPLE"))) { + // This is a framework. + this->AddFrameworkItem(entry); + } else if (cmSystemTools::FileIsFullPath(item.Value)) { + if (cmSystemTools::FileIsDirectory(item.Value)) { // This is a directory. this->DropDirectoryItem(item); } else { @@ -1424,11 +1427,41 @@ void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry) this->OldLinkDirItems.push_back(item.Value); } - // Now add the full path to the library. - this->Items.emplace_back(item, ItemIsPath::Yes, target, - this->FindLibraryFeature(entry.Feature == DEFAULT - ? "__CMAKE_LINK_LIBRARY" - : entry.Feature)); + if (target->IsFrameworkOnApple() && this->GlobalGenerator->IsXcode() && + entry.Feature == DEFAULT) { + // ensure FRAMEWORK feature is loaded + this->AddLibraryFeature("FRAMEWORK"); + } + + if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s) && + target->IsFrameworkOnApple() && !this->GlobalGenerator->IsXcode()) { + // Add the framework directory and the framework item itself + auto fwItems = this->GlobalGenerator->SplitFrameworkPath(item.Value, true); + if (!fwItems) { + this->CMakeInstance->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Could not parse framework path \"", item.Value, + "\" linked by target ", this->Target->GetName(), '.'), + item.Backtrace); + return; + } + if (!fwItems->first.empty()) { + // Add the directory portion to the framework search path. + this->AddFrameworkPath(fwItems->first); + } + this->Items.emplace_back(fwItems->second, ItemIsPath::Yes, target, + this->FindLibraryFeature(entry.Feature)); + } else { + // Now add the full path to the library. + this->Items.emplace_back( + item, ItemIsPath::Yes, target, + this->FindLibraryFeature( + entry.Feature == DEFAULT + ? (target->IsFrameworkOnApple() && this->GlobalGenerator->IsXcode() + ? "FRAMEWORK" + : "__CMAKE_LINK_LIBRARY") + : entry.Feature)); + } } void cmComputeLinkInformation::AddFullItem(LinkEntry const& entry) @@ -1679,7 +1712,9 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry) std::string const& item = entry.Item.Value; // Try to separate the framework name and path. - if (!this->SplitFramework.find(item)) { + auto fwItems = + this->GlobalGenerator->SplitFrameworkPath(item, entry.Feature != DEFAULT); + if (!fwItems) { std::ostringstream e; e << "Could not parse framework path \"" << item << "\" " << "linked by target " << this->Target->GetName() << "."; @@ -1687,26 +1722,36 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry) return; } - std::string fw_path = this->SplitFramework.match(1); - std::string fw = this->SplitFramework.match(2); - std::string full_fw = cmStrCat(fw_path, '/', fw, ".framework/", fw); + std::string fw_path = std::move(fwItems->first); + std::string fw = std::move(fwItems->second); + std::string full_fw = cmStrCat(fw, ".framework/", fw); - // Add the directory portion to the framework search path. - this->AddFrameworkPath(fw_path); + if (!fw_path.empty()) { + full_fw = cmStrCat(fw_path, '/', full_fw); + // Add the directory portion to the framework search path. + this->AddFrameworkPath(fw_path); + } // add runtime information this->AddLibraryRuntimeInfo(full_fw); + if (entry.Feature == DEFAULT) { + // ensure FRAMEWORK feature is loaded + this->AddLibraryFeature("FRAMEWORK"); + } + if (this->GlobalGenerator->IsXcode()) { // Add framework path - it will be handled by Xcode after it's added to // "Link Binary With Libraries" build phase - this->Items.emplace_back(item, ItemIsPath::Yes); + this->Items.emplace_back(item, ItemIsPath::Yes, nullptr, + this->FindLibraryFeature(entry.Feature == DEFAULT + ? "FRAMEWORK" + : entry.Feature)); } else { - // Add the item using the -framework option. - this->Items.emplace_back(std::string("-framework"), ItemIsPath::No); - cmOutputConverter converter(this->Makefile->GetStateSnapshot()); - fw = converter.EscapeForShell(fw); - this->Items.emplace_back(fw, ItemIsPath::No); + this->Items.emplace_back(fw, ItemIsPath::Yes, nullptr, + this->FindLibraryFeature(entry.Feature == DEFAULT + ? "FRAMEWORK" + : entry.Feature)); } } @@ -1739,9 +1784,6 @@ void cmComputeLinkInformation::ComputeFrameworkInfo() this->FrameworkPathsEmitted.insert(implicitDirVec.begin(), implicitDirVec.end()); - - // Regular expression to extract a framework path and name. - this->SplitFramework.compile("(.*)/(.*)\\.framework$"); } void cmComputeLinkInformation::AddFrameworkPath(std::string const& p) diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h index ce9f393..4b7fb1a 100644 --- a/Source/cmComputeLinkInformation.h +++ b/Source/cmComputeLinkInformation.h @@ -63,6 +63,11 @@ public: cmGeneratorTarget const* Target = nullptr; bool HasFeature() const { return this->Feature != nullptr; } + const std::string& GetFeatureName() const + { + return HasFeature() ? this->Feature->Name + : cmComputeLinkDepends::LinkEntry::DEFAULT; + } BT<std::string> GetFormattedItem(std::string const& path) const { @@ -205,7 +210,6 @@ private: void ComputeFrameworkInfo(); void AddFrameworkPath(std::string const& p); std::set<std::string> FrameworkPathsEmitted; - cmsys::RegularExpression SplitFramework; // Linker search path computation. std::unique_ptr<cmOrderDirectories> OrderLinkerSearchPath; diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 156ecce..baa54e5 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -19,6 +19,7 @@ #include "cmsys/Directory.hxx" #include "cmsys/FStream.hxx" +#include "cmsys/RegularExpression.hxx" #if defined(_WIN32) && !defined(__CYGWIN__) # include <windows.h> @@ -2522,6 +2523,47 @@ bool cmGlobalGenerator::NameResolvesToFramework( return false; } +// If the file has no extension it's either a raw executable or might +// be a direct reference to a binary within a framework (bad practice!). +// This is where we change the path to point to the framework directory. +// .tbd files also can be located in SDK frameworks (they are +// placeholders for actual libraries shipped with the OS) +cm::optional<std::pair<std::string, std::string>> +cmGlobalGenerator::SplitFrameworkPath(const std::string& path, + bool extendedFormat) const +{ + // Check for framework structure: + // (/path/to/)?FwName.framework + // or (/path/to/)?FwName.framework/FwName(.tbd)? + // or (/path/to/)?FwName.framework/Versions/*/FwName(.tbd)? + static cmsys::RegularExpression frameworkPath( + "((.+)/)?(.+)\\.framework(/Versions/[^/]+)?(/(.+))?$"); + + auto ext = cmSystemTools::GetFilenameLastExtension(path); + if ((ext.empty() || ext == ".tbd" || ext == ".framework") && + frameworkPath.find(path)) { + auto name = frameworkPath.match(3); + auto libname = + cmSystemTools::GetFilenameWithoutExtension(frameworkPath.match(6)); + if (!libname.empty() && name != libname) { + return cm::nullopt; + } + return std::pair<std::string, std::string>{ frameworkPath.match(2), name }; + } + + if (extendedFormat) { + // path format can be more flexible: (/path/to/)?fwName(.framework)? + auto fwDir = cmSystemTools::GetParentDirectory(path); + auto name = cmSystemTools::GetFilenameLastExtension(path) == ".framework" + ? cmSystemTools::GetFilenameWithoutExtension(path) + : cmSystemTools::GetFilenameName(path); + + return std::pair<std::string, std::string>{ fwDir, name }; + } + + return cm::nullopt; +} + bool cmGlobalGenerator::CheckCMP0037(std::string const& targetName, std::string const& reason) const { diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index a43d4a6..a4b2ae3 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -367,6 +367,13 @@ public: /** Determine if a name resolves to a framework on disk or a built target that is a framework. */ bool NameResolvesToFramework(const std::string& libname) const; + /** Split a framework path to the directory and name of the framework + * returns std::nullopt if the path does not match with framework format + * when extendedFormat is true, required format is relaxed (i.e. extension + * `.framework' is optional). Used when FRAMEWORK link feature is + * specified */ + cm::optional<std::pair<std::string, std::string>> SplitFrameworkPath( + const std::string& path, bool extendedFormat = false) const; cmMakefile* FindMakefile(const std::string& start_dir) const; cmLocalGenerator* FindLocalGenerator(cmDirectoryId const& id) const; diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 489c7fb..b752c41 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -1154,47 +1154,25 @@ std::string GetSourcecodeValueFromFileExtension( return sourcecode; } -// If the file has no extension it's either a raw executable or might -// be a direct reference to a binary within a framework (bad practice!). -// This is where we change the path to point to the framework directory. -// .tbd files also can be located in SDK frameworks (they are -// placeholders for actual libraries shipped with the OS) -std::string GetLibraryOrFrameworkPath(const std::string& path) +} // anonymous + +// Extracts the framework directory, if path matches the framework syntax +// otherwise returns the path untouched +std::string cmGlobalXCodeGenerator::GetLibraryOrFrameworkPath( + const std::string& path) const { - auto ext = cmSystemTools::GetFilenameLastExtension(path); - if (ext.empty() || ext == ".tbd") { - auto name = cmSystemTools::GetFilenameWithoutExtension(path); - // Check for iOS framework structure: - // FwName.framework/FwName (and also on macOS where FwName lib is a - // symlink) - auto parentDir = cmSystemTools::GetParentDirectory(path); - auto parentName = cmSystemTools::GetFilenameWithoutExtension(parentDir); - ext = cmSystemTools::GetFilenameLastExtension(parentDir); - if (ext == ".framework" && name == parentName) { - return parentDir; - } - // Check for macOS framework structure: - // FwName.framework/Versions/*/FwName - std::vector<std::string> components; - cmSystemTools::SplitPath(path, components); - if (components.size() > 3 && - components[components.size() - 3] == "Versions") { - ext = cmSystemTools::GetFilenameLastExtension( - components[components.size() - 4]); - parentName = cmSystemTools::GetFilenameWithoutExtension( - components[components.size() - 4]); - if (ext == ".framework" && name == parentName) { - components.erase(components.begin() + components.size() - 3, - components.end()); - return cmSystemTools::JoinPath(components); - } + auto fwItems = this->SplitFrameworkPath(path); + if (fwItems) { + if (fwItems->first.empty()) { + return cmStrCat(fwItems->second, ".framework"); + } else { + return cmStrCat(fwItems->first, '/', fwItems->second, ".framework"); } } + return path; } -} // anonymous - cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath( const std::string& fullpath, cmGeneratorTarget* target, const std::string& lang, cmSourceFile* sf) @@ -1217,7 +1195,7 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath( ext = ext.substr(1); } if (fileType.empty()) { - path = GetLibraryOrFrameworkPath(path); + path = this->GetLibraryOrFrameworkPath(path); ext = cmSystemTools::GetFilenameLastExtension(path); if (!ext.empty()) { ext = ext.substr(1); @@ -3541,13 +3519,14 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) } else { linkDir = libItem->Value.Value; } - linkDir = GetLibraryOrFrameworkPath(linkDir); - bool isFramework = cmSystemTools::IsPathToFramework(linkDir); - linkDir = cmSystemTools::GetParentDirectory(linkDir); - if (isFramework) { - if (std::find(frameworkSearchPaths.begin(), frameworkSearchPaths.end(), - linkDir) == frameworkSearchPaths.end()) { - frameworkSearchPaths.push_back(linkDir); + if (cmHasSuffix(libItem->GetFeatureName(), "FRAMEWORK"_s)) { + auto fwItems = this->SplitFrameworkPath(linkDir, true); + if (fwItems && !fwItems->first.empty()) { + linkDir = std::move(fwItems->first); + if (std::find(frameworkSearchPaths.begin(), frameworkSearchPaths.end(), + linkDir) == frameworkSearchPaths.end()) { + frameworkSearchPaths.push_back(linkDir); + } } } else { if (std::find(linkSearchPaths.begin(), linkSearchPaths.end(), linkDir) == @@ -3555,7 +3534,7 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) linkSearchPaths.push_back(linkDir); } } - // Add target dependency + if (libItem->Target && !libItem->Target->IsImported()) { for (auto const& configName : this->CurrentConfigurationTypes) { target->AddDependTarget(configName, libItem->Target->GetName()); @@ -3729,24 +3708,27 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) if (cmSystemTools::FileIsFullPath(cleanPath)) { cleanPath = cmSystemTools::CollapseFullPath(cleanPath); } - const auto libPath = GetLibraryOrFrameworkPath(cleanPath); - if (cmSystemTools::StringEndsWith(libPath.c_str(), ".framework")) { - const auto fwName = - cmSystemTools::GetFilenameWithoutExtension(libPath); - const auto fwDir = cmSystemTools::GetParentDirectory(libPath); - if (emitted.insert(fwDir).second) { - // This is a search path we had not added before and it isn't an - // implicit search path, so we need it - libPaths.Add("-F " + this->XCodeEscapePath(fwDir)); + bool isFramework = + cmHasSuffix(libName.GetFeatureName(), "FRAMEWORK"_s); + if (isFramework) { + const auto fwItems = + this->SplitFrameworkPath(cleanPath, isFramework); + if (!fwItems->first.empty() && + emitted.insert(fwItems->first).second) { + // This is a search path we had not added before and it isn't + // an implicit search path, so we need it + libPaths.Add("-F " + this->XCodeEscapePath(fwItems->first)); } - libPaths.Add("-framework " + this->XCodeEscapePath(fwName)); + libPaths.Add( + libName.GetFormattedItem(this->XCodeEscapePath(fwItems->second)) + .Value); } else { libPaths.Add( libName.GetFormattedItem(this->XCodeEscapePath(cleanPath)) .Value); } if ((!libName.Target || libName.Target->IsImported()) && - IsLinkPhaseLibraryExtension(libPath)) { + (isFramework || IsLinkPhaseLibraryExtension(cleanPath))) { // Create file reference for embedding auto it = this->ExternalLibRefs.find(cleanPath); if (it == this->ExternalLibRefs.end()) { @@ -3914,8 +3896,8 @@ void cmGlobalXCodeGenerator::AddEmbeddedFrameworks(cmXCodeObject* target) { static const auto dstSubfolderSpec = "10"; - // Despite the name, by default Xcode uses "Embed Frameworks" build phase for - // both frameworks and dynamic libraries + // Despite the name, by default Xcode uses "Embed Frameworks" build phase + // for both frameworks and dynamic libraries this->AddEmbeddedObjects(target, "Embed Frameworks", "XCODE_EMBED_FRAMEWORKS", dstSubfolderSpec, NoActionOnCopyByDefault); diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index ff6ffe8..98cebef 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h @@ -330,6 +330,8 @@ private: { } + std::string GetLibraryOrFrameworkPath(const std::string& path) const; + std::string GetObjectsDirectory(const std::string& projName, const std::string& configName, const cmGeneratorTarget* t, diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 652ea5c..3673ac9 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -659,6 +659,7 @@ add_RunCMake_test(target_link_libraries-LINK_LIBRARY -DCMAKE_SYSTEM_NAME=${CMAKE -DMSYS=${MSYS} -DCYGWIN=${CYGWIN} -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} + -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION} -DMSVC_VERSION=${MSVC_VERSION} -DCMAKE_SHARED_LIBRARY_PREFIX=${CMAKE_SHARED_LIBRARY_PREFIX} -DCMAKE_SHARED_LIBRARY_SUFFIX=${CMAKE_SHARED_LIBRARY_SUFFIX} diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake index 9ebbdb7..9582e11 100644 --- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake @@ -79,3 +79,21 @@ if ((RunCMake_GENERATOR MATCHES "Makefiles|Ninja|Xcode" unset(RunCMake_TEST_OUTPUT_MERGE) endif() + +# Apple framework features +if(APPLE AND (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")) + run_cmake(apple_framework) + run_cmake_target(apple_framework framework main-framework) + run_cmake_target(apple_framework reexport_framework main-reexport_framework) + run_cmake_target(apple_framework weak_framework main-weak_framework) + + run_cmake_target(apple_framework target-framework main-target-framework) + run_cmake_target(apple_framework target-reexport_framework main-target-reexport_framework) + run_cmake_target(apple_framework target-weak_framework main-target-weak_framework) +endif() + +if (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION GREATER_EQUAL "12") + run_cmake_target(apple_framework needed_framework main-needed_framework) + + run_cmake_target(apple_framework target-needed_framework main-target-needed_framework) +endif() diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/apple_framework.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/apple_framework.cmake new file mode 100644 index 0000000..e9a93e9 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/apple_framework.cmake @@ -0,0 +1,61 @@ + +enable_language(OBJCXX) + + +# feature FRAMEWORK +add_library(foo-framework SHARED foo.mm) +target_link_libraries(foo-framework PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>") + +add_executable(main-framework main.mm) +target_link_libraries(main-framework PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" foo-framework) + + +# feature NEEDED_FRAMEWORK +add_library(foo-needed_framework SHARED foo.mm) +target_link_libraries(foo-needed_framework PRIVATE "$<LINK_LIBRARY:NEEDED_FRAMEWORK,Foundation>") + +add_executable(main-needed_framework main.mm) +target_link_libraries(main-needed_framework PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" foo-needed_framework) + + +# feature REEXPORT_FRAMEWORK +add_library(foo-reexport_framework SHARED foo.mm) +target_link_libraries(foo-reexport_framework PRIVATE "$<LINK_LIBRARY:REEXPORT_FRAMEWORK,Foundation>") + +add_executable(main-reexport_framework main.mm) +target_link_libraries(main-reexport_framework PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" foo-reexport_framework) + + +# feature WEAK_FRAMEWORK +add_library(foo-weak_framework SHARED foo.mm) +target_link_libraries(foo-weak_framework PRIVATE "$<LINK_LIBRARY:WEAK_FRAMEWORK,Foundation>") + +add_executable(main-weak_framework main.mm) +target_link_libraries(main-weak_framework PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" foo-weak_framework) + + +## +## Consumption of target specified as FRAMEWORK +add_library(target-framework SHARED foo.mm) +set_target_properties(target-framework PROPERTIES FRAMEWORK TRUE) +target_link_libraries(target-framework PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>") + + +# feature FRAMEWORK +add_executable(main-target-framework main.mm) +target_link_libraries(main-target-framework PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" "$<LINK_LIBRARY:FRAMEWORK,target-framework>") + + +# feature NEEDED_FRAMEWORK +add_executable(main-target-needed_framework main.mm) +target_link_libraries(main-target-needed_framework PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" "$<LINK_LIBRARY:NEEDED_FRAMEWORK,target-framework>") + + +# feature REEXPORT_FRAMEWORK +add_executable(main-target-reexport_framework main.mm) +target_link_libraries(main-target-reexport_framework PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" "$<LINK_LIBRARY:REEXPORT_FRAMEWORK,target-framework>") + + +# feature WEAK_FRAMEWORK +add_executable(main-target-weak_framework main.mm) +target_link_libraries(main-target-weak_framework PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" "$<LINK_LIBRARY:REEXPORT_FRAMEWORK,target-framework>") diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/foo.h b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/foo.h new file mode 100644 index 0000000..b3fb084 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/foo.h @@ -0,0 +1,9 @@ +#import <Foundation/Foundation.h> + +@interface Foo : NSObject { + NSNumber* age; +} + +@property (nonatomic, retain) NSNumber* age; + +@end diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/foo.mm b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/foo.mm new file mode 100644 index 0000000..2d452a8 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/foo.mm @@ -0,0 +1,7 @@ +#import "foo.h" + +@implementation Foo + +@synthesize age; + +@end diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main.mm b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main.mm new file mode 100644 index 0000000..7c85551 --- /dev/null +++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main.mm @@ -0,0 +1,14 @@ +#import <Foundation/Foundation.h> +#import "foo.h" +#include <iostream> + +int main(int argc, char **argv) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + Foo *theFoo = [[Foo alloc] init]; + theFoo.age = [NSNumber numberWithInt:argc]; + NSLog(@"%d\n",[theFoo.age intValue]); + std::cout << [theFoo.age intValue] << std::endl; + [pool release]; + return 0; +} |