Add support for FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG>

This commit is contained in:
Alexandru Croitor 2020-01-27 11:23:13 +01:00
parent d016637eef
commit ad3f69c86e
15 changed files with 240 additions and 18 deletions

View File

@ -200,6 +200,7 @@ Properties on Targets
/prop_tgt/Fortran_FORMAT /prop_tgt/Fortran_FORMAT
/prop_tgt/Fortran_MODULE_DIRECTORY /prop_tgt/Fortran_MODULE_DIRECTORY
/prop_tgt/FRAMEWORK /prop_tgt/FRAMEWORK
/prop_tgt/FRAMEWORK_MULTI_CONFIG_POSTFIX_CONFIG
/prop_tgt/FRAMEWORK_VERSION /prop_tgt/FRAMEWORK_VERSION
/prop_tgt/GENERATOR_FILE_NAME /prop_tgt/GENERATOR_FILE_NAME
/prop_tgt/GHS_INTEGRITY_APP /prop_tgt/GHS_INTEGRITY_APP

View File

@ -388,6 +388,7 @@ Variables that Control the Build
/variable/CMAKE_EXE_LINKER_FLAGS_INIT /variable/CMAKE_EXE_LINKER_FLAGS_INIT
/variable/CMAKE_FOLDER /variable/CMAKE_FOLDER
/variable/CMAKE_FRAMEWORK /variable/CMAKE_FRAMEWORK
/variable/CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_CONFIG
/variable/CMAKE_Fortran_FORMAT /variable/CMAKE_Fortran_FORMAT
/variable/CMAKE_Fortran_MODULE_DIRECTORY /variable/CMAKE_Fortran_MODULE_DIRECTORY
/variable/CMAKE_GHS_NO_SOURCE_GROUP_FILE /variable/CMAKE_GHS_NO_SOURCE_GROUP_FILE

View File

@ -8,3 +8,6 @@ is appended to the target file name built on disk. For non-executable
targets, this property is initialized by the value of the variable targets, this property is initialized by the value of the variable
CMAKE_<CONFIG>_POSTFIX if it is set when a target is created. This CMAKE_<CONFIG>_POSTFIX if it is set when a target is created. This
property is ignored on the Mac for Frameworks and App Bundles. property is ignored on the Mac for Frameworks and App Bundles.
For macOS see also the :prop_tgt:`FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG>`
target property.

View File

@ -0,0 +1,25 @@
FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG>
---------------------------------------
Postfix to append to the framework file name for configuration <CONFIG>,
when using a multi-config generator (like Xcode and Ninja Multi-Config).
When building with configuration <CONFIG> the value of this property
is appended to the framework file name built on disk.
For example given a framework called ``my_fw``, a value of ``_debug``
for the :prop_tgt:`FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG>` property, and
``Debug;Release`` in `CMAKE_CONFIGURATION_TYPES`, the following relevant
files would be created for the ``Debug`` and ``Release`` configurations:
- Release/my_fw.framework/my_fw
- Release/my_fw.framework/Versions/A/my_fw
- Debug/my_fw.framework/my_fw_debug
- Debug/my_fw.framework/Versions/A/my_fw_debug
For framework targets, this property is initialized by the value of the
variable :variable:`CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG>` if it
is set when a target is created.
This property is ignored for non-framework targets, and when using single
config generators.

View File

@ -0,0 +1,7 @@
framework-multi-config-postfix
------------------------------
* The :prop_tgt:`FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG>` target property
and associated :variable:`CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG>`
variable were created to allow adding a postfix to the name of a
framework file name when using a multi-config generator.

View File

@ -0,0 +1,8 @@
CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG>
---------------------------------------------
Default framework filename postfix under configuration ``<CONFIG>`` when
using a multi-config generator.
When a framework target is created its :prop_tgt:`FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG>`
target property is initialized with the value of this variable if it is set.

View File

@ -539,15 +539,43 @@ std::string cmGeneratorTarget::GetFileSuffix(
std::string cmGeneratorTarget::GetFilePostfix(const std::string& config) const std::string cmGeneratorTarget::GetFilePostfix(const std::string& config) const
{ {
const char* postfix = nullptr; const char* postfix = nullptr;
std::string frameworkPostfix;
if (!config.empty()) { if (!config.empty()) {
std::string configProp = std::string configProp =
cmStrCat(cmSystemTools::UpperCase(config), "_POSTFIX"); cmStrCat(cmSystemTools::UpperCase(config), "_POSTFIX");
postfix = this->GetProperty(configProp); postfix = this->GetProperty(configProp);
// Mac application bundles and frameworks have no postfix.
// Mac application bundles and frameworks have no regular postfix like
// libraries do.
if (!this->IsImported() && postfix && if (!this->IsImported() && postfix &&
(this->IsAppBundleOnApple() || this->IsFrameworkOnApple())) { (this->IsAppBundleOnApple() || this->IsFrameworkOnApple())) {
postfix = nullptr; postfix = nullptr;
} }
// Frameworks created by multi config generators can have a special
// framework postfix.
frameworkPostfix = GetFrameworkMultiConfigPostfix(config);
if (!frameworkPostfix.empty()) {
postfix = frameworkPostfix.c_str();
}
}
return postfix ? postfix : std::string();
}
std::string cmGeneratorTarget::GetFrameworkMultiConfigPostfix(
const std::string& config) const
{
const char* postfix = nullptr;
if (!config.empty()) {
std::string configProp = cmStrCat("FRAMEWORK_MULTI_CONFIG_POSTFIX_",
cmSystemTools::UpperCase(config));
postfix = this->GetProperty(configProp);
if (!this->IsImported() && postfix &&
(this->IsFrameworkOnApple() &&
!GetGlobalGenerator()->IsMultiConfig())) {
postfix = nullptr;
}
} }
return postfix ? postfix : std::string(); return postfix ? postfix : std::string();
} }
@ -4241,8 +4269,8 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames(
targetNames.Real += this->GetFrameworkVersion(); targetNames.Real += this->GetFrameworkVersion();
targetNames.Real += "/"; targetNames.Real += "/";
} }
targetNames.Real += targetNames.Base; targetNames.Real += targetNames.Base + suffix;
targetNames.SharedObject = targetNames.Real; targetNames.SharedObject = targetNames.Real + suffix;
} else { } else {
// The library's soname. // The library's soname.
this->ComputeVersionedName(targetNames.SharedObject, prefix, this->ComputeVersionedName(targetNames.SharedObject, prefix,
@ -4417,7 +4445,15 @@ void cmGeneratorTarget::GetFullNameInternal(
outBase += this->GetOutputName(config, artifact); outBase += this->GetOutputName(config, artifact);
// Append the per-configuration postfix. // Append the per-configuration postfix.
outBase += configPostfix; // When using Xcode, the postfix should be part of the suffix rather than the
// base, because the suffix ends up being used in Xcode's EXECUTABLE_SUFFIX
// attribute.
if (this->IsFrameworkOnApple() &&
GetGlobalGenerator()->GetName() == "Xcode") {
targetSuffix = configPostfix.c_str();
} else {
outBase += configPostfix;
}
// Name shared libraries with their version number on some platforms. // Name shared libraries with their version number on some platforms.
if (const char* soversion = this->GetProperty("SOVERSION")) { if (const char* soversion = this->GetProperty("SOVERSION")) {

View File

@ -588,6 +588,9 @@ public:
/** Get target file postfix */ /** Get target file postfix */
std::string GetFilePostfix(const std::string& config) const; std::string GetFilePostfix(const std::string& config) const;
/** Get framework multi-config-specific postfix */
std::string GetFrameworkMultiConfigPostfix(const std::string& config) const;
/** Clears cached meta data for local and external source files. /** Clears cached meta data for local and external source files.
* The meta data will be recomputed on demand. * The meta data will be recomputed on demand.
*/ */

View File

@ -812,8 +812,20 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
targetOutputReal = this->ConvertToNinjaPath(targetOutputReal); targetOutputReal = this->ConvertToNinjaPath(targetOutputReal);
} else if (gt->IsFrameworkOnApple()) { } else if (gt->IsFrameworkOnApple()) {
// Create the library framework. // Create the library framework.
cmOSXBundleGenerator::SkipParts bundleSkipParts;
if (globalGen->GetName() == "Ninja Multi-Config") {
const auto postFix = this->GeneratorTarget->GetFilePostfix(config);
// Skip creating Info.plist when there are multiple configurations, and
// the current configuration has a postfix. The non-postfix configuration
// Info.plist can be used by all the other configurations.
if (!postFix.empty()) {
bundleSkipParts.infoPlist = true;
}
}
this->OSXBundleGenerator->CreateFramework( this->OSXBundleGenerator->CreateFramework(
tgtNames.Output, gt->GetDirectory(config), config); tgtNames.Output, gt->GetDirectory(config), config, bundleSkipParts);
} else if (gt->IsCFBundleOnApple()) { } else if (gt->IsCFBundleOnApple()) {
// Create the core foundation bundle. // Create the core foundation bundle.
this->OSXBundleGenerator->CreateCFBundle(tgtNames.Output, this->OSXBundleGenerator->CreateCFBundle(tgtNames.Output,

View File

@ -56,9 +56,9 @@ void cmOSXBundleGenerator::CreateAppBundle(const std::string& targetName,
outpath = out; outpath = out;
} }
void cmOSXBundleGenerator::CreateFramework(const std::string& targetName, void cmOSXBundleGenerator::CreateFramework(
const std::string& outpath, const std::string& targetName, const std::string& outpath,
const std::string& config) const std::string& config, const cmOSXBundleGenerator::SkipParts& skipParts)
{ {
if (this->MustSkip()) { if (this->MustSkip()) {
return; return;
@ -77,16 +77,18 @@ void cmOSXBundleGenerator::CreateFramework(const std::string& targetName,
std::string frameworkVersion = this->GT->GetFrameworkVersion(); std::string frameworkVersion = this->GT->GetFrameworkVersion();
// Configure the Info.plist file
std::string plist = newoutpath;
if (!this->Makefile->PlatformIsAppleEmbedded()) {
// Put the Info.plist file into the Resources directory.
this->MacContentFolders->insert("Resources");
plist += "/Resources";
}
plist += "/Info.plist";
std::string name = cmSystemTools::GetFilenameName(targetName); std::string name = cmSystemTools::GetFilenameName(targetName);
this->LocalGenerator->GenerateFrameworkInfoPList(this->GT, name, plist); if (!skipParts.infoPlist) {
// Configure the Info.plist file
std::string plist = newoutpath;
if (!this->Makefile->PlatformIsAppleEmbedded()) {
// Put the Info.plist file into the Resources directory.
this->MacContentFolders->insert("Resources");
plist += "/Resources";
}
plist += "/Info.plist";
this->LocalGenerator->GenerateFrameworkInfoPList(this->GT, name, plist);
}
// Generate Versions directory only for MacOSX frameworks // Generate Versions directory only for MacOSX frameworks
if (this->Makefile->PlatformIsAppleEmbedded()) { if (this->Makefile->PlatformIsAppleEmbedded()) {

View File

@ -19,6 +19,15 @@ class cmOSXBundleGenerator
public: public:
cmOSXBundleGenerator(cmGeneratorTarget* target); cmOSXBundleGenerator(cmGeneratorTarget* target);
struct SkipParts
{
SkipParts()
: infoPlist(false)
{
}
bool infoPlist; // NOLINT(modernize-use-default-member-init)
};
// create an app bundle at a given root, and return // create an app bundle at a given root, and return
// the directory within the bundle that contains the executable // the directory within the bundle that contains the executable
void CreateAppBundle(const std::string& targetName, std::string& root, void CreateAppBundle(const std::string& targetName, std::string& root,
@ -26,7 +35,8 @@ public:
// create a framework at a given root // create a framework at a given root
void CreateFramework(const std::string& targetName, const std::string& root, void CreateFramework(const std::string& targetName, const std::string& root,
const std::string& config); const std::string& config,
const SkipParts& skipParts = SkipParts());
// create a cf bundle at a given root // create a cf bundle at a given root
void CreateCFBundle(const std::string& targetName, const std::string& root, void CreateCFBundle(const std::string& targetName, const std::string& root,

View File

@ -300,6 +300,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
initProp("PDB_OUTPUT_DIRECTORY"); initProp("PDB_OUTPUT_DIRECTORY");
initProp("COMPILE_PDB_OUTPUT_DIRECTORY"); initProp("COMPILE_PDB_OUTPUT_DIRECTORY");
initProp("FRAMEWORK"); initProp("FRAMEWORK");
initProp("FRAMEWORK_MULTI_CONFIG_POSTFIX");
initProp("Fortran_FORMAT"); initProp("Fortran_FORMAT");
initProp("Fortran_MODULE_DIRECTORY"); initProp("Fortran_MODULE_DIRECTORY");
initProp("Fortran_COMPILER_LAUNCHER"); initProp("Fortran_COMPILER_LAUNCHER");
@ -435,6 +436,13 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
cmStrCat(cmSystemTools::UpperCase(configName), "_POSTFIX"); cmStrCat(cmSystemTools::UpperCase(configName), "_POSTFIX");
initProp(property); initProp(property);
} }
if (impl->TargetType == cmStateEnums::SHARED_LIBRARY ||
impl->TargetType == cmStateEnums::STATIC_LIBRARY) {
std::string property = cmStrCat("FRAMEWORK_MULTI_CONFIG_POSTFIX_",
cmSystemTools::UpperCase(configName));
initProp(property);
}
} }
} }

View File

@ -0,0 +1,45 @@
include("${RunCMake_TEST_BINARY_DIR}/FrameworkMultiConfigPostfixInfo.cmake")
get_filename_component(framework_location "${framework_dir}" DIRECTORY)
set(non_existent_debug_framework_dir "${framework_location}/${target_file_name}_debug.framework")
set(framework_resources "${framework_dir}/Resources")
set(plist_file "${framework_resources}/Info.plist")
set(symlink_release_path "${framework_dir}/${target_file_name}")
set(framework_release_path "${framework_dir}/Versions/A/${target_file_name}")
# When using a multi config generator (like Ninja Multi-Config and Xcode),
# the postfix will be applied to the debug framework library name and the symlink name.
# For single config generators, the name stays the same as the the release framework.
if(is_multi_config)
set(symlink_debug_path "${framework_dir}/${target_file_name}_debug")
set(framework_debug_path "${framework_dir}/Versions/A/${target_file_name}_debug")
else()
set(symlink_debug_path "${framework_dir}/${target_file_name}")
set(framework_debug_path "${framework_dir}/Versions/A/${target_file_name}")
endif()
if(NOT IS_DIRECTORY ${framework_dir})
message(SEND_ERROR "Framework dir not found at ${framework_dir}")
endif()
if(IS_DIRECTORY ${non_existent_debug_framework_dir})
message(SEND_ERROR
"A framework dir with a debug suffix should not exist at ${non_existent_debug_framework_dir}")
endif()
if(NOT IS_SYMLINK "${symlink_release_path}")
message(SEND_ERROR "Release framework symlink not found at ${symlink_release_path}")
endif()
if(NOT IS_SYMLINK "${symlink_debug_path}")
message(SEND_ERROR "Debug framework symlink not found at ${symlink_debug_path}")
endif()
if(NOT EXISTS "${framework_release_path}")
message(SEND_ERROR "Release framework not found at ${framework_release_path}")
endif()
if(NOT EXISTS "${framework_debug_path}")
message(SEND_ERROR "Debug framework not found at ${framework_debug_path}")
endif()

View File

@ -0,0 +1,25 @@
enable_language(C)
set(CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_DEBUG "_debug")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR}/bin)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR}/lib)
set(target_name "mylib")
add_library(${target_name} SHARED foo.c)
set_property(TARGET ${target_name} PROPERTY FRAMEWORK ON)
get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
string(APPEND content
"set(is_multi_config \"${is_multi_config}\")\n"
"set(framework_dir \"$<TARGET_BUNDLE_DIR:${target_name}>\")\n"
"set(target_file_name ${target_name})\n")
file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/FrameworkMultiConfigPostfixInfo.cmake
CONTENT "${content}")

View File

@ -45,3 +45,39 @@ framework_type_test(ios SHARED YES)
framework_type_test(ios STATIC YES) framework_type_test(ios STATIC YES)
framework_type_test(osx SHARED YES) framework_type_test(osx SHARED YES)
framework_type_test(osx STATIC YES) framework_type_test(osx STATIC YES)
function(framework_multi_config_postfix_test)
set(configure_name "FrameworkMultiConfigPostfix")
set(build_name "${configure_name}-build-intermediate")
set(build_name_final "${configure_name}-build-final")
if(RunCMake_GENERATOR MATCHES "Ninja Multi-Config")
set(RunCMake_TEST_OPTIONS
"-DCMAKE_CONFIGURATION_TYPES=Debug\\;Release;-DCMAKE_CROSS_CONFIGS=all")
elseif(RunCMake_GENERATOR MATCHES "Xcode")
set(RunCMake_TEST_OPTIONS
"-DCMAKE_CONFIGURATION_TYPES=Debug\\;Release")
else()
set(RunCMake_TEST_OPTIONS "-DCMAKE_BUILD_TYPE=Debug")
endif()
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${configure_name})
set(RunCMake_TEST_NO_CLEAN 1)
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
run_cmake(${configure_name})
unset(RunCMake_TEST_OPTIONS)
if(RunCMake_GENERATOR MATCHES "Ninja Multi-Config")
run_cmake_command(${build_name_final} ${CMAKE_COMMAND} --build . --target all:all)
elseif(RunCMake_GENERATOR MATCHES "Xcode")
run_cmake_command(${build_name} ${CMAKE_COMMAND} --build . --config Release)
run_cmake_command(${build_name} ${CMAKE_COMMAND} --build . --config Debug)
run_cmake_command(${build_name_final} ${CMAKE_COMMAND} --build . --config Debug)
else()
run_cmake_command(${build_name_final} ${CMAKE_COMMAND} --build .)
endif()
endfunction()
framework_multi_config_postfix_test()