cmCPackIFWPackage.cxx 20.4 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3 4
#include "cmCPackIFWPackage.h"

5
#include "cmCPackComponentGroup.h"
6
#include "cmCPackIFWCommon.h"
7
#include "cmCPackIFWGenerator.h"
8
#include "cmCPackIFWInstaller.h"
9
#include "cmCPackLog.h" // IWYU pragma: keep
10 11 12 13
#include "cmGeneratedFileStream.h"
#include "cmSystemTools.h"
#include "cmTimestamp.h"
#include "cmXMLWriter.h"
14

15 16 17
#include <map>
#include <sstream>
#include <stddef.h>
18
#include <utility>
19 20

//---------------------------------------------------------- CompareStruct ---
21
cmCPackIFWPackage::CompareStruct::CompareStruct()
22
  : Type(cmCPackIFWPackage::CompareNone)
23 24 25 26 27 28 29 30 31
{
}

//------------------------------------------------------- DependenceStruct ---
cmCPackIFWPackage::DependenceStruct::DependenceStruct()
{
}

cmCPackIFWPackage::DependenceStruct::DependenceStruct(
32
  const std::string& dependence)
33 34 35
{
  // Search compare section
  size_t pos = std::string::npos;
36
  if ((pos = dependence.find("<=")) != std::string::npos) {
37 38
    this->Compare.Type = cmCPackIFWPackage::CompareLessOrEqual;
    this->Compare.Value = dependence.substr(pos + 2);
39
  } else if ((pos = dependence.find(">=")) != std::string::npos) {
40 41
    this->Compare.Type = cmCPackIFWPackage::CompareGreaterOrEqual;
    this->Compare.Value = dependence.substr(pos + 2);
42
  } else if ((pos = dependence.find('<')) != std::string::npos) {
43 44
    this->Compare.Type = cmCPackIFWPackage::CompareLess;
    this->Compare.Value = dependence.substr(pos + 1);
45
  } else if ((pos = dependence.find('=')) != std::string::npos) {
46 47
    this->Compare.Type = cmCPackIFWPackage::CompareEqual;
    this->Compare.Value = dependence.substr(pos + 1);
48
  } else if ((pos = dependence.find('>')) != std::string::npos) {
49 50
    this->Compare.Type = cmCPackIFWPackage::CompareGreater;
    this->Compare.Value = dependence.substr(pos + 1);
51
  } else if ((pos = dependence.find('-')) != std::string::npos) {
52 53
    this->Compare.Type = cmCPackIFWPackage::CompareNone;
    this->Compare.Value = dependence.substr(pos + 1);
54 55 56 57
  }
  size_t dashPos = dependence.find('-');
  if (dashPos != std::string::npos) {
    pos = dashPos;
58
  }
59 60
  this->Name =
    pos == std::string::npos ? dependence : dependence.substr(0, pos);
61 62 63 64
}

std::string cmCPackIFWPackage::DependenceStruct::NameWithCompare() const
{
65 66
  if (this->Compare.Type == cmCPackIFWPackage::CompareNone) {
    return this->Name;
67
  }
68

69
  std::string result = this->Name;
70

71 72
  if (this->Compare.Type != cmCPackIFWPackage::CompareNone ||
      !this->Compare.Value.empty()) {
73 74 75
    result += "-";
  }

76
  if (this->Compare.Type == cmCPackIFWPackage::CompareLessOrEqual) {
77
    result += "<=";
78
  } else if (this->Compare.Type == cmCPackIFWPackage::CompareGreaterOrEqual) {
79
    result += ">=";
80
  } else if (this->Compare.Type == cmCPackIFWPackage::CompareLess) {
81
    result += "<";
82
  } else if (this->Compare.Type == cmCPackIFWPackage::CompareEqual) {
83
    result += "=";
84
  } else if (this->Compare.Type == cmCPackIFWPackage::CompareGreater) {
85
    result += ">";
86
  }
87

88
  result += this->Compare.Value;
89 90 91 92 93

  return result;
}

//------------------------------------------------------ cmCPackIFWPackage ---
94
cmCPackIFWPackage::cmCPackIFWPackage()
Daniel Pfeifer's avatar
Daniel Pfeifer committed
95
  : Installer(nullptr)
96
{
97 98
}

99
std::string cmCPackIFWPackage::GetComponentName(cmCPackComponent* component)
100
{
101
  if (!component) {
102
    return "";
103
  }
104
  const char* option =
105 106
    this->GetOption("CPACK_IFW_COMPONENT_" +
                    cmsys::SystemTools::UpperCase(component->Name) + "_NAME");
107 108 109 110 111
  return option ? option : component->Name;
}

void cmCPackIFWPackage::DefaultConfiguration()
{
112 113
  this->DisplayName.clear();
  this->Description.clear();
114 115 116
  this->Version.clear();
  this->ReleaseDate.clear();
  this->Script.clear();
117 118 119
  this->Licenses.clear();
  this->UserInterfaces.clear();
  this->Translations.clear();
120 121 122 123 124 125 126
  this->SortingPriority.clear();
  this->UpdateText.clear();
  this->Default.clear();
  this->Essential.clear();
  this->Virtual.clear();
  this->ForcedInstallation.clear();
  this->RequiresAdminRights.clear();
127 128 129 130 131 132
}

// Defaul configuration (all in one package)
int cmCPackIFWPackage::ConfigureFromOptions()
{
  // Restore defaul configuration
133
  this->DefaultConfiguration();
134 135

  // Name
136
  this->Name = this->Generator->GetRootPackageName();
137 138

  // Display name
139
  if (const char* option = this->GetOption("CPACK_PACKAGE_NAME")) {
140
    this->DisplayName[""] = option;
141
  } else {
142
    this->DisplayName[""] = "Your package";
143
  }
144 145 146

  // Description
  if (const char* option =
147
        this->GetOption("CPACK_PACKAGE_DESCRIPTION_SUMMARY")) {
148
    this->Description[""] = option;
149
  } else {
150
    this->Description[""] = "Your package description";
151
  }
152 153

  // Version
154 155
  if (const char* option = this->GetOption("CPACK_PACKAGE_VERSION")) {
    this->Version = option;
156
  } else {
157
    this->Version = "1.0.0";
158
  }
159

160
  this->ForcedInstallation = "true";
161 162 163 164

  return 1;
}

165
int cmCPackIFWPackage::ConfigureFromComponent(cmCPackComponent* component)
166
{
167
  if (!component) {
168
    return 0;
169
  }
170 171

  // Restore defaul configuration
172
  this->DefaultConfiguration();
173

174 175
  std::string prefix = "CPACK_IFW_COMPONENT_" +
    cmsys::SystemTools::UpperCase(component->Name) + "_";
176 177

  // Display name
178
  this->DisplayName[""] = component->DisplayName;
179 180

  // Description
181
  this->Description[""] = component->Description;
182 183

  // Version
184 185
  if (const char* optVERSION = this->GetOption(prefix + "VERSION")) {
    this->Version = optVERSION;
186
  } else if (const char* optPACKAGE_VERSION =
187 188
               this->GetOption("CPACK_PACKAGE_VERSION")) {
    this->Version = optPACKAGE_VERSION;
189
  } else {
190
    this->Version = "1.0.0";
191
  }
192 193

  // Script
194 195
  if (const char* option = this->GetOption(prefix + "SCRIPT")) {
    this->Script = option;
196
  }
197

198 199
  // User interfaces
  if (const char* option = this->GetOption(prefix + "USER_INTERFACES")) {
200 201
    this->UserInterfaces.clear();
    cmSystemTools::ExpandListArgument(option, this->UserInterfaces);
202 203
  }

204
  // CMake dependencies
205
  if (!component->Dependencies.empty()) {
206 207
    for (cmCPackComponent* dep : component->Dependencies) {
      this->Dependencies.insert(this->Generator->ComponentPackages[dep]);
208
    }
209
  }
210 211

  // Licenses
212
  if (const char* option = this->GetOption(prefix + "LICENSES")) {
213 214 215
    this->Licenses.clear();
    cmSystemTools::ExpandListArgument(option, this->Licenses);
    if (this->Licenses.size() % 2 != 0) {
216 217 218 219 220
      cmCPackIFWLogger(
        WARNING,
        prefix << "LICENSES"
               << " should contain pairs of <display_name> and <file_path>."
               << std::endl);
221
      this->Licenses.clear();
222
    }
223
  }
224 225

  // Priority
226
  if (const char* option = this->GetOption(prefix + "PRIORITY")) {
227
    this->SortingPriority = option;
228
    cmCPackIFWLogger(
229 230
      WARNING,
      "The \"PRIORITY\" option is set "
231 232 233
        << "for component \"" << component->Name << "\", but there option is "
        << "deprecated. Please use \"SORTING_PRIORITY\" option instead."
        << std::endl);
234
  }
235 236

  // Default
237
  this->Default = component->IsDisabledByDefault ? "false" : "true";
238

239 240
  // Essential
  if (this->IsOn(prefix + "ESSENTIAL")) {
241
    this->Essential = "true";
242 243
  }

244
  // Virtual
245
  this->Virtual = component->IsHidden ? "true" : "";
246 247

  // ForcedInstallation
248
  this->ForcedInstallation = component->IsRequired ? "true" : "false";
249

250
  return this->ConfigureFromPrefix(prefix);
251 252
}

253
int cmCPackIFWPackage::ConfigureFromGroup(cmCPackComponentGroup* group)
254
{
255
  if (!group) {
256
    return 0;
257
  }
258 259

  // Restore defaul configuration
260
  this->DefaultConfiguration();
261

262 263
  std::string prefix = "CPACK_IFW_COMPONENT_GROUP_" +
    cmsys::SystemTools::UpperCase(group->Name) + "_";
264

265 266
  this->DisplayName[""] = group->DisplayName;
  this->Description[""] = group->Description;
267 268

  // Version
269 270
  if (const char* optVERSION = this->GetOption(prefix + "VERSION")) {
    this->Version = optVERSION;
271
  } else if (const char* optPACKAGE_VERSION =
272 273
               this->GetOption("CPACK_PACKAGE_VERSION")) {
    this->Version = optPACKAGE_VERSION;
274
  } else {
275
    this->Version = "1.0.0";
276
  }
277

278
  // Script
279 280
  if (const char* option = this->GetOption(prefix + "SCRIPT")) {
    this->Script = option;
281
  }
282

283 284
  // User interfaces
  if (const char* option = this->GetOption(prefix + "USER_INTERFACES")) {
285 286
    this->UserInterfaces.clear();
    cmSystemTools::ExpandListArgument(option, this->UserInterfaces);
287 288
  }

289
  // Licenses
290
  if (const char* option = this->GetOption(prefix + "LICENSES")) {
291 292 293
    this->Licenses.clear();
    cmSystemTools::ExpandListArgument(option, this->Licenses);
    if (this->Licenses.size() % 2 != 0) {
294 295 296 297 298
      cmCPackIFWLogger(
        WARNING,
        prefix << "LICENSES"
               << " should contain pairs of <display_name> and <file_path>."
               << std::endl);
299
      this->Licenses.clear();
300
    }
301
  }
302 303

  // Priority
304
  if (const char* option = this->GetOption(prefix + "PRIORITY")) {
305
    this->SortingPriority = option;
306
    cmCPackIFWLogger(
307 308
      WARNING,
      "The \"PRIORITY\" option is set "
309 310 311 312
        << "for component group \"" << group->Name
        << "\", but there option is "
        << "deprecated. Please use \"SORTING_PRIORITY\" option instead."
        << std::endl);
313
  }
314

315
  return this->ConfigureFromPrefix(prefix);
316 317
}

318
int cmCPackIFWPackage::ConfigureFromGroup(const std::string& groupName)
319 320 321 322
{
  // Group configuration

  cmCPackComponentGroup group;
323 324
  std::string prefix =
    "CPACK_COMPONENT_GROUP_" + cmsys::SystemTools::UpperCase(groupName) + "_";
325

326
  if (const char* option = this->GetOption(prefix + "DISPLAY_NAME")) {
327
    group.DisplayName = option;
328
  } else {
329
    group.DisplayName = group.Name;
330
  }
331

332
  if (const char* option = this->GetOption(prefix + "DESCRIPTION")) {
333
    group.Description = option;
334
  }
335 336
  group.IsBold = this->IsOn(prefix + "BOLD_TITLE");
  group.IsExpandedByDefault = this->IsOn(prefix + "EXPANDED");
337 338 339 340 341

  // Package configuration

  group.Name = groupName;

342
  if (Generator) {
343
    this->Name = this->Generator->GetGroupPackageName(&group);
344
  } else {
345
    this->Name = group.Name;
346
  }
347

348
  return this->ConfigureFromGroup(&group);
349 350
}

351 352 353 354 355 356 357 358
// Common options for components and groups
int cmCPackIFWPackage::ConfigureFromPrefix(const std::string& prefix)
{
  // Temporary variable for full option name
  std::string option;

  // Display name
  option = prefix + "DISPLAY_NAME";
359 360 361
  if (this->IsSetToEmpty(option)) {
    this->DisplayName.clear();
  } else if (const char* value = this->GetOption(option)) {
362
    this->ExpandListArgument(value, this->DisplayName);
363 364 365 366
  }

  // Description
  option = prefix + "DESCRIPTION";
367 368 369
  if (this->IsSetToEmpty(option)) {
    this->Description.clear();
  } else if (const char* value = this->GetOption(option)) {
370
    this->ExpandListArgument(value, this->Description);
371 372 373 374
  }

  // Release date
  option = prefix + "RELEASE_DATE";
375 376 377 378
  if (this->IsSetToEmpty(option)) {
    this->ReleaseDate.clear();
  } else if (const char* value = this->GetOption(option)) {
    this->ReleaseDate = value;
379 380
  }

381 382
  // Sorting priority
  option = prefix + "SORTING_PRIORITY";
383 384 385 386
  if (this->IsSetToEmpty(option)) {
    this->SortingPriority.clear();
  } else if (const char* value = this->GetOption(option)) {
    this->SortingPriority = value;
387 388 389 390
  }

  // Update text
  option = prefix + "UPDATE_TEXT";
391 392 393 394
  if (this->IsSetToEmpty(option)) {
    this->UpdateText.clear();
  } else if (const char* value = this->GetOption(option)) {
    this->UpdateText = value;
395 396 397 398
  }

  // Translations
  option = prefix + "TRANSLATIONS";
399 400
  if (this->IsSetToEmpty(option)) {
    this->Translations.clear();
401
  } else if (const char* value = this->GetOption(option)) {
402 403
    this->Translations.clear();
    cmSystemTools::ExpandListArgument(value, this->Translations);
404 405 406 407 408 409 410 411 412 413 414 415
  }

  // QtIFW dependencies
  std::vector<std::string> deps;
  option = prefix + "DEPENDS";
  if (const char* value = this->GetOption(option)) {
    cmSystemTools::ExpandListArgument(value, deps);
  }
  option = prefix + "DEPENDENCIES";
  if (const char* value = this->GetOption(option)) {
    cmSystemTools::ExpandListArgument(value, deps);
  }
416 417
  for (std::string const& d : deps) {
    DependenceStruct dep(d);
418 419
    if (this->Generator->Packages.count(dep.Name)) {
      cmCPackIFWPackage& depPkg = this->Generator->Packages[dep.Name];
420 421
      dep.Name = depPkg.Name;
    }
422 423
    bool hasDep = this->Generator->DependentPackages.count(dep.Name) > 0;
    DependenceStruct& depRef = this->Generator->DependentPackages[dep.Name];
424 425 426
    if (!hasDep) {
      depRef = dep;
    }
427
    this->AlienDependencies.insert(&depRef);
428 429 430 431
  }

  // Automatic dependency on
  option = prefix + "AUTO_DEPEND_ON";
432 433
  if (this->IsSetToEmpty(option)) {
    this->AlienAutoDependOn.clear();
434 435 436
  } else if (const char* value = this->GetOption(option)) {
    std::vector<std::string> depsOn;
    cmSystemTools::ExpandListArgument(value, depsOn);
437 438
    for (std::string const& d : depsOn) {
      DependenceStruct dep(d);
439 440
      if (this->Generator->Packages.count(dep.Name)) {
        cmCPackIFWPackage& depPkg = this->Generator->Packages[dep.Name];
441 442
        dep.Name = depPkg.Name;
      }
443 444
      bool hasDep = this->Generator->DependentPackages.count(dep.Name) > 0;
      DependenceStruct& depRef = this->Generator->DependentPackages[dep.Name];
445 446 447
      if (!hasDep) {
        depRef = dep;
      }
448
      this->AlienAutoDependOn.insert(&depRef);
449 450 451
    }
  }

452 453
  // Visibility
  option = prefix + "VIRTUAL";
454 455 456 457
  if (this->IsSetToEmpty(option)) {
    this->Virtual.clear();
  } else if (this->IsOn(option)) {
    this->Virtual = "true";
458 459 460 461
  }

  // Default selection
  option = prefix + "DEFAULT";
462 463 464
  if (this->IsSetToEmpty(option)) {
    this->Default.clear();
  } else if (const char* value = this->GetOption(option)) {
465
    std::string lowerValue = cmsys::SystemTools::LowerCase(value);
466
    if (lowerValue == "true") {
467
      this->Default = "true";
468
    } else if (lowerValue == "false") {
469
      this->Default = "false";
470
    } else if (lowerValue == "script") {
471
      this->Default = "script";
472
    } else {
473
      this->Default = value;
474 475 476 477 478
    }
  }

  // Forsed installation
  option = prefix + "FORCED_INSTALLATION";
479 480 481 482 483 484
  if (this->IsSetToEmpty(option)) {
    this->ForcedInstallation.clear();
  } else if (this->IsOn(option)) {
    this->ForcedInstallation = "true";
  } else if (this->IsSetToOff(option)) {
    this->ForcedInstallation = "false";
485 486
  }

487 488 489 490 491 492 493 494 495
  // Replaces
  option = prefix + "REPLACES";
  if (this->IsSetToEmpty(option)) {
    this->Replaces.clear();
  } else if (const char* value = this->GetOption(option)) {
    this->Replaces.clear();
    cmSystemTools::ExpandListArgument(value, this->Replaces);
  }

496 497
  // Requires admin rights
  option = prefix + "REQUIRES_ADMIN_RIGHTS";
498 499 500 501 502 503
  if (this->IsSetToEmpty(option)) {
    this->RequiresAdminRights.clear();
  } else if (this->IsOn(option)) {
    this->RequiresAdminRights = "true";
  } else if (this->IsSetToOff(option)) {
    this->RequiresAdminRights = "false";
504 505
  }

506 507 508 509 510 511 512 513 514 515
  // Checkable
  option = prefix + "CHECKABLE";
  if (this->IsSetToEmpty(option)) {
    this->Checkable.clear();
  } else if (this->IsOn(option)) {
    this->Checkable = "true";
  } else if (this->IsSetToOff(option)) {
    this->Checkable = "false";
  }

516 517 518
  return 1;
}

519 520 521
void cmCPackIFWPackage::GeneratePackageFile()
{
  // Lazy directory initialization
522 523 524 525 526
  if (this->Directory.empty()) {
    if (this->Installer) {
      this->Directory = this->Installer->Directory + "/packages/" + this->Name;
    } else if (this->Generator) {
      this->Directory = this->Generator->toplevel + "/packages/" + this->Name;
527
    }
528
  }
529 530

  // Output stream
531
  cmGeneratedFileStream fout((this->Directory + "/meta/package.xml").data());
532
  cmXMLWriter xout(fout);
533

534
  xout.StartDocument();
535 536 537

  WriteGeneratedByToStrim(xout);

538
  xout.StartElement("Package");
539

540
  // DisplayName (with translations)
541
  for (auto const& dn : this->DisplayName) {
542
    xout.StartElement("DisplayName");
543 544
    if (!dn.first.empty()) {
      xout.Attribute("xml:lang", dn.first);
545
    }
546
    xout.Content(dn.second);
547 548 549 550
    xout.EndElement();
  }

  // Description (with translations)
551
  for (auto const& d : this->Description) {
552
    xout.StartElement("Description");
553 554
    if (!d.first.empty()) {
      xout.Attribute("xml:lang", d.first);
555
    }
556
    xout.Content(d.second);
557 558
    xout.EndElement();
  }
559 560

  // Update text
561 562
  if (!this->UpdateText.empty()) {
    xout.Element("UpdateText", this->UpdateText);
563 564
  }

565 566
  xout.Element("Name", this->Name);
  xout.Element("Version", this->Version);
567

568 569
  if (!this->ReleaseDate.empty()) {
    xout.Element("ReleaseDate", this->ReleaseDate);
570
  } else {
571
    xout.Element("ReleaseDate", cmTimestamp().CurrentTime("%Y-%m-%d", true));
572
  }
573 574

  // Script (copy to meta dir)
575 576 577 578
  if (!this->Script.empty()) {
    std::string name = cmSystemTools::GetFilenameName(this->Script);
    std::string path = this->Directory + "/meta/" + name;
    cmsys::SystemTools::CopyFileIfDifferent(this->Script, path);
579
    xout.Element("Script", name);
580
  }
581

582 583
  // User Interfaces (copy to meta dir)
  std::vector<std::string> userInterfaces = UserInterfaces;
584 585
  for (std::string& userInterface : userInterfaces) {
    std::string name = cmSystemTools::GetFilenameName(userInterface);
586
    std::string path = this->Directory + "/meta/" + name;
587 588
    cmsys::SystemTools::CopyFileIfDifferent(userInterface, path);
    userInterface = name;
589 590 591
  }
  if (!userInterfaces.empty()) {
    xout.StartElement("UserInterfaces");
592 593
    for (std::string const& userInterface : userInterfaces) {
      xout.Element("UserInterface", userInterface);
594 595 596 597
    }
    xout.EndElement();
  }

598 599
  // Translations (copy to meta dir)
  std::vector<std::string> translations = Translations;
600 601
  for (std::string& translation : translations) {
    std::string name = cmSystemTools::GetFilenameName(translation);
602
    std::string path = this->Directory + "/meta/" + name;
603 604
    cmsys::SystemTools::CopyFileIfDifferent(translation, path);
    translation = name;
605 606 607
  }
  if (!translations.empty()) {
    xout.StartElement("Translations");
608 609
    for (std::string const& translation : translations) {
      xout.Element("Translation", translation);
610 611 612 613
    }
    xout.EndElement();
  }

614 615
  // Dependencies
  std::set<DependenceStruct> compDepSet;
616 617
  for (DependenceStruct* ad : this->AlienDependencies) {
    compDepSet.insert(*ad);
618
  }
619 620
  for (cmCPackIFWPackage* d : this->Dependencies) {
    compDepSet.insert(DependenceStruct(d->Name));
621
  }
622
  // Write dependencies
623
  if (!compDepSet.empty()) {
624
    std::ostringstream dependencies;
625
    std::set<DependenceStruct>::iterator it = compDepSet.begin();
626
    dependencies << it->NameWithCompare();
627
    ++it;
628
    while (it != compDepSet.end()) {
629
      dependencies << "," << it->NameWithCompare();
630 631
      ++it;
    }
632 633
    xout.Element("Dependencies", dependencies.str());
  }
634

635 636
  // Automatic dependency on
  std::set<DependenceStruct> compAutoDepSet;
637 638
  for (DependenceStruct* aad : this->AlienAutoDependOn) {
    compAutoDepSet.insert(*aad);
639 640 641 642 643 644 645 646 647 648 649 650 651 652
  }
  // Write automatic dependency on
  if (!compAutoDepSet.empty()) {
    std::ostringstream dependencies;
    std::set<DependenceStruct>::iterator it = compAutoDepSet.begin();
    dependencies << it->NameWithCompare();
    ++it;
    while (it != compAutoDepSet.end()) {
      dependencies << "," << it->NameWithCompare();
      ++it;
    }
    xout.Element("AutoDependOn", dependencies.str());
  }

653
  // Licenses (copy to meta dir)
654
  std::vector<std::string> licenses = this->Licenses;
655
  for (size_t i = 1; i < licenses.size(); i += 2) {
656
    std::string name = cmSystemTools::GetFilenameName(licenses[i]);
657
    std::string path = this->Directory + "/meta/" + name;
658
    cmsys::SystemTools::CopyFileIfDifferent(licenses[i], path);
659
    licenses[i] = name;
660 661
  }
  if (!licenses.empty()) {
662
    xout.StartElement("Licenses");
663
    for (size_t i = 0; i < licenses.size(); i += 2) {
664 665 666 667
      xout.StartElement("License");
      xout.Attribute("name", licenses[i]);
      xout.Attribute("file", licenses[i + 1]);
      xout.EndElement();
668
    }
669 670
    xout.EndElement();
  }
671

672 673
  if (!this->ForcedInstallation.empty()) {
    xout.Element("ForcedInstallation", this->ForcedInstallation);
674
  }
675

676 677 678 679 680 681 682 683 684 685 686 687 688
  // Replaces
  if (!this->Replaces.empty()) {
    std::ostringstream replaces;
    std::vector<std::string>::iterator it = this->Replaces.begin();
    replaces << *it;
    ++it;
    while (it != this->Replaces.end()) {
      replaces << "," << *it;
      ++it;
    }
    xout.Element("Replaces", replaces.str());
  }

689 690
  if (!this->RequiresAdminRights.empty()) {
    xout.Element("RequiresAdminRights", this->RequiresAdminRights);
691 692
  }

693 694 695 696
  if (!this->Virtual.empty()) {
    xout.Element("Virtual", this->Virtual);
  } else if (!this->Default.empty()) {
    xout.Element("Default", this->Default);
697
  }
698

699
  // Essential
700 701
  if (!this->Essential.empty()) {
    xout.Element("Essential", this->Essential);
702 703
  }

704
  // Priority
705 706
  if (!this->SortingPriority.empty()) {
    xout.Element("SortingPriority", this->SortingPriority);
707
  }
708

709 710 711 712 713
  // Checkable
  if (!this->Checkable.empty()) {
    xout.Element("Checkable", this->Checkable);
  }

714 715
  xout.EndElement();
  xout.EndDocument();
716
}