Переглянути джерело

Merge topic 'cpack-rpm-single-debuginfo'

bb8cf521 CPack/RPM single debuginfo packaging
758f58b4 CPack/RPM learned defining main component
Brad King 9 роки тому
батько
коміт
cb5cadf3c7

+ 11 - 0
Help/release/dev/cpack-rpm-single-debuginfo.rst

@@ -0,0 +1,11 @@
+cpack-rpm-single-debuginfo
+--------------------------
+
+* The :module:`CPackRPM` module learned to generate main component package
+  which forces generation of a rpm for defined component without component
+  suffix in filename and package name.
+  See :variable:`CPACK_RPM_MAIN_COMPONENT` variable.
+
+* The :module:`CPackRPM` module learned to generate a single debuginfo package
+  on demand even if components packagin is used.
+  See :variable:`CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE` variable.

+ 266 - 56
Modules/CPackRPM.cmake

@@ -95,6 +95,17 @@
 #    and it is up to the packager to set the variables in a manner that will
 #    prevent such errors.
 #
+# .. variable:: CPACK_RPM_MAIN_COMPONENT
+#
+#  Main component that is packaged without component suffix.
+#
+#  * Mandatory : NO
+#  * Default   : -
+#
+#  This variable can be set to any component or group name so that component or
+#  group rpm package is generated without component suffix in filename and
+#  package name.
+#
 # .. variable:: CPACK_RPM_PACKAGE_VERSION
 #
 #  The RPM package version.
@@ -792,6 +803,26 @@
 #  * Mandatory : NO
 #  * Default   : -
 #
+# .. variable:: CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE
+#
+#  Create a single debuginfo package even if components packaging is set.
+#
+#  * Mandatory : NO
+#  * Default   : OFF
+#
+#  When this variable is enabled it produces a single debuginfo package even if
+#  component packaging is enabled.
+#
+#  When using this feature in combination with components packaging and there is
+#  more than one component this variable requires :variable:`CPACK_RPM_MAIN_COMPONENT`
+#  to be set.
+#
+# .. note::
+#
+#  If none of the :variable:`CPACK_RPM_<component>_DEBUGINFO_PACKAGE` variables
+#  is set then :variable:`CPACK_RPM_DEBUGINFO_PACKAGE` is automatically set to
+#  ``ON`` when :variable:`CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE` is set.
+#
 # Packaging of sources (SRPM)
 # ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 #
@@ -1649,10 +1680,16 @@ function(cpack_rpm_generate_package)
   endif()
 
   if(CPACK_RPM_PACKAGE_COMPONENT)
-    string(APPEND CPACK_RPM_PACKAGE_NAME "-${CPACK_RPM_PACKAGE_COMPONENT}")
-    cpack_rpm_variable_fallback("CPACK_RPM_PACKAGE_NAME"
-      "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_PACKAGE_NAME"
-      "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_PACKAGE_NAME")
+    string(TOUPPER "${CPACK_RPM_MAIN_COMPONENT}"
+      CPACK_RPM_MAIN_COMPONENT_UPPER)
+
+    if(NOT CPACK_RPM_MAIN_COMPONENT_UPPER STREQUAL CPACK_RPM_PACKAGE_COMPONENT_UPPER)
+      string(APPEND CPACK_RPM_PACKAGE_NAME "-${CPACK_RPM_PACKAGE_COMPONENT}")
+
+      cpack_rpm_variable_fallback("CPACK_RPM_PACKAGE_NAME"
+        "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_PACKAGE_NAME"
+        "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_PACKAGE_NAME")
+    endif()
   endif()
 
   # CPACK_RPM_PACKAGE_VERSION (mandatory)
@@ -2059,7 +2096,7 @@ function(cpack_rpm_generate_package)
     "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_DEBUGINFO_PACKAGE"
     "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_DEBUGINFO_PACKAGE"
     "CPACK_RPM_DEBUGINFO_PACKAGE")
-  if(CPACK_RPM_DEBUGINFO_PACKAGE)
+  if(CPACK_RPM_DEBUGINFO_PACKAGE OR (CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE AND NOT GENERATE_SPEC_PARTS))
     cpack_rpm_variable_fallback("CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX"
       "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT}_BUILD_SOURCE_DIRS_PREFIX"
       "CPACK_RPM_${CPACK_RPM_PACKAGE_COMPONENT_UPPER}_BUILD_SOURCE_DIRS_PREFIX"
@@ -2067,9 +2104,81 @@ function(cpack_rpm_generate_package)
     if(NOT CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX)
       set(CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX "/usr/src/debug/${CPACK_PACKAGE_FILE_NAME}${CPACK_RPM_PACKAGE_COMPONENT_PART_PATH}")
     endif()
-    cpack_rpm_debugsymbol_check("${CPACK_RPM_INSTALL_FILES}" "${WDIR}")
 
-    set(TMP_RPM_DEBUGINFO "
+    if(CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE AND GENERATE_SPEC_PARTS)
+      file(WRITE "${CPACK_RPM_ROOTDIR}/SPECS/${CPACK_RPM_PACKAGE_COMPONENT}.files"
+        "${CPACK_RPM_INSTALL_FILES}")
+    else()
+      if(CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE AND CPACK_RPM_PACKAGE_COMPONENT)
+        # this part is only required by components packaging - with monolithic
+        # packages we can be certain that there are no other components present
+        # so CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE is a noop
+        if(CPACK_RPM_DEBUGINFO_PACKAGE)
+          # only add current package files to debuginfo list if debuginfo
+          # generation is enabled for current package
+          set(install_files_ "${CPACK_RPM_INSTALL_FILES}")
+        else()
+          unset(install_files_)
+        endif()
+
+        file(GLOB files_ "${CPACK_RPM_DIRECTORY}/SPECS/*.files")
+
+        foreach(f_ IN LISTS files_)
+          file(READ "${f_}" tmp_)
+          string(APPEND install_files_ ";${tmp_}")
+        endforeach()
+
+        # if there were other components/groups so we need to move files from them
+        # to current component otherwise those files won't be found
+        file(GLOB components_ LIST_DIRECTORIES true RELATIVE
+          "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}"
+          "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}/*")
+        foreach(component_ IN LISTS components_)
+          string(TOUPPER "${component_}" component_dir_upper_)
+          if(component_dir_upper_ STREQUAL CPACK_RPM_PACKAGE_COMPONENT_UPPER)
+            # skip current component
+            continue()
+          endif()
+
+          cmake_policy(PUSH)
+            cmake_policy(SET CMP0009 NEW)
+            file(GLOB_RECURSE files_for_move_ LIST_DIRECTORIES false RELATIVE
+              "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}/${component_}"
+              "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}/${component_}/*")
+          cmake_policy(POP)
+
+          foreach(f_ IN LISTS files_for_move_)
+            get_filename_component(dir_path_ "${f_}" DIRECTORY)
+            set(src_file_
+              "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}/${component_}/${f_}")
+
+            # check that we are not overriding an existing file that doesn't
+            # match the file that we want to copy
+            if(EXISTS "${src_file_}" AND EXISTS "${WDIR}/${f_}")
+              execute_process(
+                  COMMAND ${CMAKE_COMMAND} -E compare_files "${src_file_}" "${WDIR}/${f_}"
+                  RESULT_VARIABLE res_
+                )
+              if(res_)
+                message(FATAL_ERROR "CPackRPM:Error: File on path '${WDIR}/${f_}'"
+                  " already exists but is a different than the one in component"
+                  " '${component_}'! Packages will not be generated.")
+              endif()
+            endif()
+
+            file(MAKE_DIRECTORY "${WDIR}/${dir_path_}")
+            file(RENAME "${src_file_}"
+              "${WDIR}/${f_}")
+          endforeach()
+        endforeach()
+
+        cpack_rpm_debugsymbol_check("${install_files_}" "${WDIR}")
+      else()
+        cpack_rpm_debugsymbol_check("${CPACK_RPM_INSTALL_FILES}" "${WDIR}")
+      endif()
+
+      if(TMP_DEBUGINFO_ADDITIONAL_SOURCES)
+        set(TMP_RPM_DEBUGINFO "
 # Modified version of %%debug_package macro
 # defined in /usr/lib/rpm/macros as that one
 # can't handle injection of extra source files.
@@ -2088,6 +2197,15 @@ package or when debugging this package.
 ${TMP_DEBUGINFO_ADDITIONAL_SOURCES}
 %endif
 ")
+      elseif(CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE)
+        message(AUTHOR_WARNING "CPackRPM:Warning: debuginfo package was requested"
+          " but will not be generated as no source files were found!")
+      else()
+        message(AUTHOR_WARNING "CPackRPM:Warning: debuginfo package was requested"
+          " but will not be generated as no source files were found! Component: '"
+          "${CPACK_RPM_PACKAGE_COMPONENT}'.")
+      endif()
+    endif()
   endif()
 
   # Prepare install files
@@ -2164,12 +2282,25 @@ ${TMP_DEBUGINFO_ADDITIONAL_SOURCES}
       cmake_policy(POP)
     else()
       # old file name format for back compatibility
-      set(CPACK_RPM_FILE_NAME "${CPACK_OUTPUT_FILE_NAME}")
+      string(TOUPPER "${CPACK_RPM_MAIN_COMPONENT}"
+        CPACK_RPM_MAIN_COMPONENT_UPPER)
+
+      if(CPACK_RPM_MAIN_COMPONENT_UPPER STREQUAL CPACK_RPM_PACKAGE_COMPONENT_UPPER)
+        # this is the main component so ignore the component filename part
+        set(CPACK_RPM_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}.rpm")
+      else()
+        set(CPACK_RPM_FILE_NAME "${CPACK_OUTPUT_FILE_NAME}")
+      endif()
     endif()
     # else example:
     #set(CPACK_RPM_FILE_NAME "${CPACK_RPM_PACKAGE_NAME}-${CPACK_RPM_PACKAGE_VERSION}-${CPACK_RPM_PACKAGE_RELEASE}-${CPACK_RPM_PACKAGE_ARCHITECTURE}.rpm")
 
-    if(NOT CPACK_RPM_DEBUGINFO_PACKAGE)
+    if(CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE AND GENERATE_SPEC_PARTS)
+      string(TOLOWER "${CPACK_RPM_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}.*\\.rpm" expected_filename_)
+
+      file(WRITE "${CPACK_RPM_ROOTDIR}/SPECS/${CPACK_RPM_PACKAGE_COMPONENT}.rpm_name"
+        "${expected_filename_};${CPACK_RPM_FILE_NAME}")
+    elseif(NOT CPACK_RPM_DEBUGINFO_PACKAGE)
       set(FILE_NAME_DEFINE "%define _rpmfilename ${CPACK_RPM_FILE_NAME}")
     endif()
   endif()
@@ -2264,7 +2395,54 @@ Vendor:         \@CPACK_RPM_PACKAGE_VENDOR\@
 \@CPACK_RPM_SPEC_CHANGELOG\@
 "
     )
+
+  elseif(GENERATE_SPEC_PARTS) # binary rpm with single debuginfo package
+    file(WRITE ${CPACK_RPM_BINARY_SPECFILE}.in
+        "# -*- rpm-spec -*-
+%package -n \@CPACK_RPM_PACKAGE_NAME\@
+Summary:        \@CPACK_RPM_PACKAGE_SUMMARY\@
+Version:        \@CPACK_RPM_PACKAGE_VERSION\@
+Release:        \@CPACK_RPM_PACKAGE_RELEASE\@
+License:        \@CPACK_RPM_PACKAGE_LICENSE\@
+Group:          \@CPACK_RPM_PACKAGE_GROUP\@
+Vendor:         \@CPACK_RPM_PACKAGE_VENDOR\@
+
+\@TMP_RPM_URL\@
+\@TMP_RPM_REQUIRES\@
+\@TMP_RPM_REQUIRES_PRE\@
+\@TMP_RPM_REQUIRES_POST\@
+\@TMP_RPM_REQUIRES_PREUN\@
+\@TMP_RPM_REQUIRES_POSTUN\@
+\@TMP_RPM_PROVIDES\@
+\@TMP_RPM_OBSOLETES\@
+\@TMP_RPM_CONFLICTS\@
+\@TMP_RPM_AUTOPROV\@
+\@TMP_RPM_AUTOREQ\@
+\@TMP_RPM_AUTOREQPROV\@
+\@TMP_RPM_BUILDARCH\@
+\@TMP_RPM_PREFIXES\@
+
+%description -n \@CPACK_RPM_PACKAGE_NAME\@
+\@CPACK_RPM_PACKAGE_DESCRIPTION\@
+
+%files -n \@CPACK_RPM_PACKAGE_NAME\@
+%defattr(\@TMP_DEFAULT_FILE_PERMISSIONS\@,\@TMP_DEFAULT_USER\@,\@TMP_DEFAULT_GROUP\@,\@TMP_DEFAULT_DIR_PERMISSIONS\@)
+\@CPACK_RPM_INSTALL_FILES\@
+\@CPACK_RPM_ABSOLUTE_INSTALL_FILES\@
+\@CPACK_RPM_USER_INSTALL_FILES\@
+"
+    )
+
   else()  # binary rpm
+    if(CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE)
+      # find generated spec file and take its name
+      file(GLOB spec_files_ "${CPACK_RPM_DIRECTORY}/SPECS/*.spec")
+
+      foreach(f_ IN LISTS spec_files_)
+        file(READ "${f_}" tmp_)
+        string(APPEND TMP_OTHER_COMPONENTS "\n${tmp_}\n")
+      endforeach()
+    endif()
 
     # We should generate a USER spec file template:
     #  - either because the user asked for it : CPACK_RPM_GENERATE_USER_BINARY_SPECFILE_TEMPLATE
@@ -2350,6 +2528,8 @@ mv %_topdir/tmpBBroot $RPM_BUILD_ROOT
 
 %changelog
 \@CPACK_RPM_SPEC_CHANGELOG\@
+
+\@TMP_OTHER_COMPONENTS\@
 "
       )
     endif()
@@ -2376,60 +2556,90 @@ mv %_topdir/tmpBBroot $RPM_BUILD_ROOT
     configure_file(${CPACK_RPM_BINARY_SPECFILE}.in ${CPACK_RPM_BINARY_SPECFILE} @ONLY)
   endif()
 
-  if(RPMBUILD_EXECUTABLE)
-    # Now call rpmbuild using the SPECFILE
-    execute_process(
-      COMMAND "${RPMBUILD_EXECUTABLE}" ${RPMBUILD_FLAGS}
-              --define "_topdir ${CPACK_RPM_DIRECTORY}"
-              --buildroot "%_topdir/${CPACK_PACKAGE_FILE_NAME}${CPACK_RPM_PACKAGE_COMPONENT_PART_PATH}"
-              --target "${CPACK_RPM_PACKAGE_ARCHITECTURE}"
-              "${CPACK_RPM_BINARY_SPECFILE}"
-      WORKING_DIRECTORY "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}${CPACK_RPM_PACKAGE_COMPONENT_PART_PATH}"
-      RESULT_VARIABLE CPACK_RPMBUILD_EXEC_RESULT
-      ERROR_FILE "${CPACK_TOPLEVEL_DIRECTORY}/rpmbuild${CPACK_RPM_PACKAGE_NAME}.err"
-      OUTPUT_FILE "${CPACK_TOPLEVEL_DIRECTORY}/rpmbuild${CPACK_RPM_PACKAGE_NAME}.out")
-    if(CPACK_RPM_PACKAGE_DEBUG OR CPACK_RPMBUILD_EXEC_RESULT)
-      file(READ ${CPACK_TOPLEVEL_DIRECTORY}/rpmbuild${CPACK_RPM_PACKAGE_NAME}.err RPMBUILDERR)
-      file(READ ${CPACK_TOPLEVEL_DIRECTORY}/rpmbuild${CPACK_RPM_PACKAGE_NAME}.out RPMBUILDOUT)
-      message("CPackRPM:Debug: You may consult rpmbuild logs in: ")
-      message("CPackRPM:Debug:    - ${CPACK_TOPLEVEL_DIRECTORY}/rpmbuild${CPACK_RPM_PACKAGE_NAME}.err")
-      message("CPackRPM:Debug: *** ${RPMBUILDERR} ***")
-      message("CPackRPM:Debug:    - ${CPACK_TOPLEVEL_DIRECTORY}/rpmbuild${CPACK_RPM_PACKAGE_NAME}.out")
-      message("CPackRPM:Debug: *** ${RPMBUILDOUT} ***")
+  if(NOT GENERATE_SPEC_PARTS) # generate package
+    if(RPMBUILD_EXECUTABLE)
+      # Now call rpmbuild using the SPECFILE
+      execute_process(
+        COMMAND "${RPMBUILD_EXECUTABLE}" ${RPMBUILD_FLAGS}
+                --define "_topdir ${CPACK_RPM_DIRECTORY}"
+                --buildroot "%_topdir/${CPACK_PACKAGE_FILE_NAME}${CPACK_RPM_PACKAGE_COMPONENT_PART_PATH}"
+                --target "${CPACK_RPM_PACKAGE_ARCHITECTURE}"
+                "${CPACK_RPM_BINARY_SPECFILE}"
+        WORKING_DIRECTORY "${CPACK_TOPLEVEL_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}${CPACK_RPM_PACKAGE_COMPONENT_PART_PATH}"
+        RESULT_VARIABLE CPACK_RPMBUILD_EXEC_RESULT
+        ERROR_FILE "${CPACK_TOPLEVEL_DIRECTORY}/rpmbuild${CPACK_RPM_PACKAGE_NAME}.err"
+        OUTPUT_FILE "${CPACK_TOPLEVEL_DIRECTORY}/rpmbuild${CPACK_RPM_PACKAGE_NAME}.out")
+      if(CPACK_RPM_PACKAGE_DEBUG OR CPACK_RPMBUILD_EXEC_RESULT)
+        file(READ ${CPACK_TOPLEVEL_DIRECTORY}/rpmbuild${CPACK_RPM_PACKAGE_NAME}.err RPMBUILDERR)
+        file(READ ${CPACK_TOPLEVEL_DIRECTORY}/rpmbuild${CPACK_RPM_PACKAGE_NAME}.out RPMBUILDOUT)
+        message("CPackRPM:Debug: You may consult rpmbuild logs in: ")
+        message("CPackRPM:Debug:    - ${CPACK_TOPLEVEL_DIRECTORY}/rpmbuild${CPACK_RPM_PACKAGE_NAME}.err")
+        message("CPackRPM:Debug: *** ${RPMBUILDERR} ***")
+        message("CPackRPM:Debug:    - ${CPACK_TOPLEVEL_DIRECTORY}/rpmbuild${CPACK_RPM_PACKAGE_NAME}.out")
+        message("CPackRPM:Debug: *** ${RPMBUILDOUT} ***")
+      endif()
+    else()
+      if(ALIEN_EXECUTABLE)
+        message(FATAL_ERROR "RPM packaging through alien not done (yet)")
+      endif()
     endif()
-  else()
-    if(ALIEN_EXECUTABLE)
-      message(FATAL_ERROR "RPM packaging through alien not done (yet)")
+
+    # find generated rpm files and take their names
+    cmake_policy(PUSH)
+      # Tell file(GLOB_RECURSE) not to follow directory symlinks
+      # even if the project does not set this policy to NEW.
+      cmake_policy(SET CMP0009 NEW)
+      file(GLOB_RECURSE GENERATED_FILES "${CPACK_RPM_DIRECTORY}/RPMS/*.rpm"
+        "${CPACK_RPM_DIRECTORY}/SRPMS/*.rpm")
+    cmake_policy(POP)
+
+    if(NOT GENERATED_FILES)
+      message(FATAL_ERROR "RPM package was not generated! ${CPACK_RPM_DIRECTORY}")
     endif()
-  endif()
 
-  # find generated rpm files and take their names
-  cmake_policy(PUSH)
-    # Tell file(GLOB_RECURSE) not to follow directory symlinks
-    # even if the project does not set this policy to NEW.
-    cmake_policy(SET CMP0009 NEW)
-    file(GLOB_RECURSE GENERATED_FILES "${CPACK_RPM_DIRECTORY}/RPMS/*.rpm"
-      "${CPACK_RPM_DIRECTORY}/SRPMS/*.rpm")
-  cmake_policy(POP)
+    unset(expected_filenames_)
+    unset(filenames_)
+    if(CPACK_RPM_DEBUGINFO_PACKAGE AND NOT CPACK_RPM_FILE_NAME STREQUAL "RPM-DEFAULT")
+      string(TOLOWER "${CPACK_RPM_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}.*\\.rpm" efn_)
+      list(APPEND expected_filenames_ "${efn_}")
+      list(APPEND filenames_ "${CPACK_RPM_FILE_NAME}")
+    endif()
 
-  if(NOT GENERATED_FILES)
-    message(FATAL_ERROR "RPM package was not generated! ${CPACK_RPM_DIRECTORY}")
-  endif()
+    # check if other files have to be renamed
+    file(GLOB rename_files_ "${CPACK_RPM_DIRECTORY}/SPECS/*.rpm_name")
+    if(rename_files_)
+      foreach(f_ IN LISTS rename_files_)
+        file(READ "${f_}" tmp_)
+        list(GET tmp_ 0 efn_)
+        list(APPEND expected_filenames_ "${efn_}")
+        list(GET tmp_ 1 fn_)
+        list(APPEND filenames_ "${fn_}")
+      endforeach()
+    endif()
 
-  if(CPACK_RPM_DEBUGINFO_PACKAGE AND NOT CPACK_RPM_FILE_NAME STREQUAL "RPM-DEFAULT")
-    string(TOLOWER "${CPACK_RPM_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}.*\\.rpm" EXPECTED_FILENAME)
+    if(expected_filenames_)
+      foreach(F IN LISTS GENERATED_FILES)
+        unset(matched_)
+        foreach(expected_ IN LISTS expected_filenames_)
+          if(F MATCHES ".*/${expected_}")
+            list(FIND expected_filenames_ "${expected_}" idx_)
+            list(GET filenames_ ${idx_} filename_)
+            get_filename_component(FILE_PATH "${F}" DIRECTORY)
+            file(RENAME "${F}" "${FILE_PATH}/${filename_}")
+            list(APPEND new_files_list_ "${FILE_PATH}/${filename_}")
+            set(matched_ "YES")
+
+            break()
+          endif()
+        endforeach()
 
-    foreach(F IN LISTS GENERATED_FILES)
-      if(F MATCHES ".*/${EXPECTED_FILENAME}")
-        get_filename_component(FILE_PATH "${F}" DIRECTORY)
-        file(RENAME "${F}" "${FILE_PATH}/${CPACK_RPM_FILE_NAME}")
-        list(APPEND new_files_list_ "${FILE_PATH}/${CPACK_RPM_FILE_NAME}")
-      else()
-        list(APPEND new_files_list_ "${F}")
-      endif()
-    endforeach()
+        if(NOT matched_)
+          list(APPEND new_files_list_ "${F}")
+        endif()
+      endforeach()
 
-    set(GENERATED_FILES "${new_files_list_}")
+      set(GENERATED_FILES "${new_files_list_}")
+    endif()
   endif()
 
   set(GEN_CPACK_OUTPUT_FILES "${GENERATED_FILES}" PARENT_SCOPE)

+ 217 - 28
Source/CPack/cmCPackRPMGenerator.cxx

@@ -105,39 +105,224 @@ int cmCPackRPMGenerator::PackageComponents(bool ignoreGroup)
   packageFileNames.clear();
   std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
 
-  // The default behavior is to have one package by component group
-  // unless CPACK_COMPONENTS_IGNORE_GROUP is specified.
-  if (!ignoreGroup) {
-    std::map<std::string, cmCPackComponentGroup>::iterator compGIt;
-    for (compGIt = this->ComponentGroups.begin();
-         compGIt != this->ComponentGroups.end(); ++compGIt) {
-      cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Packaging component group: "
-                      << compGIt->first << std::endl);
-      retval &= PackageOnePack(initialTopLevel, compGIt->first);
+  const char* mainComponent = this->GetOption("CPACK_RPM_MAIN_COMPONENT");
+
+  if (this->IsOn("CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE") &&
+      !this->IsOn("CPACK_RPM_DEBUGINFO_PACKAGE")) {
+    // check if we need to set CPACK_RPM_DEBUGINFO_PACKAGE because non of
+    // the components is setting per component debuginfo package variable
+    bool shouldSet = true;
+
+    if (ignoreGroup) {
+      std::map<std::string, cmCPackComponent>::iterator compIt;
+      for (compIt = this->Components.begin(); compIt != this->Components.end();
+           ++compIt) {
+        std::string component(compIt->first);
+        std::transform(component.begin(), component.end(), component.begin(),
+                       ::toupper);
+
+        if (this->IsOn("CPACK_RPM_" + compIt->first + "_DEBUGINFO_PACKAGE") ||
+            this->IsOn("CPACK_RPM_" + component + "_DEBUGINFO_PACKAGE")) {
+          shouldSet = false;
+          break;
+        }
+      }
+    } else {
+      std::map<std::string, cmCPackComponentGroup>::iterator compGIt;
+      for (compGIt = this->ComponentGroups.begin();
+           compGIt != this->ComponentGroups.end(); ++compGIt) {
+        std::string component(compGIt->first);
+        std::transform(component.begin(), component.end(), component.begin(),
+                       ::toupper);
+
+        if (this->IsOn("CPACK_RPM_" + compGIt->first + "_DEBUGINFO_PACKAGE") ||
+            this->IsOn("CPACK_RPM_" + component + "_DEBUGINFO_PACKAGE")) {
+          shouldSet = false;
+          break;
+        }
+      }
+
+      if (shouldSet) {
+        std::map<std::string, cmCPackComponent>::iterator compIt;
+        for (compIt = this->Components.begin();
+             compIt != this->Components.end(); ++compIt) {
+          // Does the component belong to a group?
+          if (compIt->second.Group == CM_NULLPTR) {
+            std::string component(compIt->first);
+            std::transform(component.begin(), component.end(),
+                           component.begin(), ::toupper);
+
+            if (this->IsOn("CPACK_RPM_" + compIt->first +
+                           "_DEBUGINFO_PACKAGE") ||
+                this->IsOn("CPACK_RPM_" + component + "_DEBUGINFO_PACKAGE")) {
+              shouldSet = false;
+              break;
+            }
+          }
+        }
+      }
+    }
+
+    if (shouldSet) {
+      cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Setting "
+                      << "CPACK_RPM_DEBUGINFO_PACKAGE because "
+                      << "CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE is set but "
+                      << " none of the "
+                      << "CPACK_RPM_<component>_DEBUGINFO_PACKAGE variables "
+                      << "are set." << std::endl);
+      this->SetOption("CPACK_RPM_DEBUGINFO_PACKAGE", "ON");
     }
-    // Handle Orphan components (components not belonging to any groups)
-    std::map<std::string, cmCPackComponent>::iterator compIt;
-    for (compIt = this->Components.begin(); compIt != this->Components.end();
-         ++compIt) {
-      // Does the component belong to a group?
-      if (compIt->second.Group == CM_NULLPTR) {
-        cmCPackLogger(
-          cmCPackLog::LOG_VERBOSE, "Component <"
-            << compIt->second.Name
-            << "> does not belong to any group, package it separately."
-            << std::endl);
+  }
+
+  if (mainComponent) {
+    if (this->IsOn("CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE")) {
+      this->SetOption("GENERATE_SPEC_PARTS", "ON");
+    }
+
+    std::string mainComponentUpper(mainComponent);
+    std::transform(mainComponentUpper.begin(), mainComponentUpper.end(),
+                   mainComponentUpper.begin(), ::toupper);
+
+    // The default behavior is to have one package by component group
+    // unless CPACK_COMPONENTS_IGNORE_GROUP is specified.
+    if (!ignoreGroup) {
+      std::map<std::string, cmCPackComponentGroup>::iterator mainCompGIt =
+        this->ComponentGroups.end();
+
+      std::map<std::string, cmCPackComponentGroup>::iterator compGIt;
+      for (compGIt = this->ComponentGroups.begin();
+           compGIt != this->ComponentGroups.end(); ++compGIt) {
+        std::string component(compGIt->first);
+        std::transform(component.begin(), component.end(), component.begin(),
+                       ::toupper);
+
+        if (mainComponentUpper == component) {
+          // main component will be handled last
+          mainCompGIt = compGIt;
+          continue;
+        }
+
+        cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Packaging component group: "
+                        << compGIt->first << std::endl);
+        retval &= PackageOnePack(initialTopLevel, compGIt->first);
+      }
+      // Handle Orphan components (components not belonging to any groups)
+      std::map<std::string, cmCPackComponent>::iterator mainCompIt =
+        this->Components.end();
+      std::map<std::string, cmCPackComponent>::iterator compIt;
+      for (compIt = this->Components.begin(); compIt != this->Components.end();
+           ++compIt) {
+        // Does the component belong to a group?
+        if (compIt->second.Group == CM_NULLPTR) {
+          std::string component(compIt->first);
+          std::transform(component.begin(), component.end(), component.begin(),
+                         ::toupper);
+
+          if (mainComponentUpper == component) {
+            // main component will be handled last
+            mainCompIt = compIt;
+            continue;
+          }
+
+          cmCPackLogger(
+            cmCPackLog::LOG_VERBOSE, "Component <"
+              << compIt->second.Name
+              << "> does not belong to any group, package it separately."
+              << std::endl);
+          retval &= PackageOnePack(initialTopLevel, compIt->first);
+        }
+      }
+
+      if (retval) {
+        this->SetOption("GENERATE_SPEC_PARTS", "OFF");
+
+        if (mainCompGIt != this->ComponentGroups.end()) {
+          retval &= PackageOnePack(initialTopLevel, mainCompGIt->first);
+        } else if (mainCompIt != this->Components.end()) {
+          retval &= PackageOnePack(initialTopLevel, mainCompIt->first);
+        } else {
+          cmCPackLogger(cmCPackLog::LOG_ERROR, "CPACK_RPM_MAIN_COMPONENT set"
+                          << " to non existing component.\n");
+          retval = 0;
+        }
+      }
+    }
+    // CPACK_COMPONENTS_IGNORE_GROUPS is set
+    // We build 1 package per component
+    else {
+      std::map<std::string, cmCPackComponent>::iterator mainCompIt =
+        this->Components.end();
+
+      std::map<std::string, cmCPackComponent>::iterator compIt;
+      for (compIt = this->Components.begin(); compIt != this->Components.end();
+           ++compIt) {
+        std::string component(compIt->first);
+        std::transform(component.begin(), component.end(), component.begin(),
+                       ::toupper);
+
+        if (mainComponentUpper == component) {
+          // main component will be handled last
+          mainCompIt = compIt;
+          continue;
+        }
+
         retval &= PackageOnePack(initialTopLevel, compIt->first);
       }
+
+      if (retval) {
+        this->SetOption("GENERATE_SPEC_PARTS", "OFF");
+
+        if (mainCompIt != this->Components.end()) {
+          retval &= PackageOnePack(initialTopLevel, mainCompIt->first);
+        } else {
+          cmCPackLogger(cmCPackLog::LOG_ERROR, "CPACK_RPM_MAIN_COMPONENT set"
+                          << " to non existing component.\n");
+          retval = 0;
+        }
+      }
     }
-  }
-  // CPACK_COMPONENTS_IGNORE_GROUPS is set
-  // We build 1 package per component
-  else {
-    std::map<std::string, cmCPackComponent>::iterator compIt;
-    for (compIt = this->Components.begin(); compIt != this->Components.end();
-         ++compIt) {
-      retval &= PackageOnePack(initialTopLevel, compIt->first);
+  } else if (!this->IsOn("CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE") ||
+             this->Components.size() == 1) {
+    // The default behavior is to have one package by component group
+    // unless CPACK_COMPONENTS_IGNORE_GROUP is specified.
+    if (!ignoreGroup) {
+      std::map<std::string, cmCPackComponentGroup>::iterator compGIt;
+      for (compGIt = this->ComponentGroups.begin();
+           compGIt != this->ComponentGroups.end(); ++compGIt) {
+        cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Packaging component group: "
+                        << compGIt->first << std::endl);
+        retval &= PackageOnePack(initialTopLevel, compGIt->first);
+      }
+      // Handle Orphan components (components not belonging to any groups)
+      std::map<std::string, cmCPackComponent>::iterator compIt;
+      for (compIt = this->Components.begin(); compIt != this->Components.end();
+           ++compIt) {
+        // Does the component belong to a group?
+        if (compIt->second.Group == CM_NULLPTR) {
+          cmCPackLogger(
+            cmCPackLog::LOG_VERBOSE, "Component <"
+              << compIt->second.Name
+              << "> does not belong to any group, package it separately."
+              << std::endl);
+          retval &= PackageOnePack(initialTopLevel, compIt->first);
+        }
+      }
+    }
+    // CPACK_COMPONENTS_IGNORE_GROUPS is set
+    // We build 1 package per component
+    else {
+      std::map<std::string, cmCPackComponent>::iterator compIt;
+      for (compIt = this->Components.begin(); compIt != this->Components.end();
+           ++compIt) {
+        retval &= PackageOnePack(initialTopLevel, compIt->first);
+      }
     }
+  } else {
+    cmCPackLogger(
+      cmCPackLog::LOG_ERROR, "CPACK_RPM_MAIN_COMPONENT not set but"
+        << " it is mandatory with CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE"
+        << " being set.\n");
+    retval = 0;
   }
 
   if (retval) {
@@ -156,6 +341,10 @@ int cmCPackRPMGenerator::PackageComponentsAllInOne(
   packageFileNames.clear();
   std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
 
+  if (this->IsOn("CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE")) {
+    this->SetOption("CPACK_RPM_DEBUGINFO_PACKAGE", "ON");
+  }
+
   cmCPackLogger(cmCPackLog::LOG_VERBOSE,
                 "Packaging all groups in one package..."
                 "(CPACK_COMPONENTS_ALL_[GROUPS_]IN_ONE_PACKAGE is set)"

+ 14 - 0
Tests/RunCMake/CPack/MAIN_COMPONENT.cmake

@@ -0,0 +1,14 @@
+set(CPACK_RPM_COMPONENT_INSTALL "ON")
+
+install(FILES CMakeLists.txt DESTINATION foo COMPONENT applications)
+install(FILES CMakeLists.txt DESTINATION bar COMPONENT headers)
+install(FILES CMakeLists.txt DESTINATION bas COMPONENT libs)
+
+if(RunCMake_SUBTEST_SUFFIX STREQUAL "invalid")
+  set(CPACK_RPM_MAIN_COMPONENT "")
+else()
+  set(CPACK_RPM_MAIN_COMPONENT "applications")
+  set(CPACK_RPM_APPLICATIONS_FILE_NAME "RPM-DEFAULT")
+endif()
+
+set(CPACK_PACKAGE_NAME "main_component")

+ 13 - 0
Tests/RunCMake/CPack/RPM/MAIN_COMPONENT-ExpectedFiles.cmake

@@ -0,0 +1,13 @@
+set(whitespaces_ "[\t\n\r ]*")
+
+set(EXPECTED_FILES_COUNT "0")
+
+if(NOT RunCMake_SUBTEST_SUFFIX STREQUAL "invalid")
+  set(EXPECTED_FILES_COUNT "3")
+  set(EXPECTED_FILE_1 "main_component-0.1.1-1.*.rpm")
+  set(EXPECTED_FILE_CONTENT_1 "^/usr/foo${whitespaces_}/usr/foo/CMakeLists.txt$")
+  set(EXPECTED_FILE_2 "main_component*-headers.rpm")
+  set(EXPECTED_FILE_CONTENT_2 "^/usr/bar${whitespaces_}/usr/bar/CMakeLists.txt$")
+  set(EXPECTED_FILE_3 "main_component*-libs.rpm")
+  set(EXPECTED_FILE_CONTENT_3 "^/usr/bas${whitespaces_}/usr/bas/CMakeLists.txt$")
+endif()

+ 3 - 0
Tests/RunCMake/CPack/RPM/MAIN_COMPONENT-found-stderr.txt

@@ -0,0 +1,3 @@
+^CPackRPM: Will use GENERATED spec file: .*/Tests/RunCMake/RPM/CPack/MAIN_COMPONENT-build-found-subtest/_CPack_Packages/.*/RPM/SPECS/main_component-headers.spec
+CPackRPM: Will use GENERATED spec file: .*/Tests/RunCMake/RPM/CPack/MAIN_COMPONENT-build-found-subtest/_CPack_Packages/.*/RPM/SPECS/main_component-libs.spec
+CPackRPM: Will use GENERATED spec file: .*/Tests/RunCMake/RPM/CPack/MAIN_COMPONENT-build-found-subtest/_CPack_Packages/.*/RPM/SPECS/main_component.spec$

+ 1 - 0
Tests/RunCMake/CPack/RPM/MAIN_COMPONENT-invalid-stderr.txt

@@ -0,0 +1 @@
+CPACK_RPM_MAIN_COMPONENT set to non existing component.

+ 30 - 0
Tests/RunCMake/CPack/RPM/SINGLE_DEBUGINFO-ExpectedFiles.cmake

@@ -0,0 +1,30 @@
+set(whitespaces_ "[\t\n\r ]*")
+
+set(EXPECTED_FILES_COUNT "0")
+
+if(RunCMake_SUBTEST_SUFFIX STREQUAL "valid" OR RunCMake_SUBTEST_SUFFIX STREQUAL "no_debuginfo")
+  set(EXPECTED_FILES_COUNT "4")
+  set(EXPECTED_FILE_1 "single_debuginfo-0.1.1-1.*.rpm")
+  set(EXPECTED_FILE_CONTENT_1 "^/usr/foo${whitespaces_}/usr/foo/test_prog$")
+  set(EXPECTED_FILE_2 "single_debuginfo*-headers.rpm")
+  set(EXPECTED_FILE_CONTENT_2 "^/usr/bar${whitespaces_}/usr/bar/CMakeLists.txt$")
+  set(EXPECTED_FILE_3 "single_debuginfo*-libs.rpm")
+  set(EXPECTED_FILE_CONTENT_3 "^/usr/bas${whitespaces_}/usr/bas/libtest_lib.so$")
+
+  set(EXPECTED_FILE_4 "single_debuginfo-debuginfo*.rpm")
+  set(EXPECTED_FILE_CONTENT_4 ".*/src${whitespaces_}/src/src_1${whitespaces_}/src/src_1/main.cpp${whitespaces_}/src/src_1/test_lib.cpp.*")
+elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "one_component" OR RunCMake_SUBTEST_SUFFIX STREQUAL "one_component_no_debuginfo")
+  set(EXPECTED_FILES_COUNT "2")
+  set(EXPECTED_FILE_1 "single_debuginfo-0*-applications.rpm")
+  set(EXPECTED_FILE_CONTENT_1 "^/usr/foo${whitespaces_}/usr/foo/test_prog$")
+
+  set(EXPECTED_FILE_2 "single_debuginfo-applications-debuginfo*.rpm")
+  set(EXPECTED_FILE_CONTENT_2 ".*/src${whitespaces_}/src/src_1${whitespaces_}/src/src_1/main.cpp.*")
+elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "one_component_main" OR RunCMake_SUBTEST_SUFFIX STREQUAL "no_components")
+  set(EXPECTED_FILES_COUNT "2")
+  set(EXPECTED_FILE_1 "single_debuginfo-0*.rpm")
+  set(EXPECTED_FILE_CONTENT_1 "^/usr/foo${whitespaces_}/usr/foo/test_prog$")
+
+  set(EXPECTED_FILE_2 "single_debuginfo-debuginfo*.rpm")
+  set(EXPECTED_FILE_CONTENT_2 ".*/src${whitespaces_}/src/src_1${whitespaces_}/src/src_1/main.cpp.*")
+endif()

+ 1 - 0
Tests/RunCMake/CPack/RPM/SINGLE_DEBUGINFO-no_components-stderr.txt

@@ -0,0 +1 @@
+^CPackRPM: Will use GENERATED spec file: .*/Tests/RunCMake/RPM/CPack/SINGLE_DEBUGINFO-build-no_components-subtest/_CPack_Packages/.*/RPM/SPECS/single_debuginfo.spec$

+ 3 - 0
Tests/RunCMake/CPack/RPM/SINGLE_DEBUGINFO-no_debuginfo-stderr.txt

@@ -0,0 +1,3 @@
+^CPackRPM: Will use GENERATED spec file: .*/Tests/RunCMake/RPM/CPack/SINGLE_DEBUGINFO-build-no_debuginfo-subtest/_CPack_Packages/.*/RPM/SPECS/single_debuginfo-headers.spec
+CPackRPM: Will use GENERATED spec file: .*/Tests/RunCMake/RPM/CPack/SINGLE_DEBUGINFO-build-no_debuginfo-subtest/_CPack_Packages/.*/RPM/SPECS/single_debuginfo-libs.spec
+CPackRPM: Will use GENERATED spec file: .*/Tests/RunCMake/RPM/CPack/SINGLE_DEBUGINFO-build-no_debuginfo-subtest/_CPack_Packages/.*/RPM/SPECS/single_debuginfo.spec$

+ 1 - 0
Tests/RunCMake/CPack/RPM/SINGLE_DEBUGINFO-no_main_component-stderr.txt

@@ -0,0 +1 @@
+CPack Error: CPACK_RPM_MAIN_COMPONENT not set but it is mandatory with CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE being set.

+ 1 - 0
Tests/RunCMake/CPack/RPM/SINGLE_DEBUGINFO-one_component-stderr.txt

@@ -0,0 +1 @@
+^CPackRPM: Will use GENERATED spec file: .*/Tests/RunCMake/RPM/CPack/SINGLE_DEBUGINFO-build-one_component-subtest/_CPack_Packages/.*/RPM/SPECS/single_debuginfo-applications.spec$

+ 1 - 0
Tests/RunCMake/CPack/RPM/SINGLE_DEBUGINFO-one_component_main-stderr.txt

@@ -0,0 +1 @@
+^CPackRPM: Will use GENERATED spec file: .*/Tests/RunCMake/RPM/CPack/SINGLE_DEBUGINFO-build-one_component_main-subtest/_CPack_Packages/.*/RPM/SPECS/single_debuginfo.spec$

+ 1 - 0
Tests/RunCMake/CPack/RPM/SINGLE_DEBUGINFO-one_component_no_debuginfo-stderr.txt

@@ -0,0 +1 @@
+^CPackRPM: Will use GENERATED spec file: .*/Tests/RunCMake/RPM/CPack/SINGLE_DEBUGINFO-build-one_component_no_debuginfo-subtest/_CPack_Packages/.*/RPM/SPECS/single_debuginfo-applications.spec$

+ 3 - 0
Tests/RunCMake/CPack/RPM/SINGLE_DEBUGINFO-valid-stderr.txt

@@ -0,0 +1,3 @@
+^CPackRPM: Will use GENERATED spec file: .*/Tests/RunCMake/RPM/CPack/SINGLE_DEBUGINFO-build-valid-subtest/_CPack_Packages/.*/RPM/SPECS/single_debuginfo-headers.spec
+CPackRPM: Will use GENERATED spec file: .*/Tests/RunCMake/RPM/CPack/SINGLE_DEBUGINFO-build-valid-subtest/_CPack_Packages/.*/RPM/SPECS/single_debuginfo-libs.spec
+CPackRPM: Will use GENERATED spec file: .*/Tests/RunCMake/RPM/CPack/SINGLE_DEBUGINFO-build-valid-subtest/_CPack_Packages/.*/RPM/SPECS/single_debuginfo.spec$

+ 2 - 0
Tests/RunCMake/CPack/RunCMakeTest.cmake

@@ -12,11 +12,13 @@ run_cpack_test(DEPENDENCIES "RPM;DEB" true)
 run_cpack_test(EMPTY_DIR "RPM;DEB;TGZ" true)
 run_cpack_test(COMPONENTS_EMPTY_DIR "RPM;DEB;TGZ" true)
 run_cpack_test(CUSTOM_NAMES "RPM;DEB" true)
+run_cpack_test_subtests(MAIN_COMPONENT "invalid;found" "RPM" false)
 run_cpack_test(PER_COMPONENT_FIELDS "RPM;DEB" false)
 run_cpack_test(RPM_DIST "RPM" false)
 run_cpack_test(INSTALL_SCRIPTS "RPM" false)
 run_cpack_test(DEB_GENERATE_SHLIBS "DEB" true)
 run_cpack_test(DEB_GENERATE_SHLIBS_LDCONFIG "DEB" true)
 run_cpack_test(DEBUGINFO "RPM" true)
+run_cpack_test_subtests(SINGLE_DEBUGINFO "no_main_component;one_component;one_component_main;no_debuginfo;one_component_no_debuginfo;no_components;valid" "RPM" true)
 run_cpack_test(LONG_FILENAMES "DEB" false)
 run_cpack_test_subtests(PACKAGE_CHECKSUM "invalid;MD5;SHA1;SHA224;SHA256;SHA384;SHA512" "TGZ" false)

+ 56 - 0
Tests/RunCMake/CPack/SINGLE_DEBUGINFO.cmake

@@ -0,0 +1,56 @@
+set(CMAKE_BUILD_WITH_INSTALL_RPATH 1)
+
+# PGI compiler doesn't add build id to binaries by default
+if(CMAKE_CXX_COMPILER_ID STREQUAL "PGI")
+  string(APPEND CMAKE_EXE_LINKER_FLAGS "-Wl,--build-id")
+  string(APPEND CMAKE_SHARED_LINKER_FLAGS "-Wl,--build-id")
+endif()
+
+if(NOT RunCMake_SUBTEST_SUFFIX STREQUAL "no_components")
+  set(CPACK_RPM_COMPONENT_INSTALL "ON")
+endif()
+
+set(CMAKE_BUILD_TYPE Debug)
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test_lib.hpp"
+    "int test_lib();\n")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test_lib.cpp"
+    "#include \"test_lib.hpp\"\nint test_lib() {return 0;}\n")
+add_library(test_lib SHARED "${CMAKE_CURRENT_BINARY_DIR}/test_lib.cpp")
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/main.cpp"
+    "#include \"test_lib.hpp\"\nint main() {return test_lib();}\n")
+add_executable(test_prog "${CMAKE_CURRENT_BINARY_DIR}/main.cpp")
+target_link_libraries(test_prog test_lib)
+
+install(TARGETS test_prog DESTINATION foo COMPONENT applications)
+
+if(RunCMake_SUBTEST_SUFFIX STREQUAL "valid"
+  OR RunCMake_SUBTEST_SUFFIX STREQUAL "no_main_component"
+  OR RunCMake_SUBTEST_SUFFIX STREQUAL "no_debuginfo")
+  install(FILES CMakeLists.txt DESTINATION bar COMPONENT headers)
+  install(TARGETS test_lib DESTINATION bas COMPONENT libs)
+elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "one_component"
+  OR RunCMake_SUBTEST_SUFFIX STREQUAL "one_component_no_debuginfo")
+  set(CPACK_COMPONENTS_ALL applications)
+endif()
+
+set(CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE ON)
+
+if(RunCMake_SUBTEST_SUFFIX STREQUAL "valid"
+  OR RunCMake_SUBTEST_SUFFIX STREQUAL "one_component_main"
+  OR RunCMake_SUBTEST_SUFFIX STREQUAL "no_debuginfo")
+  set(CPACK_RPM_MAIN_COMPONENT "applications")
+  set(CPACK_RPM_APPLICATIONS_FILE_NAME "RPM-DEFAULT")
+endif()
+
+if(RunCMake_SUBTEST_SUFFIX STREQUAL "valid"
+  OR RunCMake_SUBTEST_SUFFIX STREQUAL "no_main_component"
+  OR RunCMake_SUBTEST_SUFFIX STREQUAL "one_component")
+  set(CPACK_RPM_APPLICATIONS_DEBUGINFO_PACKAGE ON)
+  set(CPACK_RPM_LIBS_DEBUGINFO_PACKAGE ON)
+endif()
+
+set(CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX "/src")
+
+set(CPACK_PACKAGE_NAME "single_debuginfo")