Browse Source

Add support for FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG>

Alexandru Croitor 5 years ago
parent
commit
ad3f69c86e

+ 1 - 0
Help/manual/cmake-properties.7.rst

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

+ 1 - 0
Help/manual/cmake-variables.7.rst

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

+ 3 - 0
Help/prop_tgt/CONFIG_POSTFIX.rst

@@ -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
 CMAKE_<CONFIG>_POSTFIX if it is set when a target is created.  This
 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.

+ 25 - 0
Help/prop_tgt/FRAMEWORK_MULTI_CONFIG_POSTFIX_CONFIG.rst

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

+ 7 - 0
Help/release/dev/framework-multi-config-postfix.rst

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

+ 8 - 0
Help/variable/CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_CONFIG.rst

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

+ 40 - 4
Source/cmGeneratorTarget.cxx

@@ -539,15 +539,43 @@ std::string cmGeneratorTarget::GetFileSuffix(
 std::string cmGeneratorTarget::GetFilePostfix(const std::string& config) const
 {
   const char* postfix = nullptr;
+  std::string frameworkPostfix;
   if (!config.empty()) {
     std::string configProp =
       cmStrCat(cmSystemTools::UpperCase(config), "_POSTFIX");
     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 &&
         (this->IsAppBundleOnApple() || this->IsFrameworkOnApple())) {
       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();
 }
@@ -4241,8 +4269,8 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames(
       targetNames.Real += this->GetFrameworkVersion();
       targetNames.Real += "/";
     }
-    targetNames.Real += targetNames.Base;
-    targetNames.SharedObject = targetNames.Real;
+    targetNames.Real += targetNames.Base + suffix;
+    targetNames.SharedObject = targetNames.Real + suffix;
   } else {
     // The library's soname.
     this->ComputeVersionedName(targetNames.SharedObject, prefix,
@@ -4417,7 +4445,15 @@ void cmGeneratorTarget::GetFullNameInternal(
   outBase += this->GetOutputName(config, artifact);
 
   // 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.
   if (const char* soversion = this->GetProperty("SOVERSION")) {

+ 3 - 0
Source/cmGeneratorTarget.h

@@ -588,6 +588,9 @@ public:
   /** Get target file postfix */
   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.
    * The meta data will be recomputed on demand.
    */

+ 13 - 1
Source/cmNinjaNormalTargetGenerator.cxx

@@ -812,8 +812,20 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
     targetOutputReal = this->ConvertToNinjaPath(targetOutputReal);
   } else if (gt->IsFrameworkOnApple()) {
     // 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(
-      tgtNames.Output, gt->GetDirectory(config), config);
+      tgtNames.Output, gt->GetDirectory(config), config, bundleSkipParts);
   } else if (gt->IsCFBundleOnApple()) {
     // Create the core foundation bundle.
     this->OSXBundleGenerator->CreateCFBundle(tgtNames.Output,

+ 14 - 12
Source/cmOSXBundleGenerator.cxx

@@ -56,9 +56,9 @@ void cmOSXBundleGenerator::CreateAppBundle(const std::string& targetName,
   outpath = out;
 }
 
-void cmOSXBundleGenerator::CreateFramework(const std::string& targetName,
-                                           const std::string& outpath,
-                                           const std::string& config)
+void cmOSXBundleGenerator::CreateFramework(
+  const std::string& targetName, const std::string& outpath,
+  const std::string& config, const cmOSXBundleGenerator::SkipParts& skipParts)
 {
   if (this->MustSkip()) {
     return;
@@ -77,16 +77,18 @@ void cmOSXBundleGenerator::CreateFramework(const std::string& targetName,
 
   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);
-  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
   if (this->Makefile->PlatformIsAppleEmbedded()) {

+ 11 - 1
Source/cmOSXBundleGenerator.h

@@ -19,6 +19,15 @@ class cmOSXBundleGenerator
 public:
   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
   // the directory within the bundle that contains the executable
   void CreateAppBundle(const std::string& targetName, std::string& root,
@@ -26,7 +35,8 @@ public:
 
   // create a framework at a given 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
   void CreateCFBundle(const std::string& targetName, const std::string& root,

+ 8 - 0
Source/cmTarget.cxx

@@ -300,6 +300,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
     initProp("PDB_OUTPUT_DIRECTORY");
     initProp("COMPILE_PDB_OUTPUT_DIRECTORY");
     initProp("FRAMEWORK");
+    initProp("FRAMEWORK_MULTI_CONFIG_POSTFIX");
     initProp("Fortran_FORMAT");
     initProp("Fortran_MODULE_DIRECTORY");
     initProp("Fortran_COMPILER_LAUNCHER");
@@ -435,6 +436,13 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
           cmStrCat(cmSystemTools::UpperCase(configName), "_POSTFIX");
         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);
+      }
     }
   }
 

+ 45 - 0
Tests/RunCMake/Framework/FrameworkMultiConfigPostfix-build-final-check.cmake

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

+ 25 - 0
Tests/RunCMake/Framework/FrameworkMultiConfigPostfix.cmake

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

+ 36 - 0
Tests/RunCMake/Framework/RunCMakeTest.cmake

@@ -45,3 +45,39 @@ framework_type_test(ios SHARED YES)
 framework_type_test(ios STATIC YES)
 framework_type_test(osx SHARED 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()