Browse Source

Merge topic 'cpack-ifw-product-images'

5b8fa21512 Tests: Add a test covering the CPack IFW Generator
3331c7032f CPack/IFW: Add option for ProductImages URLs
d58f90f628 CPack/IFW: Actually ignore missing ProductImages
3c214f2638 CPack/IFW: Fix generation of ProductImages

Acked-by: Kitware Robot <[email protected]>
Merge-request: !9798
Brad King 1 year ago
parent
commit
2321b88edf

+ 10 - 0
Help/cpack_gen/ifw.rst

@@ -275,6 +275,16 @@ Package
 
  This feature is available for QtIFW 4.0.0 and later.
 
+.. variable:: CPACK_IFW_PACKAGE_PRODUCT_IMAGE_URLS
+
+ .. versionadded:: 3.31
+
+ A list of URLs associated with the ProductImages.
+ Only used if  ``CPACK_IFW_PACKAGE_PRODUCT_IMAGES`` is defined
+ and it has the same size.
+
+ This feature is available for QtIFW 4.0.0 and later.
+
 .. variable:: CPACK_IFW_PACKAGE_RUN_PROGRAM
 
  .. versionadded:: 3.23

+ 8 - 0
Help/release/dev/cpack-ifw-product-images.rst

@@ -0,0 +1,8 @@
+cpack-ifw-product-images
+------------------------
+
+* The :cpack_gen:`CPack IFW Generator` gained the new
+  :variable:`CPACK_IFW_PACKAGE_PRODUCT_IMAGE_URLS` variable to
+  specify images associated with entries of
+  :variable:`CPACK_IFW_PACKAGE_PRODUCT_IMAGES`.
+  This feature is available for QtIFW 4.0 and newer.

+ 38 - 5
Source/CPack/IFW/cmCPackIFWInstaller.cxx

@@ -2,6 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackIFWInstaller.h"
 
+#include <algorithm>
 #include <cstddef>
 #include <sstream>
 #include <utility>
@@ -306,12 +307,37 @@ void cmCPackIFWInstaller::ConfigureFromOptions()
         this->GetOption("CPACK_IFW_PACKAGE_PRODUCT_IMAGES")) {
     this->ProductImages.clear();
     cmExpandList(productImages, this->ProductImages);
-    for (const auto& file : this->ProductImages) {
+
+    auto erase_missing_file_pred = [this](const std::string& file) -> bool {
       if (!cmSystemTools::FileExists(file)) {
-        // The warning will say skipped, but there will later be a hard error
-        // when the binarycreator tool tries to read the missing file.
         this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_PRODUCT_IMAGES",
                                         file);
+        return true;
+      }
+      return false;
+    };
+
+    this->ProductImages.erase(std::remove_if(this->ProductImages.begin(),
+                                             this->ProductImages.end(),
+                                             erase_missing_file_pred),
+                              this->ProductImages.end());
+  }
+
+  if (!this->ProductImages.empty()) {
+    if (cmValue productUrls =
+          this->GetOption("CPACK_IFW_PACKAGE_PRODUCT_IMAGE_URLS")) {
+      this->ProductImageUrls.clear();
+      cmExpandList(productUrls, this->ProductImageUrls);
+      if (this->ProductImageUrls.size() != this->ProductImages.size()) {
+        cmCPackIFWLogger(
+          WARNING,
+          "Option \"CPACK_IFW_PACKAGE_PRODUCT_IMAGE_URLS\" will be skipped "
+          "because it contains "
+            << this->ProductImageUrls.size()
+            << " elements while \"CPACK_IFW_PACKAGE_PRODUCT_IMAGES\" "
+               "contains "
+            << this->ProductImages.size() << " elements." << std::endl);
+        this->ProductImageUrls.clear();
       }
     }
   }
@@ -614,13 +640,20 @@ void cmCPackIFWInstaller::GenerateInstallerFile()
   // Product images (copy to config dir)
   if (!this->IsVersionLess("4.0") && !this->ProductImages.empty()) {
     xout.StartElement("ProductImages");
-    for (auto const& srcImg : this->ProductImages) {
+    const bool hasProductImageUrl = !this->ProductImageUrls.empty();
+    for (size_t i = 0; i < this->ProductImages.size(); ++i) {
+      xout.StartElement("ProductImage");
+      auto const& srcImg = this->ProductImages[i];
       std::string name = cmSystemTools::GetFilenameName(srcImg);
       std::string dstImg = this->Directory + "/config/" + name;
       cmsys::SystemTools::CopyFileIfDifferent(srcImg, dstImg);
       xout.Element("Image", name);
+      if (hasProductImageUrl) {
+        xout.Element("Url", this->ProductImageUrls.at(i));
+      }
+      xout.EndElement(); // </ProductImage>
     }
-    xout.EndElement();
+    xout.EndElement(); // </ProductImages>
   }
 
   // Resources (copy to resources dir)

+ 4 - 0
Source/CPack/IFW/cmCPackIFWInstaller.h

@@ -124,6 +124,10 @@ public:
   /// A list of images to be shown on PerformInstallationPage.
   std::vector<std::string> ProductImages;
 
+  /// A list of associated URLs linked to images to be shown on
+  /// PerformInstallationPage.
+  std::vector<std::string> ProductImageUrls;
+
   /// Command executed after the installer is done if the user accepts the
   /// action
   std::string RunProgram;

+ 26 - 0
Tests/CMakeLists.txt

@@ -1009,6 +1009,32 @@ if(BUILD_TESTING)
       "${CMake_BINARY_DIR}/Tests/CPackNSISGenerator/_CPack_Packages/win32/NSIS/NSISOutput.log")
   endif()
 
+  find_program(IFW_BINARYCREATOR_EXECUTABLE NAMES binarycreator
+    DOC "IFW binarycreator program location"
+  )
+
+  if(IFW_BINARYCREATOR_EXECUTABLE)
+    add_test(CPackIFWGenerator ${CMAKE_CTEST_COMMAND}
+      -C \${CTEST_CONFIGURATION_TYPE}
+      --build-and-test
+      "${CMake_SOURCE_DIR}/Tests/CPackIFWGenerator"
+      "${CMake_BINARY_DIR}/Tests/CPackIFWGenerator"
+      ${build_generator_args}
+      --build-project CPackIFWGenerator
+      --build-options
+      --test-command ${CMAKE_CMAKE_COMMAND}
+      "-DCPackIFWGenerator_BINARY_DIR:PATH=${CMake_BINARY_DIR}/Tests/CPackIFWGenerator"
+      "-Dconfig=\${CTEST_CONFIGURATION_TYPE}"
+      -P "${CMake_SOURCE_DIR}/Tests/CPackIFWGenerator/RunCPackVerifyResult.cmake")
+
+    set_property(TEST CPackIFWGenerator PROPERTY
+      ATTACHED_FILES_ON_FAIL
+      "${CMake_BINARY_DIR}/Tests/CPackIFWGenerator/_CPack_Packages/Linux/IFW/IFWOutput.log"
+      "${CMake_BINARY_DIR}/Tests/CPackIFWGenerator/_CPack_Packages/Darwin/IFW/IFWOutput.log"
+      "${CMake_BINARY_DIR}/Tests/CPackIFWGenerator/_CPack_Packages/win32/IFW/IFWOutput.log"
+    )
+  endif()
+
   if(CTEST_TEST_CPACK)
     add_test(CPackUseDefaultVersion ${CMAKE_CTEST_COMMAND}
       --build-and-test

BIN
Tests/CPackIFWGenerator/ApplicationIcon.png


BIN
Tests/CPackIFWGenerator/BundleIcon.icns


+ 54 - 0
Tests/CPackIFWGenerator/CMakeLists.txt

@@ -0,0 +1,54 @@
+cmake_minimum_required(VERSION 3.16)
+
+project(CPackIFWGenerator)
+
+add_executable(hello main.cpp)
+
+install(TARGETS hello
+  ARCHIVE DESTINATION .
+  RUNTIME DESTINATION .
+  LIBRARY DESTINATION .
+  BUNDLE DESTINATION .)
+
+# Component that is a reserved name on Windows.
+# See https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
+install(
+  DIRECTORY .
+  DESTINATION txt
+  COMPONENT CON
+  FILES_MATCHING PATTERN *.txt)
+# Component name that is similar to a reserved name on Windows.
+install(
+  DIRECTORY .
+  DESTINATION txt
+  COMPONENT Console
+  FILES_MATCHING PATTERN *.txt)
+# Component name that is strongly discouraged on Windows.
+install(
+  DIRECTORY .
+  DESTINATION txt
+  COMPONENT EndsWithDot.
+  FILES_MATCHING PATTERN *.txt)
+
+set(CPACK_IFW_PRODUCT_URL "https://cmake.org/")
+if(WIN32)
+  set(CPACK_IFW_PACKAGE_ICON "${PROJECT_SOURCE_DIR}/install.ico")
+else()
+  set(CPACK_IFW_PACKAGE_ICON "${PROJECT_SOURCE_DIR}/BundleIcon.icns")
+endif()
+
+set(CPACK_IFW_PACKAGE_WINDOW_ICON "${PROJECT_SOURCE_DIR}/install.ico")
+set(CPACK_GENERATOR "IFW")
+
+set(CPACK_IFW_PACKAGE_PRODUCT_IMAGES
+  "${PROJECT_SOURCE_DIR}/ApplicationIcon.png"
+  "${PROJECT_SOURCE_DIR}/SplashScreen.png"
+)
+
+set(CPACK_IFW_PACKAGE_PRODUCT_IMAGE_URLS
+  "https://www.ApplicationIcon.org"
+  "https://www.SplashScreen.org"
+)
+
+include(CPack)
+include(CPackIFW)

+ 93 - 0
Tests/CPackIFWGenerator/RunCPackVerifyResult.cmake

@@ -0,0 +1,93 @@
+message(STATUS "=============================================================")
+message(STATUS "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)")
+message(STATUS "")
+
+if(NOT CPackIFWGenerator_BINARY_DIR)
+  message(FATAL_ERROR "CPackIFWGenerator_BINARY_DIR not set")
+endif()
+
+message(STATUS "CMAKE_COMMAND: ${CMAKE_COMMAND}")
+message(STATUS "CMAKE_CPACK_COMMAND: ${CMAKE_CPACK_COMMAND}")
+message(STATUS "CPackIFWGenerator_BINARY_DIR: ${CPackIFWGenerator_BINARY_DIR}")
+
+if(config)
+  set(_C_config -C ${config})
+endif()
+
+execute_process(COMMAND "${CMAKE_CPACK_COMMAND}"
+                        ${_C_config}
+  RESULT_VARIABLE CPack_result
+  OUTPUT_VARIABLE CPack_output
+  ERROR_VARIABLE CPack_error
+  WORKING_DIRECTORY "${CPackIFWGenerator_BINARY_DIR}")
+
+if(CPack_result)
+  message(FATAL_ERROR "CPack execution went wrong!, CPack_output=${CPack_output}, CPack_error=${CPack_error}")
+else ()
+  message(STATUS "CPack_output=${CPack_output}")
+endif()
+
+set(expected_file_mask "${CPackIFWGenerator_BINARY_DIR}/_CPack_Packages/*/IFW/*/config/config.xml")
+file(GLOB project_file "${expected_file_mask}")
+
+message(STATUS "project_file='${project_file}'")
+message(STATUS "expected_file_mask='${expected_file_mask}'")
+
+if(NOT project_file)
+  message(FATAL_ERROR "project_file does not exist.")
+endif()
+
+# should match !define MUI_HEADERIMAGE_BITMAP "${PROJECT_SOURCE_DIR}\header-image.bmp"
+file(STRINGS "${project_file}" lines)
+
+file(STRINGS "${project_file}" tag_start REGEX [[<ProductImage>]])
+file(STRINGS "${project_file}" tag_end REGEX [[</ProductImage>]])
+list(LENGTH tag_start tag_start_size)
+list(LENGTH tag_end tag_end_size)
+
+if(NOT tag_start_size EQUAL 2)
+  message(FATAL_ERROR "Expected to have 2 <ProductImage> not ${tag_start_size}")
+endif()
+if(NOT tag_end_size EQUAL 2)
+  message(FATAL_ERROR "Expected to have 2 </ProductImage> not ${tag_end_size}")
+endif()
+
+file(STRINGS "${project_file}" tag_start REGEX [[<Image>]])
+file(STRINGS "${project_file}" tag_end REGEX [[</Image>]])
+list(LENGTH tag_start tag_start_size)
+list(LENGTH tag_end tag_end_size)
+
+if(NOT tag_start_size EQUAL 2)
+  message(FATAL_ERROR "Expected to have 2 <Image> not ${tag_start_size}")
+endif()
+if(NOT tag_end_size EQUAL 2)
+  message(FATAL_ERROR "Expected to have 2 </Image> not ${tag_end_size}")
+endif()
+
+file(STRINGS "${project_file}" tag_start REGEX [[<Url>]])
+file(STRINGS "${project_file}" tag_end REGEX [[</Url>]])
+list(LENGTH tag_start tag_start_size)
+list(LENGTH tag_end tag_end_size)
+
+if(NOT tag_start_size EQUAL 2)
+  message(FATAL_ERROR "Expected to have 2 <Url> not ${tag_start_size}")
+endif()
+if(NOT tag_end_size EQUAL 2)
+  message(FATAL_ERROR "Expected to have 2 </Url> not ${tag_end_size}")
+endif()
+
+set(TO_SEARCHES
+  "<ProductImages>"
+  "<Image>ApplicationIcon.png</Image>"
+  "<Url>https://www.ApplicationIcon.org</Url>"
+  "<Image>SplashScreen.png</Image>"
+  "<Url>https://www.SplashScreen.org</Url>"
+  "</ProductImages>"
+)
+foreach(TO_SEARCH ${TO_SEARCHES})
+  string(FIND "${lines}" "${TO_SEARCH}" output_index)
+  message(STATUS "Found the ${TO_SEARCH} at index ${output_index}")
+  if("${output_index}" EQUAL "-1")
+    message(FATAL_ERROR "${TO_SEARCH} not found in the project")
+  endif()
+endforeach()

BIN
Tests/CPackIFWGenerator/SplashScreen.png


BIN
Tests/CPackIFWGenerator/install.ico


+ 4 - 0
Tests/CPackIFWGenerator/main.cpp

@@ -0,0 +1,4 @@
+int main()
+{
+  return 42;
+}