GenEx/LINK_LIBRARY: Add features for framework support on Apple

This commit is contained in:
Marc Chevrier
2022-02-10 23:57:14 +01:00
parent 40178f3c90
commit a2cfa2da4f
17 changed files with 329 additions and 46 deletions

View File

@@ -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``

View File

@@ -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

View File

@@ -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()

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -10,6 +10,7 @@
#include <cm/memory>
#include <cm/optional>
#include <cmext/algorithm>
#include <cmext/string_view>
#include "cmComputeLinkDepends.h"
#include "cmGeneratorTarget.h"
@@ -19,7 +20,6 @@
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmOrderDirectories.h"
#include "cmOutputConverter.h"
#include "cmPlaceholderExpander.h"
#include "cmPolicies.h"
#include "cmState.h"
@@ -1045,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 {
@@ -1425,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)
@@ -1680,7 +1712,8 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry)
std::string const& item = entry.Item.Value;
// Try to separate the framework name and path.
auto fwItems = this->GlobalGenerator->SplitFrameworkPath(item);
auto fwItems =
this->GlobalGenerator->SplitFrameworkPath(item, entry.Feature != DEFAULT);
if (!fwItems) {
std::ostringstream e;
e << "Could not parse framework path \"" << item << "\" "
@@ -1691,24 +1724,34 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry)
std::string fw_path = std::move(fwItems->first);
std::string fw = std::move(fwItems->second);
std::string full_fw = cmStrCat(fw_path, '/', fw, ".framework/", fw);
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));
}
}

View File

@@ -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
{

View File

@@ -2529,7 +2529,8 @@ bool cmGlobalGenerator::NameResolvesToFramework(
// .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) const
cmGlobalGenerator::SplitFrameworkPath(const std::string& path,
bool extendedFormat) const
{
// Check for framework structure:
// (/path/to/)?FwName.framework
@@ -2550,6 +2551,16 @@ cmGlobalGenerator::SplitFrameworkPath(const std::string& path) const
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;
}

View File

@@ -368,9 +368,12 @@ public:
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 */
* 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) const;
const std::string& path, bool extendedFormat = false) const;
cmMakefile* FindMakefile(const std::string& start_dir) const;
cmLocalGenerator* FindLocalGenerator(cmDirectoryId const& id) const;

View File

@@ -3519,13 +3519,14 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
} else {
linkDir = libItem->Value.Value;
}
linkDir = this->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) ==
@@ -3533,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());
@@ -3707,24 +3708,27 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
if (cmSystemTools::FileIsFullPath(cleanPath)) {
cleanPath = cmSystemTools::CollapseFullPath(cleanPath);
}
const auto libPath = this->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()) {
@@ -3892,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);

View File

@@ -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}

View File

@@ -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()

View File

@@ -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>")

View File

@@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
@interface Foo : NSObject {
NSNumber* age;
}
@property (nonatomic, retain) NSNumber* age;
@end

View File

@@ -0,0 +1,7 @@
#import "foo.h"
@implementation Foo
@synthesize age;
@end

View File

@@ -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;
}