[cmake-developers] [CMake 0011944]: CPackDeb: Support dependencies between components/Debian packages

Raffi Enficiaud raffi.enficiaud at mines-paris.org
Fri Apr 17 09:50:41 EDT 2015


Le 16/04/15 22:31, Brad King a écrit :
> On 04/16/2015 04:19 PM, Domen Vrankar wrote:
>> I've pushed the patch with minor changes to next.
>> http://www.cmake.org/gitweb?p=cmake.git;h=0779b679
>
> Thanks.  The "fixup! " mark is useful only during incremental
> development of an open topic.  Once a commit is in 'master'
> then it should be explicitly referenced by a later fixup commit
> with a normal commit message.  I've revised the commit as:
>
>   CPack: Fix single component packaging
>   http://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=ed0b0630
>
> I also rebased it back on the original commit and renamed the
> topic to match.  This makes it look like one topic that was
> merged to master in the middle.
>
> -Brad
>

Ok, here is a patch that shows you the advance. This is not yet a 
candidate for merging to anything, but rather a support for some 
discussions.
It is based on ed0b063 for this particular topic.

I added the following functionalities:
- set the permissions of the md5sum to RW-R--R--, because lintian complains
- added an option to set the shlibdeps per component
- added an option to set the dependencies per component
- added an option to set the description per component
- enforcing the lower case policy of Debian for package names (in the 
file, due to the comment "# debian policy enforce lower case for package 
name")

On the other hand, I started writing tests based on the one existing for 
the CPackComponentsAll, but specific to debian packages:
- added a function for running lintian and checking for some errors. The 
md5sum permission change is now covered by that
- added a function for running dpkg-deb and extracting a particular 
field of the package metadata. No change I made are currently covered by 
this function, this is what I will do next.
- Having one specific check file per configuration, sharing a common 
.cmake providing the check functions (linitian and dpkg-deb). This would 
prevent the cluttering of the checks that we can observe in the 
CPackComponentsAll final test.


Of course, your comments are more than welcome.

Best,
Raffi

-------------- next part --------------
>From 2681e05c844eee5b77568895b5b1329f1377b05b Mon Sep 17 00:00:00 2001
From: Raffi Enficiaud <raffi.enficiaud at mines-paris.org>
Date: Fri, 17 Apr 2015 15:49:33 +0200
Subject: [PATCH] Debian packaging

- enabling a per component shlibdeps
- enabling a per component dependencies
- enabling a per component description
- fixing the file permissions of the auto-generated md5sum
- new tests specific to CPackDeb.cmake with lintian and dpkg-deb
---
 Modules/CPackDeb.cmake                             |  89 +++++++--
 Source/CPack/cmCPackDebGenerator.cxx               |   8 +
 Tests/CMakeLists.txt                               |  35 ++++
 Tests/CPackComponentsDEB/CMakeLists.txt            | 103 ++++++++++
 ...Config-one-file-per-component-no-group.cmake.in |  15 ++
 ...ifyResult-one-file-per-component-no-group.cmake |  78 ++++++++
 .../CPackComponentsDEB/RunCPackVerifyResult.cmake  | 217 +++++++++++++++++++++
 Tests/CPackComponentsDEB/license.txt               |   3 +
 Tests/CPackComponentsDEB/mylib.cpp                 |   7 +
 Tests/CPackComponentsDEB/mylib.h                   |   1 +
 Tests/CPackComponentsDEB/mylibapp.cpp              |   6 +
 11 files changed, 550 insertions(+), 12 deletions(-)
 create mode 100644 Tests/CPackComponentsDEB/CMakeLists.txt
 create mode 100644 Tests/CPackComponentsDEB/MyLibCPackConfig-one-file-per-component-no-group.cmake.in
 create mode 100644 Tests/CPackComponentsDEB/RunCPackVerifyResult-one-file-per-component-no-group.cmake
 create mode 100644 Tests/CPackComponentsDEB/RunCPackVerifyResult.cmake
 create mode 100644 Tests/CPackComponentsDEB/license.txt
 create mode 100644 Tests/CPackComponentsDEB/mylib.cpp
 create mode 100644 Tests/CPackComponentsDEB/mylib.h
 create mode 100644 Tests/CPackComponentsDEB/mylibapp.cpp

diff --git a/Modules/CPackDeb.cmake b/Modules/CPackDeb.cmake
index 8a4fa49..647b2ae 100644
--- a/Modules/CPackDeb.cmake
+++ b/Modules/CPackDeb.cmake
@@ -104,6 +104,15 @@
 #  may fail to find your own shared libs.
 #  See http://www.cmake.org/Wiki/CMake_RPATH_handling.
 #
+# .. variable:: CPACK_DEBIAN_<comp>_PACKAGE_SHLIBDEPS
+#
+#  * Mandatory : NO
+#  * Default   : CPACK_DEBIAN_PACKAGE_SHLIBDEPS
+#
+#  Same as `CPACK_DEBIAN_PACKAGE_SHLIBDEPS` but for one specific component.
+#  If set (either to ON or OFF) it overrides the default given by
+#  `CPACK_DEBIAN_PACKAGE_SHLIBDEPS`.
+#
 # .. variable:: CPACK_DEBIAN_PACKAGE_DEBUG
 #
 #  * Mandatory : NO
@@ -229,12 +238,28 @@ if(NOT DEFINED CPACK_DEBIAN_PACKAGE_SHLIBDEPS)
   set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OFF)
 endif()
 
+set(_cpack_debian_shlibdeps_local ${CPACK_DEBIAN_PACKAGE_SHLIBDEPS})
+
 find_program(FAKEROOT_EXECUTABLE fakeroot)
 if(FAKEROOT_EXECUTABLE)
   set(CPACK_DEBIAN_FAKEROOT_EXECUTABLE ${FAKEROOT_EXECUTABLE})
 endif()
 
-if(CPACK_DEBIAN_PACKAGE_SHLIBDEPS)
+if(CPACK_DEB_PACKAGE_COMPONENT)
+  set(_component_shlibdeps_var CPACK_DEBIAN_${CPACK_DEB_PACKAGE_COMPONENT}_PACKAGE_SHLIBDEPS)
+  
+  # if set, overrides the global configuration
+  if(${_component_shlibdeps_var})
+    set(_cpack_debian_shlibdeps_local ${${_component_shlibdeps_var}})
+    if(CPACK_DEBIAN_PACKAGE_DEBUG)
+      message("CPackDeb Debug: component ${CPACK_DEB_PACKAGE_COMPONENT} dpkg-shlibdeps set to ${_cpack_debian_shlibdeps_local}")
+    endif()
+  endif()
+endif()
+
+
+set(CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS)
+if(_cpack_debian_shlibdeps_local)
   # dpkg-shlibdeps is a Debian utility for generating dependency list
   find_program(SHLIBDEPS_EXECUTABLE dpkg-shlibdeps)
 
@@ -306,13 +331,6 @@ if(CPACK_DEBIAN_PACKAGE_SHLIBDEPS)
     # Might not be safe if package actual contain file or directory named debian
     file(REMOVE_RECURSE "${CPACK_TEMPORARY_DIRECTORY}/debian")
 
-    # Append user depend if set
-    if (CPACK_DEBIAN_PACKAGE_DEPENDS)
-      set (CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}, ${CPACK_DEBIAN_PACKAGE_DEPENDS}")
-    else ()
-      set (CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}")
-    endif ()
-
   else ()
     if(CPACK_DEBIAN_PACKAGE_DEBUG)
       message( "CPackDeb Debug: Using only user-provided depends because dpkg-shlibdeps is not found.")
@@ -325,6 +343,33 @@ else ()
   endif()
 endif()
 
+
+if(CPACK_DEB_PACKAGE_COMPONENT)
+  set(_component_depends_var CPACK_DEBIAN_${CPACK_DEB_PACKAGE_COMPONENT}_PACKAGE_DEPENDS)
+  
+  # if set, overrides the global configuration
+  if(NOT "${${_component_depends_var}}" STREQUAL "")
+    set(CPACK_DEBIAN_PACKAGE_DEPENDS ${${_component_depends_var}})
+    if(CPACK_DEBIAN_PACKAGE_DEBUG)
+      message("CPackDeb Debug: component ${CPACK_DEB_PACKAGE_COMPONENT} dependencies set to ${CPACK_DEBIAN_PACKAGE_DEPENDS}")
+    endif()
+  endif()
+endif()
+
+# Merge with auto depends
+if(_cpack_debian_shlibdeps_local)
+  if (NOT "${CPACK_DEBIAN_PACKAGE_DEPENDS}" STREQUAL "")
+    set (CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}, ${CPACK_DEBIAN_PACKAGE_DEPENDS}")
+  else ()
+    set (CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS}")
+  endif ()
+endif()
+unset(CPACK_DEBIAN_PACKAGE_AUTO_DEPENDS)
+
+
+
+
+
 # Let's define the control file found in debian package:
 
 # Binary package:
@@ -335,6 +380,8 @@ endif()
 # Package: (mandatory)
 if(NOT CPACK_DEBIAN_PACKAGE_NAME)
   string(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_DEBIAN_PACKAGE_NAME)
+else()
+  string(TOLOWER "${CPACK_DEBIAN_PACKAGE_NAME}" CPACK_DEBIAN_PACKAGE_NAME)
 endif()
 
 # Version: (mandatory)
@@ -378,11 +425,29 @@ if(NOT CPACK_DEBIAN_PACKAGE_MAINTAINER)
 endif()
 
 # Description: (mandatory)
-if(NOT CPACK_DEBIAN_PACKAGE_DESCRIPTION)
-  if(NOT CPACK_PACKAGE_DESCRIPTION_SUMMARY)
-    message(FATAL_ERROR "CPackDeb: Debian package requires a summary for a package, set CPACK_PACKAGE_DESCRIPTION_SUMMARY or CPACK_DEBIAN_PACKAGE_DESCRIPTION")
+if(NOT CPACK_DEB_PACKAGE_COMPONENT)
+  if(NOT CPACK_DEBIAN_PACKAGE_DESCRIPTION)
+    if(NOT CPACK_PACKAGE_DESCRIPTION_SUMMARY)
+      message(FATAL_ERROR "CPackDeb: Debian package requires a summary for a package, set CPACK_PACKAGE_DESCRIPTION_SUMMARY or CPACK_DEBIAN_PACKAGE_DESCRIPTION")
+    endif()
+    set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION_SUMMARY})
+  endif()
+else()
+  set(component_description_var CPACK_COMPONENT_${CPACK_DEB_PACKAGE_COMPONENT}_DESCRIPTION)
+  
+  # component description overrides package description
+  if(${component_description_var})
+    set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${${component_description_var}})
+  elseif(CPACK_DEBIAN_PACKAGE_DESCRIPTION)
+    # do nothing
+  else()
+    if(NOT CPACK_PACKAGE_DESCRIPTION_SUMMARY)
+      message(FATAL_ERROR "CPackDeb: Debian package requires a summary for a package, set CPACK_PACKAGE_DESCRIPTION_SUMMARY or CPACK_DEBIAN_PACKAGE_DESCRIPTION
+              or ${component_description_var}")
+    endif()
+    set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION_SUMMARY})
   endif()
-  set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION_SUMMARY})
+    
 endif()
 
 # Section: (recommended)
diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx
index fcf4122..9a5978f 100644
--- a/Source/CPack/cmCPackDebGenerator.cxx
+++ b/Source/CPack/cmCPackDebGenerator.cxx
@@ -20,6 +20,8 @@
 #include <cmsys/Glob.hxx>
 
 #include <limits.h> // USHRT_MAX
+#include <sys/stat.h>
+
 
 // NOTE:
 // A debian package .deb is simply an 'ar' archive. The only subtle difference
@@ -54,6 +56,7 @@ int cmCPackDebGenerator::InitializeInternal()
 int cmCPackDebGenerator::PackageOnePack(std::string initialTopLevel,
                                         std::string packageName)
   {
+
   int retval = 1;
   // Begin the archive for this pack
   std::string localToplevel(initialTopLevel);
@@ -245,6 +248,7 @@ int cmCPackDebGenerator::PackageFiles()
 
   /* Are we in the component packaging case */
   if (WantsComponentInstallation()) {
+
     // CASE 1 : COMPONENT ALL-IN-ONE package
     // If ALL GROUPS or ALL COMPONENTS in ONE package has been requested
     // then the package file is unique and should be open here.
@@ -265,6 +269,7 @@ int cmCPackDebGenerator::PackageFiles()
   // CASE 3 : NON COMPONENT package.
   else
     {
+
     if (!this->ReadListFile("CPackDeb.cmake"))
       {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
@@ -522,6 +527,9 @@ int cmCPackDebGenerator::createDeb()
     // each line contains a eol.
     // Do not end the md5sum file with yet another (invalid)
     }
+    
+    // lintian warning otherwise
+    cmSystemTools::SetPermissions(md5filename.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
 
     cmd = "";
     if (NULL != this->GetOption("CPACK_DEBIAN_FAKEROOT_EXECUTABLE"))
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 1df39aa..c0c7845 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -1004,6 +1004,41 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
         list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/CPackComponentsForAll/build${CPackGen}-${CPackComponentWay}")
       endforeach()
     endforeach()
+
+    # debian specific
+    if(DPKG_EXECUTABLE)
+        set(DEB_TEST_NAMES CPackComponentsDEB)
+        set(DEB_CONFIGURATIONS_TO_TEST "one-file-per-component-no-group")
+        set(CPackGen DEB)
+        set(CPackRun_CPackGen  "-DCPackGen=${CPackGen}")
+
+        foreach(CPackComponentWay ${DEB_CONFIGURATIONS_TO_TEST})
+            set(CPackRun_CPackComponentWay "-DCPackComponentWay=${CPackComponentWay}")
+            add_test(${DEB_TEST_NAMES}-${CPackComponentWay}
+                     ${CMAKE_CTEST_COMMAND} -C \${CTEST_CONFIGURATION_TYPE}
+                     --build-and-test
+                        "${CMake_SOURCE_DIR}/Tests/${DEB_TEST_NAMES}"
+                        "${CMake_BINARY_DIR}/Tests/${DEB_TEST_NAMES}/build${CPackGen}-${CPackComponentWay}"
+                        ${build_generator_args}
+                     --build-project CPackComponentsDEB
+                     --build-options ${build_options}
+                          -DCPACK_GENERATOR:STRING=${CPackGen}
+                          -DCPACK_BINARY_${CPackGen}:BOOL=ON
+                          ${CPackRun_CPackComponentWay}
+                          ${CPackComponentsForAll_BUILD_OPTIONS}
+                     --graphviz=${DEB_TEST_NAMES}.dot
+                     --test-command ${CMAKE_CMAKE_COMMAND}
+                          "-D${DEB_TEST_NAMES}_SOURCE_DIR:PATH=${CMake_SOURCE_DIR}/Tests/${DEB_TEST_NAMES}"
+                          "-D${DEB_TEST_NAMES}_BINARY_DIR:PATH=${CMake_BINARY_DIR}/Tests/${DEB_TEST_NAMES}/build${CPackGen}-${CPackComponentWay}"
+                          "${CPackRun_CPackGen}"
+                          "${CPackRun_CPackComponentWay}"
+                          -P "${CMake_SOURCE_DIR}/Tests/${DEB_TEST_NAMES}/RunCPackVerifyResult-${CPackComponentWay}.cmake")
+              list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/${DEB_TEST_NAMES}/build${CPackGen}-${CPackComponentWay}")
+        endforeach()
+
+
+    endif()
+
   endif()
 
   # By default, turn this test off (because it takes a long time...)
diff --git a/Tests/CPackComponentsDEB/CMakeLists.txt b/Tests/CPackComponentsDEB/CMakeLists.txt
new file mode 100644
index 0000000..0cb3ad0
--- /dev/null
+++ b/Tests/CPackComponentsDEB/CMakeLists.txt
@@ -0,0 +1,103 @@
+# CPack Example: User-selectable Installation Components
+#
+# In this example, we have a simple library (mylib) with an example
+# application (mylibapp). We create a binary installer (a CPack Generator)
+# which supports CPack components.
+#
+# Depending on the CPack generator and on some CPACK_xxx var values
+# the generator may produce a single (NSIS, PackageMaker)
+# or several package files (Archive Generators, RPM, DEB)
+cmake_minimum_required(VERSION 2.8.3.20101130 FATAL_ERROR)
+project(CPackComponentsDEB)
+
+# Use GNUInstallDirs in order to enforce lib64 if needed
+include(GNUInstallDirs)
+
+# Create the mylib library
+add_library(mylib mylib.cpp)
+
+# Create the mylibapp application
+add_executable(mylibapp mylibapp.cpp)
+target_link_libraries(mylibapp mylib)
+
+# Duplicate of mylibapp application
+# which won't be put in any component (?mistake?)
+add_executable(mylibapp2 mylibapp.cpp)
+target_link_libraries(mylibapp2 mylib)
+
+# Create installation targets. Note that we put each kind of file
+# into a different component via COMPONENT. These components will
+# be used to create the installation components.
+install(TARGETS mylib
+  ARCHIVE
+  DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  COMPONENT libraries)
+
+install(TARGETS mylibapp
+  RUNTIME
+  DESTINATION bin
+  COMPONENT applications)
+
+install(FILES mylib.h
+        DESTINATION include
+        COMPONENT headers)
+
+if("${CPACK_GENERATOR}" MATCHES "DEB")
+
+
+endif()
+
+# CPack boilerplate for this project
+set(CPACK_PACKAGE_NAME "MyLib")
+set(CPACK_PACKAGE_CONTACT "None")
+set(CPACK_PACKAGE_VENDOR "CMake.org")
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MyLib - CPack Component Installation Example")
+set(CPACK_PACKAGE_VERSION "1.0.2")
+set(CPACK_PACKAGE_VERSION_MAJOR "1")
+set(CPACK_PACKAGE_VERSION_MINOR "0")
+set(CPACK_PACKAGE_VERSION_PATCH "2")
+set(CPACK_PACKAGE_INSTALL_DIRECTORY "CPack Component Example")
+set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/license.txt)
+
+# Tell CPack all of the components to install. The "ALL"
+# refers to the fact that this is the set of components that
+# will be included when CPack is instructed to put everything
+# into the binary installer (the default behavior).
+set(CPACK_COMPONENTS_ALL applications libraries headers)
+
+# Set the displayed names for each of the components to install.
+# These will be displayed in the list of components inside the installer.
+set(CPACK_COMPONENT_APPLICATIONS_DISPLAY_NAME   "MyLib Application")
+set(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME      "Libraries")
+set(CPACK_COMPONENT_HEADERS_DISPLAY_NAME        "C++ Headers")
+
+# Provide descriptions for each of the components to install.
+# When the user hovers the mouse over the name of a component,
+# the description will be shown in the "Description" box in the
+# installer. If no descriptions are provided, the "Description"
+# box will be removed.
+set(CPACK_COMPONENT_APPLICATIONS_DESCRIPTION
+  "An extremely useful application that makes use of MyLib")
+set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION
+  "Static libraries used to build programs with MyLib")
+set(CPACK_COMPONENT_HEADERS_DESCRIPTION
+  "C/C++ header files for use with MyLib")
+
+# It doesn't make sense to install the headers without the libraries
+# (because you could never use the headers!), so make the headers component
+# depend on the libraries component.
+set(CPACK_COMPONENT_HEADERS_DEPENDS libraries)
+
+# We may use the CPack specific config file in order
+# to tailor CPack behavior on a CPack generator specific way
+# (Behavior would be different for RPM or TGZ or DEB ...)
+if (NOT ("${CPackComponentWay}" STREQUAL "default"))
+  # Setup project specific CPack-time CPack Config file.
+  configure_file(${CPackComponentsDEB_SOURCE_DIR}/MyLibCPackConfig-${CPackComponentWay}.cmake.in
+    ${CPackComponentsDEB_BINARY_DIR}/MyLibCPackConfig-${CPackComponentWay}.cmake
+    @ONLY)
+  set(CPACK_PROJECT_CONFIG_FILE ${CPackComponentsDEB_BINARY_DIR}/MyLibCPackConfig-${CPackComponentWay}.cmake)
+endif ()
+
+# Include CPack to introduce the appropriate targets
+include(CPack)
diff --git a/Tests/CPackComponentsDEB/MyLibCPackConfig-one-file-per-component-no-group.cmake.in b/Tests/CPackComponentsDEB/MyLibCPackConfig-one-file-per-component-no-group.cmake.in
new file mode 100644
index 0000000..e0a9e9d
--- /dev/null
+++ b/Tests/CPackComponentsDEB/MyLibCPackConfig-one-file-per-component-no-group.cmake.in
@@ -0,0 +1,15 @@
+#
+# Activate component packaging
+#
+
+if(CPACK_GENERATOR MATCHES "DEB")
+   set(CPACK_DEB_COMPONENT_INSTALL "ON")
+endif()
+
+#
+# Choose grouping way
+#
+#set(CPACK_COMPONENTS_ALL_GROUPS_IN_ONE_PACKAGE)
+#set(CPACK_COMPONENTS_GROUPING)
+set(CPACK_COMPONENTS_IGNORE_GROUPS 1)
+#set(CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE 1)
diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-one-file-per-component-no-group.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-one-file-per-component-no-group.cmake
new file mode 100644
index 0000000..918fb60
--- /dev/null
+++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-one-file-per-component-no-group.cmake
@@ -0,0 +1,78 @@
+if(NOT CPackComponentsDEB_SOURCE_DIR)
+  message(FATAL_ERROR "CPackComponentsDEB_SOURCE_DIR not set")
+endif()
+
+include(${CPackComponentsDEB_SOURCE_DIR}/RunCPackVerifyResult.cmake)
+
+
+
+# requirements
+
+# debian now produces lower case names
+set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/MyLib-*.deb")
+set(expected_count 3)
+
+
+set(actual_output)
+run_cpack(actual_output
+          CPack_output
+          CPack_error
+          EXPECTED_FILE_MASK "${expected_file_mask}"
+          CONFIG_ARGS ${config_args}
+          CONFIG_VERBOSE ${config_verbose})
+
+message(STATUS "expected_count='${expected_count}'")
+message(STATUS "expected_file_mask='${expected_file_mask}'")
+message(STATUS "actual_output_files='${actual_output}'")
+
+if(NOT actual_output)
+  message(FATAL_ERROR "error: expected_files do not exist: CPackComponentsDEB test fails. (CPack_output=${CPack_output}, CPack_error=${CPack_error}")
+endif()
+
+list(LENGTH actual_output actual_count)
+message(STATUS "actual_count='${actual_count}'")
+if(NOT actual_count EQUAL expected_count)
+  message(FATAL_ERROR "error: expected_count=${expected_count} does not match actual_count=${actual_count}: CPackComponents test fails. (CPack_output=${CPack_output}, CPack_error=${CPack_error})")
+endif()
+
+
+
+# lintian checks
+set(lintian_output_errors_all)
+foreach(_f IN LISTS actual_output)
+  set(STRINGS_TO_AVOID "E:([^\r\n]*)control-file-has-bad-permissions")
+  lintian_check_specific_errors(lintian_output_errors
+                                FILENAME ${_f}
+                                ERROR_REGEX_STRINGS ${STRINGS_TO_AVOID})
+
+  set(lintian_output_errors_all ${lintian_output_errors_all} ${lintian_output_errors})
+
+endforeach()
+if(NOT "${lintian_output_errors_all}" STREQUAL "")
+  message(FATAL_ERROR "Lintian checks failed:\n${lintian_output_errors_all}")
+endif()
+
+
+# dpkg-deb checks
+set(dpkgdeb_output_errors_all)
+foreach(_f IN LISTS actual_output)
+
+  run_dpkgdeb(dpkg_output
+              FILENAME ${_f}
+              )
+
+  dpkgdeb_return_specific_metaentry(dpkgentry
+                                    DPKGDEB_OUTPUT "${dpkg_output}"
+                                    METAENTRY "Maintainer:")
+
+  if(NOT ${dpkgentry} STREQUAL "Nonen")
+    set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all}
+                                  "dpkg-deb: ${_f}: Incorrect value for Maintainer: ${dpkgentry} != None\n")
+  endif()
+
+endforeach()
+
+
+if(NOT "${dpkgdeb_output_errors_all}" STREQUAL "")
+  message(FATAL_ERROR "dpkg-deb checks failed:\n${dpkgdeb_output_errors_all}")
+endif()
diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult.cmake
new file mode 100644
index 0000000..f717a55
--- /dev/null
+++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult.cmake
@@ -0,0 +1,217 @@
+# prevent older policies from interfearing with this script
+cmake_policy(PUSH)
+cmake_policy(VERSION ${CMAKE_VERSION})
+
+
+include(CMakeParseArguments)
+
+message(STATUS "=============================================================================")
+message(STATUS "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)")
+message(STATUS "")
+
+if(NOT CPackComponentsDEB_BINARY_DIR)
+  message(FATAL_ERROR "CPackComponentsDEB_BINARY_DIR not set")
+endif()
+
+if(NOT CPackGen)
+  message(FATAL_ERROR "CPackGen not set")
+endif()
+
+message("CMAKE_CPACK_COMMAND = ${CMAKE_CPACK_COMMAND}")
+if(NOT CMAKE_CPACK_COMMAND)
+  message(FATAL_ERROR "CMAKE_CPACK_COMMAND not set")
+endif()
+
+if(NOT CPackComponentWay)
+  message(FATAL_ERROR "CPackComponentWay not set")
+endif()
+
+set(expected_file_mask "")
+# The usual default behavior is to expect a single file
+# Then some specific generators (Archive, RPM, ...)
+# May produce several numbers of files depending on
+# CPACK_COMPONENT_xxx values
+set(expected_count 1)
+set(config_type $ENV{CMAKE_CONFIG_TYPE})
+set(config_args )
+if(config_type)
+  set(config_args -C ${config_type})
+endif()
+set(config_verbose )
+
+
+
+
+
+# run cpack with some options and returns the list of files generated
+# -output_expected_file: list of files that match the pattern
+function(run_cpack output_expected_file CPack_output_parent CPack_error_parent)
+  set(options )
+  set(oneValueArgs EXPECTED_FILE_MASK CONFIG_VERBOSE)
+  set(multiValueArgs CONFIG_ARGS)
+  cmake_parse_arguments(run_cpack_deb "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
+
+
+  # clean-up previously CPack generated files
+  if(${run_cpack_deb_EXPECTED_FILE_MASK})
+    file(GLOB expected_file "${${run_cpack_deb_EXPECTED_FILE_MASK}}")
+    if (expected_file)
+      file(REMOVE ${expected_file})
+    endif()
+  endif()
+
+  message("config_args = ${run_cpack_deb_CONFIG_ARGS}")
+  message("config_verbose = ${run_cpack_deb_CONFIG_VERBOSE}")
+  execute_process(COMMAND ${CMAKE_CPACK_COMMAND} ${run_cpack_deb_CONFIG_VERBOSE} -G ${CPackGen} ${run_cpack_deb_CONFIG_ARGS}
+      RESULT_VARIABLE CPack_result
+      OUTPUT_VARIABLE CPack_output
+      ERROR_VARIABLE CPack_error
+      WORKING_DIRECTORY ${CPackComponentsDEB_BINARY_DIR})
+
+  set(${CPack_output_parent} ${CPack_output} PARENT_SCOPE)
+  set(${CPack_error_parent}  ${CPack_error} PARENT_SCOPE)
+
+  if (CPack_result)
+    message(FATAL_ERROR "error: CPack execution went wrong!, CPack_output=${CPack_output}, CPack_error=${CPack_error}")
+  else ()
+    message(STATUS "CPack_output=${CPack_output}")
+  endif()
+
+
+  if(run_cpack_deb_EXPECTED_FILE_MASK)
+    file(GLOB _output_expected_file "${run_cpack_deb_EXPECTED_FILE_MASK}")
+    set(${output_expected_file} ${_output_expected_file} PARENT_SCOPE)
+  endif()
+
+endfunction()
+
+
+
+# This function runs lintian on a .deb and returns its output
+function(run_lintian lintian_output)
+
+  set(${lintian_output} "" PARENT_SCOPE)
+
+  find_program(LINTIAN_EXECUTABLE lintian)
+  if(LINTIAN_EXECUTABLE)
+
+    set(options )
+    set(oneValueArgs FILENAME )
+    set(multiValueArgs )
+    cmake_parse_arguments(run_lintian_deb "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
+
+
+    if(NOT run_lintian_deb_FILENAME)
+      message(FATAL_ERROR "error: run_lintian needs FILENAME to be set")
+    endif()
+
+    # run lintian
+    execute_process(COMMAND ${LINTIAN_EXECUTABLE} ${run_lintian_deb_FILENAME}
+      WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}"
+      OUTPUT_VARIABLE LINTIAN_OUTPUT
+      RESULT_VARIABLE LINTIAN_RESULT
+      ERROR_VARIABLE LINTIAN_ERROR
+      OUTPUT_STRIP_TRAILING_WHITESPACE )
+
+    set(${lintian_output} ${LINTIAN_OUTPUT} PARENT_SCOPE)
+  endif()
+
+endfunction()
+
+
+# Higher level lintian check that parse the output for errors and required strings
+function(lintian_check_specific_errors output_errors)
+
+    set(${output_errors} "" PARENT_SCOPE)
+    set(ERROR_ACC)
+
+    set(options )
+    set(oneValueArgs FILENAME )
+    set(multiValueArgs ERROR_REGEX_STRINGS)
+    cmake_parse_arguments(lintian_check_specific_errors_deb "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
+
+    set(lintian_output)
+    run_lintian(lintian_output FILENAME ${lintian_check_specific_errors_deb_FILENAME})
+
+    message(STATUS "Lintian output is ${lintian_output}")
+
+    # regex to avoid
+    foreach(_s IN LISTS lintian_check_specific_errors_deb_ERROR_REGEX_STRINGS)
+
+      if("${_s}" STREQUAL "")
+         continue()
+      endif()
+
+      string(REGEX MATCHALL ${_s} _TMP_CHECK_ERROR "${lintian_output}")
+
+      if(NOT "${_TMP_CHECK_ERROR}" STREQUAL "")
+        set(ERROR_ACC "${ERROR_ACC}\nlintian: ${_f}: output contains an undesirable regex:\n\t${_TMP_CHECK_ERROR}")
+      endif()
+    endforeach()
+
+    set(${output_errors} "${ERROR_ACC}" PARENT_SCOPE)
+
+endfunction()
+
+
+
+
+# This function runs dpkg-deb on a .deb and returns its output
+function(run_dpkgdeb dpkg_deb_output)
+
+  set(${dpkg_deb_output} "" PARENT_SCOPE)
+
+  find_program(DPKGDEB_EXECUTABLE dpkg-deb)
+  if(DPKGDEB_EXECUTABLE)
+
+    set(options )
+    set(oneValueArgs FILENAME )
+    set(multiValueArgs )
+    cmake_parse_arguments(run_dpkgdeb_deb "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
+
+
+    if(NOT run_dpkgdeb_deb_FILENAME)
+      message(FATAL_ERROR "error: run_dpkgdeb needs FILENAME to be set")
+    endif()
+
+    # run lintian
+    execute_process(COMMAND ${DPKGDEB_EXECUTABLE} -I ${run_dpkgdeb_deb_FILENAME}
+      WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}"
+      OUTPUT_VARIABLE DPKGDEB_OUTPUT
+      RESULT_VARIABLE DPKGDEB_RESULT
+      ERROR_VARIABLE DPKGDEB_ERROR
+      OUTPUT_STRIP_TRAILING_WHITESPACE )
+
+    set(${dpkg_deb_output} ${DPKGDEB_OUTPUT} PARENT_SCOPE)
+  endif()
+
+endfunction()
+
+
+# returns a particular line of the metadata of the .deb, for checking
+# a previous call to run_dpkgdeb should provide the DPKGDEB_OUTPUT entry.
+function(dpkgdeb_return_specific_metaentry output)
+
+    set(${output} "" PARENT_SCOPE)
+
+    set(options )
+    set(oneValueArgs DPKGDEB_OUTPUT METAENTRY)
+    set(multiValueArgs)
+    cmake_parse_arguments(dpkgdeb_return_specific_metaentry_deb "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
+
+    if(NOT dpkgdeb_return_specific_metaentry_deb_METAENTRY)
+      message(FATAL_ERROR "error: dpkgdeb_return_specific_metaentry needs METAENTRY to be set")
+    endif()
+
+    string(REGEX MATCH "${dpkgdeb_return_specific_metaentry_deb_METAENTRY}([^\r\n]*)" _TMP_STR "${dpkgdeb_return_specific_metaentry_deb_DPKGDEB_OUTPUT}")
+    #message("################ _TMP_STR = ${CMAKE_MATCH_1} ##################")
+    if(NOT "${CMAKE_MATCH_1}" STREQUAL "")
+      string(STRIP ${CMAKE_MATCH_1} _TMP_STR)
+      set(${output} ${_TMP_STR} PARENT_SCOPE)
+    endif()
+
+
+endfunction()
+
+cmake_policy(POP)
+
diff --git a/Tests/CPackComponentsDEB/license.txt b/Tests/CPackComponentsDEB/license.txt
new file mode 100644
index 0000000..ba8ba48
--- /dev/null
+++ b/Tests/CPackComponentsDEB/license.txt
@@ -0,0 +1,3 @@
+LICENSE
+-------
+This is an installer created using CPack (http://www.cmake.org). No license provided.
diff --git a/Tests/CPackComponentsDEB/mylib.cpp b/Tests/CPackComponentsDEB/mylib.cpp
new file mode 100644
index 0000000..8ddac19
--- /dev/null
+++ b/Tests/CPackComponentsDEB/mylib.cpp
@@ -0,0 +1,7 @@
+#include "mylib.h"
+#include "stdio.h"
+
+void mylib_function()
+{
+  printf("This is mylib");
+}
diff --git a/Tests/CPackComponentsDEB/mylib.h b/Tests/CPackComponentsDEB/mylib.h
new file mode 100644
index 0000000..5d0a822
--- /dev/null
+++ b/Tests/CPackComponentsDEB/mylib.h
@@ -0,0 +1 @@
+void mylib_function();
diff --git a/Tests/CPackComponentsDEB/mylibapp.cpp b/Tests/CPackComponentsDEB/mylibapp.cpp
new file mode 100644
index 0000000..a438ac7
--- /dev/null
+++ b/Tests/CPackComponentsDEB/mylibapp.cpp
@@ -0,0 +1,6 @@
+#include "mylib.h"
+
+int main()
+{
+  mylib_function();
+}
-- 
2.0.1



More information about the cmake-developers mailing list