Imported Upstream version 3.23.2
[platform/upstream/cmake.git] / Source / CPack / IFW / cmCPackIFWInstaller.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmCPackIFWInstaller.h"
4
5 #include <cstddef>
6 #include <sstream>
7 #include <utility>
8
9 #include "cmCPackIFWCommon.h"
10 #include "cmCPackIFWGenerator.h"
11 #include "cmCPackIFWPackage.h"
12 #include "cmCPackIFWRepository.h"
13 #include "cmCPackLog.h" // IWYU pragma: keep
14 #include "cmGeneratedFileStream.h"
15 #include "cmStringAlgorithms.h"
16 #include "cmSystemTools.h"
17 #include "cmValue.h"
18 #include "cmXMLParser.h"
19 #include "cmXMLWriter.h"
20
21 cmCPackIFWInstaller::cmCPackIFWInstaller() = default;
22
23 void cmCPackIFWInstaller::printSkippedOptionWarning(
24   const std::string& optionName, const std::string& optionValue)
25 {
26   cmCPackIFWLogger(
27     WARNING,
28     "Option "
29       << optionName << " contains the value \"" << optionValue
30       << "\" but will be skipped because the specified file does not exist."
31       << std::endl);
32 }
33
34 void cmCPackIFWInstaller::ConfigureFromOptions()
35 {
36   // Name;
37   if (cmValue optIFW_PACKAGE_NAME =
38         this->GetOption("CPACK_IFW_PACKAGE_NAME")) {
39     this->Name = *optIFW_PACKAGE_NAME;
40   } else if (cmValue optPACKAGE_NAME = this->GetOption("CPACK_PACKAGE_NAME")) {
41     this->Name = *optPACKAGE_NAME;
42   } else {
43     this->Name = "Your package";
44   }
45
46   // Title;
47   if (cmValue optIFW_PACKAGE_TITLE =
48         this->GetOption("CPACK_IFW_PACKAGE_TITLE")) {
49     this->Title = *optIFW_PACKAGE_TITLE;
50   } else if (cmValue optPACKAGE_DESCRIPTION_SUMMARY =
51                this->GetOption("CPACK_PACKAGE_DESCRIPTION_SUMMARY")) {
52     this->Title = *optPACKAGE_DESCRIPTION_SUMMARY;
53   } else {
54     this->Title = "Your package description";
55   }
56
57   // Version;
58   if (cmValue option = this->GetOption("CPACK_PACKAGE_VERSION")) {
59     this->Version = *option;
60   } else {
61     this->Version = "1.0.0";
62   }
63
64   // Publisher
65   if (cmValue optIFW_PACKAGE_PUBLISHER =
66         this->GetOption("CPACK_IFW_PACKAGE_PUBLISHER")) {
67     this->Publisher = *optIFW_PACKAGE_PUBLISHER;
68   } else if (cmValue optPACKAGE_VENDOR =
69                this->GetOption("CPACK_PACKAGE_VENDOR")) {
70     this->Publisher = *optPACKAGE_VENDOR;
71   }
72
73   // ProductUrl
74   if (cmValue option = this->GetOption("CPACK_IFW_PRODUCT_URL")) {
75     this->ProductUrl = *option;
76   }
77
78   // ApplicationIcon
79   if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_ICON")) {
80     if (cmSystemTools::FileExists(option)) {
81       this->InstallerApplicationIcon = *option;
82     } else {
83       this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_ICON", option);
84     }
85   }
86
87   // WindowIcon
88   if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_WINDOW_ICON")) {
89     if (cmSystemTools::FileExists(option)) {
90       this->InstallerWindowIcon = *option;
91     } else {
92       this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_WINDOW_ICON", option);
93     }
94   }
95
96   // RemoveTargetDir
97   if (this->IsSetToOff("CPACK_IFW_PACKAGE_REMOVE_TARGET_DIR")) {
98     this->RemoveTargetDir = "false";
99   } else if (this->IsOn("CPACK_IFW_PACKAGE_REMOVE_TARGET_DIR")) {
100     this->RemoveTargetDir = "true";
101   } else {
102     this->RemoveTargetDir.clear();
103   }
104
105   // Logo
106   if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_LOGO")) {
107     if (cmSystemTools::FileExists(option)) {
108       this->Logo = *option;
109     } else {
110       this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_LOGO", option);
111     }
112   }
113
114   // Watermark
115   if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_WATERMARK")) {
116     if (cmSystemTools::FileExists(option)) {
117       this->Watermark = *option;
118     } else {
119       this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_WATERMARK", option);
120     }
121   }
122
123   // Banner
124   if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_BANNER")) {
125     if (cmSystemTools::FileExists(option)) {
126       this->Banner = *option;
127     } else {
128       this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_BANNER", option);
129     }
130   }
131
132   // Background
133   if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_BACKGROUND")) {
134     if (cmSystemTools::FileExists(option)) {
135       this->Background = *option;
136     } else {
137       this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_BACKGROUND", option);
138     }
139   }
140
141   // WizardStyle
142   if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_WIZARD_STYLE")) {
143     // Setting the user value in any case
144     this->WizardStyle = *option;
145     // Check known values
146     if (this->WizardStyle != "Modern" && this->WizardStyle != "Aero" &&
147         this->WizardStyle != "Mac" && this->WizardStyle != "Classic") {
148       cmCPackIFWLogger(
149         WARNING,
150         "Option CPACK_IFW_PACKAGE_WIZARD_STYLE has unknown value \""
151           << option << "\". Expected values are: Modern, Aero, Mac, Classic."
152           << std::endl);
153     }
154   }
155
156   // StyleSheet
157   if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_STYLE_SHEET")) {
158     if (cmSystemTools::FileExists(option)) {
159       this->StyleSheet = *option;
160     } else {
161       this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_STYLE_SHEET", option);
162     }
163   }
164
165   // WizardDefaultWidth
166   if (cmValue option =
167         this->GetOption("CPACK_IFW_PACKAGE_WIZARD_DEFAULT_WIDTH")) {
168     this->WizardDefaultWidth = *option;
169   }
170
171   // WizardDefaultHeight
172   if (cmValue option =
173         this->GetOption("CPACK_IFW_PACKAGE_WIZARD_DEFAULT_HEIGHT")) {
174     this->WizardDefaultHeight = *option;
175   }
176
177   // WizardShowPageList
178   if (cmValue option =
179         this->GetOption("CPACK_IFW_PACKAGE_WIZARD_SHOW_PAGE_LIST")) {
180     if (!this->IsVersionLess("4.0")) {
181       if (this->IsSetToOff("CPACK_IFW_PACKAGE_WIZARD_SHOW_PAGE_LIST")) {
182         this->WizardShowPageList = "false";
183       } else if (this->IsOn("CPACK_IFW_PACKAGE_WIZARD_SHOW_PAGE_LIST")) {
184         this->WizardShowPageList = "true";
185       } else {
186         this->WizardShowPageList.clear();
187       }
188     } else {
189       std::string currentVersionMsg;
190       if (this->Generator) {
191         currentVersionMsg =
192           "QtIFW version " + this->Generator->FrameworkVersion;
193       } else {
194         currentVersionMsg = "an older QtIFW version";
195       }
196       cmCPackIFWLogger(
197         WARNING,
198         "Option CPACK_IFW_PACKAGE_WIZARD_SHOW_PAGE_LIST is set to \""
199           << option
200           << "\", but it is only supported with QtIFW version 4.0 or later. "
201              "It is being ignored because you are using "
202           << currentVersionMsg << std::endl);
203     }
204   }
205
206   // TitleColor
207   if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_TITLE_COLOR")) {
208     this->TitleColor = *option;
209   }
210
211   // Start menu
212   if (cmValue optIFW_START_MENU_DIR =
213         this->GetOption("CPACK_IFW_PACKAGE_START_MENU_DIRECTORY")) {
214     this->StartMenuDir = *optIFW_START_MENU_DIR;
215   } else {
216     this->StartMenuDir = this->Name;
217   }
218
219   // Default target directory for installation
220   if (cmValue optIFW_TARGET_DIRECTORY =
221         this->GetOption("CPACK_IFW_TARGET_DIRECTORY")) {
222     this->TargetDir = *optIFW_TARGET_DIRECTORY;
223   } else if (cmValue optPACKAGE_INSTALL_DIRECTORY =
224                this->GetOption("CPACK_PACKAGE_INSTALL_DIRECTORY")) {
225     this->TargetDir =
226       cmStrCat("@ApplicationsDir@/", optPACKAGE_INSTALL_DIRECTORY);
227   } else {
228     this->TargetDir = "@RootDir@/usr/local";
229   }
230
231   // Default target directory for installation with administrator rights
232   if (cmValue option = this->GetOption("CPACK_IFW_ADMIN_TARGET_DIRECTORY")) {
233     this->AdminTargetDir = *option;
234   }
235
236   // Maintenance tool
237   if (cmValue optIFW_MAINTENANCE_TOOL =
238         this->GetOption("CPACK_IFW_PACKAGE_MAINTENANCE_TOOL_NAME")) {
239     this->MaintenanceToolName = *optIFW_MAINTENANCE_TOOL;
240   }
241
242   // Maintenance tool ini file
243   if (cmValue optIFW_MAINTENANCE_TOOL_INI =
244         this->GetOption("CPACK_IFW_PACKAGE_MAINTENANCE_TOOL_INI_FILE")) {
245     this->MaintenanceToolIniFile = *optIFW_MAINTENANCE_TOOL_INI;
246   }
247
248   // Allow non-ASCII characters
249   if (this->GetOption("CPACK_IFW_PACKAGE_ALLOW_NON_ASCII_CHARACTERS")) {
250     if (this->IsOn("CPACK_IFW_PACKAGE_ALLOW_NON_ASCII_CHARACTERS")) {
251       this->AllowNonAsciiCharacters = "true";
252     } else {
253       this->AllowNonAsciiCharacters = "false";
254     }
255   }
256
257   // DisableCommandLineInterface
258   if (this->GetOption("CPACK_IFW_PACKAGE_DISABLE_COMMAND_LINE_INTERFACE")) {
259     if (this->IsOn("CPACK_IFW_PACKAGE_DISABLE_COMMAND_LINE_INTERFACE")) {
260       this->DisableCommandLineInterface = "true";
261     } else if (this->IsSetToOff(
262                  "CPACK_IFW_PACKAGE_DISABLE_COMMAND_LINE_INTERFACE")) {
263       this->DisableCommandLineInterface = "false";
264     }
265   }
266
267   // Space in path
268   if (this->GetOption("CPACK_IFW_PACKAGE_ALLOW_SPACE_IN_PATH")) {
269     if (this->IsOn("CPACK_IFW_PACKAGE_ALLOW_SPACE_IN_PATH")) {
270       this->AllowSpaceInPath = "true";
271     } else {
272       this->AllowSpaceInPath = "false";
273     }
274   }
275
276   // Control script
277   if (cmValue optIFW_CONTROL_SCRIPT =
278         this->GetOption("CPACK_IFW_PACKAGE_CONTROL_SCRIPT")) {
279     if (!cmSystemTools::FileExists(optIFW_CONTROL_SCRIPT)) {
280       this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_CONTROL_SCRIPT",
281                                       optIFW_CONTROL_SCRIPT);
282     } else {
283       this->ControlScript = *optIFW_CONTROL_SCRIPT;
284     }
285   }
286
287   // Resources
288   if (cmValue optIFW_PACKAGE_RESOURCES =
289         this->GetOption("CPACK_IFW_PACKAGE_RESOURCES")) {
290     this->Resources.clear();
291     cmExpandList(optIFW_PACKAGE_RESOURCES, this->Resources);
292     for (const auto& file : this->Resources) {
293       if (!cmSystemTools::FileExists(file)) {
294         // The warning will say skipped, but there will later be a hard error
295         // when the binarycreator tool tries to read the missing file.
296         this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_RESOURCES", file);
297       }
298     }
299   }
300
301   // ProductImages
302   if (cmValue productImages =
303         this->GetOption("CPACK_IFW_PACKAGE_PRODUCT_IMAGES")) {
304     this->ProductImages.clear();
305     cmExpandList(productImages, this->ProductImages);
306     for (const auto& file : this->ProductImages) {
307       if (!cmSystemTools::FileExists(file)) {
308         // The warning will say skipped, but there will later be a hard error
309         // when the binarycreator tool tries to read the missing file.
310         this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_PRODUCT_IMAGES",
311                                         file);
312       }
313     }
314   }
315
316   // Run program, run program arguments, and run program description
317   if (cmValue program = this->GetOption("CPACK_IFW_PACKAGE_RUN_PROGRAM")) {
318     this->RunProgram = *program;
319   }
320   if (cmValue arguments =
321         this->GetOption("CPACK_IFW_PACKAGE_RUN_PROGRAM_ARGUMENTS")) {
322     this->RunProgramArguments.clear();
323     cmExpandList(arguments, this->RunProgramArguments);
324   }
325   if (cmValue description =
326         this->GetOption("CPACK_IFW_PACKAGE_RUN_PROGRAM_DESCRIPTION")) {
327     this->RunProgramDescription = *description;
328   }
329
330 #ifdef __APPLE__
331   // Code signing identity for signing the generated app bundle
332   if (cmValue id = this->GetOption("CPACK_IFW_PACKAGE_SIGNING_IDENTITY")) {
333     this->SigningIdentity = *id;
334   }
335 #endif
336 }
337
338 /** \class cmCPackIFWResourcesParser
339  * \brief Helper class that parse resources form .qrc (Qt)
340  */
341 class cmCPackIFWResourcesParser : public cmXMLParser
342 {
343 public:
344   explicit cmCPackIFWResourcesParser(cmCPackIFWInstaller* i)
345     : installer(i)
346   {
347     this->path = i->Directory + "/resources";
348   }
349
350   bool ParseResource(size_t r)
351   {
352     this->hasFiles = false;
353     this->hasErrors = false;
354
355     this->basePath =
356       cmSystemTools::GetFilenamePath(this->installer->Resources[r]);
357
358     this->ParseFile(this->installer->Resources[r].data());
359
360     return this->hasFiles && !this->hasErrors;
361   }
362
363   cmCPackIFWInstaller* installer;
364   bool file = false;
365   bool hasFiles = false;
366   bool hasErrors = false;
367   std::string path, basePath;
368
369 protected:
370   void StartElement(const std::string& name, const char** /*atts*/) override
371   {
372     this->file = name == "file";
373     if (this->file) {
374       this->hasFiles = true;
375     }
376   }
377
378   void CharacterDataHandler(const char* data, int length) override
379   {
380     if (this->file) {
381       std::string content(data, data + length);
382       content = cmTrimWhitespace(content);
383       std::string source = this->basePath + "/" + content;
384       std::string destination = this->path + "/" + content;
385       if (!cmSystemTools::CopyFileIfDifferent(source, destination)) {
386         this->hasErrors = true;
387       }
388     }
389   }
390
391   void EndElement(const std::string& /*name*/) override {}
392 };
393
394 void cmCPackIFWInstaller::GenerateInstallerFile()
395 {
396   // Lazy directory initialization
397   if (this->Directory.empty() && this->Generator) {
398     this->Directory = this->Generator->toplevel;
399   }
400
401   // Output stream
402   cmGeneratedFileStream fout(this->Directory + "/config/config.xml");
403   cmXMLWriter xout(fout);
404
405   xout.StartDocument();
406
407   this->WriteGeneratedByToStrim(xout);
408
409   xout.StartElement("Installer");
410
411   xout.Element("Name", this->Name);
412   xout.Element("Version", this->Version);
413   xout.Element("Title", this->Title);
414
415   if (!this->Publisher.empty()) {
416     xout.Element("Publisher", this->Publisher);
417   }
418
419   if (!this->ProductUrl.empty()) {
420     xout.Element("ProductUrl", this->ProductUrl);
421   }
422
423   // Logo
424   if (!this->Logo.empty()) {
425     std::string srcName = cmSystemTools::GetFilenameName(this->Logo);
426     std::string suffix = cmSystemTools::GetFilenameLastExtension(srcName);
427     std::string name = "cm_logo" + suffix;
428     std::string path = this->Directory + "/config/" + name;
429     cmsys::SystemTools::CopyFileIfDifferent(this->Logo, path);
430     xout.Element("Logo", name);
431   }
432
433   // Banner
434   if (!this->Banner.empty()) {
435     std::string name = cmSystemTools::GetFilenameName(this->Banner);
436     std::string path = this->Directory + "/config/" + name;
437     cmsys::SystemTools::CopyFileIfDifferent(this->Banner, path);
438     xout.Element("Banner", name);
439   }
440
441   // Watermark
442   if (!this->Watermark.empty()) {
443     std::string name = cmSystemTools::GetFilenameName(this->Watermark);
444     std::string path = this->Directory + "/config/" + name;
445     cmsys::SystemTools::CopyFileIfDifferent(this->Watermark, path);
446     xout.Element("Watermark", name);
447   }
448
449   // Background
450   if (!this->Background.empty()) {
451     std::string name = cmSystemTools::GetFilenameName(this->Background);
452     std::string path = this->Directory + "/config/" + name;
453     cmsys::SystemTools::CopyFileIfDifferent(this->Background, path);
454     xout.Element("Background", name);
455   }
456
457   // Attributes introduced in QtIFW 1.4.0
458   if (!this->IsVersionLess("1.4")) {
459     // ApplicationIcon
460     if (!this->InstallerApplicationIcon.empty()) {
461       std::string srcName =
462         cmSystemTools::GetFilenameName(this->InstallerApplicationIcon);
463       std::string suffix = cmSystemTools::GetFilenameLastExtension(srcName);
464       std::string name = "cm_appicon" + suffix;
465       std::string path = this->Directory + "/config/" + name;
466       cmsys::SystemTools::CopyFileIfDifferent(this->InstallerApplicationIcon,
467                                               path);
468       // The actual file is looked up by attaching a '.icns' (macOS),
469       // '.ico' (Windows). No functionality on Unix.
470       name = cmSystemTools::GetFilenameWithoutExtension(name);
471       xout.Element("InstallerApplicationIcon", name);
472     }
473
474     // WindowIcon
475     if (!this->InstallerWindowIcon.empty()) {
476       std::string srcName =
477         cmSystemTools::GetFilenameName(this->InstallerWindowIcon);
478       std::string suffix = cmSystemTools::GetFilenameLastExtension(srcName);
479       std::string name = "cm_winicon" + suffix;
480       std::string path = this->Directory + "/config/" + name;
481       cmsys::SystemTools::CopyFileIfDifferent(this->InstallerWindowIcon, path);
482       xout.Element("InstallerWindowIcon", name);
483     }
484   }
485
486   // Attributes introduced in QtIFW 2.0.0
487   if (!this->IsVersionLess("2.0")) {
488     // WizardDefaultWidth
489     if (!this->WizardDefaultWidth.empty()) {
490       xout.Element("WizardDefaultWidth", this->WizardDefaultWidth);
491     }
492
493     // WizardDefaultHeight
494     if (!this->WizardDefaultHeight.empty()) {
495       xout.Element("WizardDefaultHeight", this->WizardDefaultHeight);
496     }
497
498     // Start menu directory
499     if (!this->StartMenuDir.empty()) {
500       xout.Element("StartMenuDir", this->StartMenuDir);
501     }
502
503     // Maintenance tool
504     if (!this->MaintenanceToolName.empty()) {
505       xout.Element("MaintenanceToolName", this->MaintenanceToolName);
506     }
507
508     // Maintenance tool ini file
509     if (!this->MaintenanceToolIniFile.empty()) {
510       xout.Element("MaintenanceToolIniFile", this->MaintenanceToolIniFile);
511     }
512
513     if (!this->AllowNonAsciiCharacters.empty()) {
514       xout.Element("AllowNonAsciiCharacters", this->AllowNonAsciiCharacters);
515     }
516     if (!this->AllowSpaceInPath.empty()) {
517       xout.Element("AllowSpaceInPath", this->AllowSpaceInPath);
518     }
519
520     // Control script (copy to config dir)
521     if (!this->ControlScript.empty()) {
522       std::string name = cmSystemTools::GetFilenameName(this->ControlScript);
523       std::string path = this->Directory + "/config/" + name;
524       cmsys::SystemTools::CopyFileIfDifferent(this->ControlScript, path);
525       xout.Element("ControlScript", name);
526     }
527   } else {
528     // CPack IFW default policy
529     xout.Comment("CPack IFW default policy for QtIFW less 2.0");
530     xout.Element("AllowNonAsciiCharacters", "true");
531     xout.Element("AllowSpaceInPath", "true");
532   }
533
534   // Target dir
535   if (!this->TargetDir.empty()) {
536     xout.Element("TargetDir", this->TargetDir);
537   }
538
539   // Admin target dir
540   if (!this->AdminTargetDir.empty()) {
541     xout.Element("AdminTargetDir", this->AdminTargetDir);
542   }
543
544   // Remote repositories
545   if (!this->RemoteRepositories.empty()) {
546     xout.StartElement("RemoteRepositories");
547     for (cmCPackIFWRepository* r : this->RemoteRepositories) {
548       r->WriteRepositoryConfig(xout);
549     }
550     xout.EndElement();
551   }
552
553   // Attributes introduced in QtIFW 3.0.0
554   if (!this->IsVersionLess("3.0")) {
555     // WizardStyle
556     if (!this->WizardStyle.empty()) {
557       xout.Element("WizardStyle", this->WizardStyle);
558     }
559
560     // Stylesheet (copy to config dir)
561     if (!this->StyleSheet.empty()) {
562       std::string name = cmSystemTools::GetFilenameName(this->StyleSheet);
563       std::string path = this->Directory + "/config/" + name;
564       cmsys::SystemTools::CopyFileIfDifferent(this->StyleSheet, path);
565       xout.Element("StyleSheet", name);
566     }
567
568     // TitleColor
569     if (!this->TitleColor.empty()) {
570       xout.Element("TitleColor", this->TitleColor);
571     }
572   }
573
574   // Attributes introduced in QtIFW 4.0.0
575   if (!this->IsVersionLess("4.0")) {
576     // WizardShowPageList
577     if (!this->WizardShowPageList.empty()) {
578       xout.Element("WizardShowPageList", this->WizardShowPageList);
579     }
580
581     // DisableCommandLineInterface
582     if (!this->DisableCommandLineInterface.empty()) {
583       xout.Element("DisableCommandLineInterface",
584                    this->DisableCommandLineInterface);
585     }
586
587     // RunProgram
588     if (!this->RunProgram.empty()) {
589       xout.Element("RunProgram", this->RunProgram);
590     }
591
592     // RunProgramArguments
593     if (!this->RunProgramArguments.empty()) {
594       xout.StartElement("RunProgramArguments");
595       for (const auto& arg : this->RunProgramArguments) {
596         xout.Element("Argument", arg);
597       }
598       xout.EndElement();
599     }
600
601     // RunProgramDescription
602     if (!this->RunProgramDescription.empty()) {
603       xout.Element("RunProgramDescription", this->RunProgramDescription);
604     }
605   }
606
607   if (!this->RemoveTargetDir.empty()) {
608     xout.Element("RemoveTargetDir", this->RemoveTargetDir);
609   }
610
611   // Product images (copy to config dir)
612   if (!this->IsVersionLess("4.0") && !this->ProductImages.empty()) {
613     xout.StartElement("ProductImages");
614     for (auto const& srcImg : this->ProductImages) {
615       std::string name = cmSystemTools::GetFilenameName(srcImg);
616       std::string dstImg = this->Directory + "/config/" + name;
617       cmsys::SystemTools::CopyFileIfDifferent(srcImg, dstImg);
618       xout.Element("Image", name);
619     }
620     xout.EndElement();
621   }
622
623   // Resources (copy to resources dir)
624   if (!this->Resources.empty()) {
625     std::vector<std::string> resources;
626     cmCPackIFWResourcesParser parser(this);
627     for (size_t i = 0; i < this->Resources.size(); i++) {
628       if (parser.ParseResource(i)) {
629         std::string name = cmSystemTools::GetFilenameName(this->Resources[i]);
630         std::string path = this->Directory + "/resources/" + name;
631         cmsys::SystemTools::CopyFileIfDifferent(this->Resources[i], path);
632         resources.push_back(std::move(name));
633       } else {
634         cmCPackIFWLogger(WARNING,
635                          "Can't copy resources from \""
636                            << this->Resources[i]
637                            << "\". Resource will be skipped." << std::endl);
638       }
639     }
640     this->Resources = resources;
641   }
642
643   xout.EndElement();
644   xout.EndDocument();
645 }
646
647 void cmCPackIFWInstaller::GeneratePackageFiles()
648 {
649   if (this->Packages.empty() || this->Generator->IsOnePackage()) {
650     // Generate default package
651     cmCPackIFWPackage package;
652     package.Generator = this->Generator;
653     package.Installer = this;
654     // Check package group
655     if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_GROUP")) {
656       package.ConfigureFromGroup(option);
657       std::string forcedOption = "CPACK_IFW_COMPONENT_GROUP_" +
658         cmsys::SystemTools::UpperCase(option) + "_FORCED_INSTALLATION";
659       if (!this->GetOption(forcedOption)) {
660         package.ForcedInstallation = "true";
661       }
662     } else {
663       package.ConfigureFromOptions();
664     }
665     package.GeneratePackageFile();
666     return;
667   }
668
669   // Generate packages meta information
670   for (auto& p : this->Packages) {
671     cmCPackIFWPackage* package = p.second;
672     package->GeneratePackageFile();
673   }
674 }