CMake Nightly Date Stamp
[kiteware-cmake.git] / Source / CPack / cmCPackRPMGenerator.cxx
blob8dfb1597c120e5be7f9ecfea83030ba696d55a3b
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 "cmCPackRPMGenerator.h"
5 #include <algorithm>
6 #include <cctype>
7 #include <map>
8 #include <ostream>
9 #include <utility>
10 #include <vector>
12 #include "cmCPackComponentGroup.h"
13 #include "cmCPackGenerator.h"
14 #include "cmCPackLog.h"
15 #include "cmCryptoHash.h"
16 #include "cmStringAlgorithms.h"
17 #include "cmSystemTools.h"
18 #include "cmValue.h"
20 cmCPackRPMGenerator::cmCPackRPMGenerator() = default;
22 cmCPackRPMGenerator::~cmCPackRPMGenerator() = default;
24 int cmCPackRPMGenerator::InitializeInternal()
26 this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr");
27 if (cmIsOff(this->GetOption("CPACK_SET_DESTDIR"))) {
28 this->SetOption("CPACK_SET_DESTDIR", "I_ON");
30 /* Replace space in CPACK_PACKAGE_NAME in order to avoid
31 * rpmbuild scream on unwanted space in filename issue
32 * Moreover RPM file do not usually embed space in filename
34 if (this->GetOption("CPACK_PACKAGE_NAME")) {
35 std::string packageName = this->GetOption("CPACK_PACKAGE_NAME");
36 std::replace(packageName.begin(), packageName.end(), ' ', '-');
37 this->SetOption("CPACK_PACKAGE_NAME", packageName);
39 /* same for CPACK_PACKAGE_FILE_NAME */
40 if (this->GetOption("CPACK_PACKAGE_FILE_NAME")) {
41 std::string packageName = this->GetOption("CPACK_PACKAGE_FILE_NAME");
42 std::replace(packageName.begin(), packageName.end(), ' ', '-');
43 this->SetOption("CPACK_PACKAGE_FILE_NAME", packageName);
45 return this->Superclass::InitializeInternal();
48 void cmCPackRPMGenerator::AddGeneratedPackageNames()
50 // add the generated packages to package file names list
51 std::string fileNames(this->GetOption("GEN_CPACK_OUTPUT_FILES"));
52 const char sep = ';';
53 std::string::size_type pos1 = 0;
54 std::string::size_type pos2 = fileNames.find(sep, pos1 + 1);
55 while (pos2 != std::string::npos) {
56 this->packageFileNames.push_back(fileNames.substr(pos1, pos2 - pos1));
57 pos1 = pos2 + 1;
58 pos2 = fileNames.find(sep, pos1 + 1);
60 this->packageFileNames.push_back(fileNames.substr(pos1, pos2 - pos1));
63 int cmCPackRPMGenerator::PackageOnePack(std::string const& initialToplevel,
64 std::string const& packageName)
66 // Determine the sanitized package name that can be used in file-names on
67 // the file-system.
68 std::string sanitizedPkgNameSuffix =
69 this->GetSanitizedDirOrFileName(packageName, false);
70 // Determine the sanitized packaging directory-name that can be used on the
71 // file-system.
72 std::string sanitizedPkgDirName =
73 this->GetSanitizedDirOrFileName(packageName);
75 // Begin the archive for this pack
76 std::string localToplevel(initialToplevel);
77 std::string packageFileName(
78 cmSystemTools::GetParentDirectory(this->toplevel));
79 std::string outputFileName(
80 this->GetComponentPackageFileName(
81 this->GetOption("CPACK_PACKAGE_FILE_NAME"), packageName, true) +
82 this->GetOutputExtension());
84 localToplevel += "/" + sanitizedPkgDirName;
85 /* replace the TEMP DIRECTORY with the component one */
86 this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel);
87 packageFileName += "/" + outputFileName;
88 /* replace proposed CPACK_OUTPUT_FILE_NAME */
89 this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName);
90 /* replace the TEMPORARY package file name */
91 this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", packageFileName);
92 // Tell CPackRPM.cmake the name of the component NAME.
93 this->SetOption("CPACK_RPM_PACKAGE_COMPONENT", packageName);
94 // Tell CPackRPM.cmake the suffix for the component NAME.
95 this->SetOption("CPACK_RPM_PACKAGE_COMPONENT_PART_NAME",
96 sanitizedPkgNameSuffix);
97 // Tell CPackRPM.cmake the path where the component is.
98 std::string component_path = cmStrCat('/', sanitizedPkgDirName);
99 this->SetOption("CPACK_RPM_PACKAGE_COMPONENT_PART_PATH", component_path);
100 if (!this->ReadListFile("Internal/CPack/CPackRPM.cmake")) {
101 cmCPackLogger(cmCPackLog::LOG_ERROR,
102 "Error while execution CPackRPM.cmake" << std::endl);
103 return 0;
106 return 1;
109 std::string cmCPackRPMGenerator::GetSanitizedDirOrFileName(
110 const std::string& name, bool isFullName) const
112 auto sanitizedName =
113 this->cmCPackGenerator::GetSanitizedDirOrFileName(name, isFullName);
114 if (sanitizedName == name && !isFullName) {
115 // Make sure to also sanitize if name contains a colon (':').
116 if (name.find_first_of(':') != std::string::npos) {
117 cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
118 return hasher.HashString(name);
121 return sanitizedName;
124 int cmCPackRPMGenerator::PackageComponents(bool ignoreGroup)
126 int retval = 1;
127 /* Reset package file name list it will be populated during the
128 * component packaging run*/
129 this->packageFileNames.clear();
130 std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
132 cmValue mainComponent = this->GetOption("CPACK_RPM_MAIN_COMPONENT");
134 if (this->IsOn("CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE") &&
135 !this->IsOn("CPACK_RPM_DEBUGINFO_PACKAGE")) {
136 // check if we need to set CPACK_RPM_DEBUGINFO_PACKAGE because non of
137 // the components is setting per component debuginfo package variable
138 bool shouldSet = true;
140 if (ignoreGroup) {
141 std::map<std::string, cmCPackComponent>::iterator compIt;
142 for (compIt = this->Components.begin(); compIt != this->Components.end();
143 ++compIt) {
144 std::string component(compIt->first);
145 std::transform(component.begin(), component.end(), component.begin(),
146 ::toupper);
148 if (this->IsOn("CPACK_RPM_" + compIt->first + "_DEBUGINFO_PACKAGE") ||
149 this->IsOn("CPACK_RPM_" + component + "_DEBUGINFO_PACKAGE")) {
150 shouldSet = false;
151 break;
154 } else {
155 std::map<std::string, cmCPackComponentGroup>::iterator compGIt;
156 for (compGIt = this->ComponentGroups.begin();
157 compGIt != this->ComponentGroups.end(); ++compGIt) {
158 std::string component(compGIt->first);
159 std::transform(component.begin(), component.end(), component.begin(),
160 ::toupper);
162 if (this->IsOn("CPACK_RPM_" + compGIt->first + "_DEBUGINFO_PACKAGE") ||
163 this->IsOn("CPACK_RPM_" + component + "_DEBUGINFO_PACKAGE")) {
164 shouldSet = false;
165 break;
169 if (shouldSet) {
170 std::map<std::string, cmCPackComponent>::iterator compIt;
171 for (compIt = this->Components.begin();
172 compIt != this->Components.end(); ++compIt) {
173 // Does the component belong to a group?
174 if (compIt->second.Group == nullptr) {
175 std::string component(compIt->first);
176 std::transform(component.begin(), component.end(),
177 component.begin(), ::toupper);
179 if (this->IsOn("CPACK_RPM_" + compIt->first +
180 "_DEBUGINFO_PACKAGE") ||
181 this->IsOn("CPACK_RPM_" + component + "_DEBUGINFO_PACKAGE")) {
182 shouldSet = false;
183 break;
190 if (shouldSet) {
191 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
192 "Setting "
193 << "CPACK_RPM_DEBUGINFO_PACKAGE because "
194 << "CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE is set but "
195 << " none of the "
196 << "CPACK_RPM_<component>_DEBUGINFO_PACKAGE variables "
197 << "are set." << std::endl);
198 this->SetOption("CPACK_RPM_DEBUGINFO_PACKAGE", "ON");
202 if (mainComponent) {
203 if (this->IsOn("CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE")) {
204 this->SetOption("GENERATE_SPEC_PARTS", "ON");
207 std::string mainComponentUpper(mainComponent);
208 std::transform(mainComponentUpper.begin(), mainComponentUpper.end(),
209 mainComponentUpper.begin(), ::toupper);
211 // The default behavior is to have one package by component group
212 // unless CPACK_COMPONENTS_IGNORE_GROUP is specified.
213 if (!ignoreGroup) {
214 auto mainCompGIt = this->ComponentGroups.end();
216 std::map<std::string, cmCPackComponentGroup>::iterator compGIt;
217 for (compGIt = this->ComponentGroups.begin();
218 compGIt != this->ComponentGroups.end(); ++compGIt) {
219 std::string component(compGIt->first);
220 std::transform(component.begin(), component.end(), component.begin(),
221 ::toupper);
223 if (mainComponentUpper == component) {
224 // main component will be handled last
225 mainCompGIt = compGIt;
226 continue;
229 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
230 "Packaging component group: " << compGIt->first
231 << std::endl);
232 retval &= this->PackageOnePack(initialTopLevel, compGIt->first);
234 // Handle Orphan components (components not belonging to any groups)
235 auto mainCompIt = this->Components.end();
236 std::map<std::string, cmCPackComponent>::iterator compIt;
237 for (compIt = this->Components.begin(); compIt != this->Components.end();
238 ++compIt) {
239 // Does the component belong to a group?
240 if (compIt->second.Group == nullptr) {
241 std::string component(compIt->first);
242 std::transform(component.begin(), component.end(), component.begin(),
243 ::toupper);
245 if (mainComponentUpper == component) {
246 // main component will be handled last
247 mainCompIt = compIt;
248 continue;
251 cmCPackLogger(
252 cmCPackLog::LOG_VERBOSE,
253 "Component <"
254 << compIt->second.Name
255 << "> does not belong to any group, package it separately."
256 << std::endl);
257 retval &= this->PackageOnePack(initialTopLevel, compIt->first);
261 if (retval) {
262 this->SetOption("GENERATE_SPEC_PARTS", "OFF");
264 if (mainCompGIt != this->ComponentGroups.end()) {
265 retval &= this->PackageOnePack(initialTopLevel, mainCompGIt->first);
266 } else if (mainCompIt != this->Components.end()) {
267 retval &= this->PackageOnePack(initialTopLevel, mainCompIt->first);
268 } else {
269 cmCPackLogger(cmCPackLog::LOG_ERROR,
270 "CPACK_RPM_MAIN_COMPONENT set"
271 << " to non existing component.\n");
272 retval = 0;
276 // CPACK_COMPONENTS_IGNORE_GROUPS is set
277 // We build 1 package per component
278 else {
279 auto mainCompIt = this->Components.end();
281 std::map<std::string, cmCPackComponent>::iterator compIt;
282 for (compIt = this->Components.begin(); compIt != this->Components.end();
283 ++compIt) {
284 std::string component(compIt->first);
285 std::transform(component.begin(), component.end(), component.begin(),
286 ::toupper);
288 if (mainComponentUpper == component) {
289 // main component will be handled last
290 mainCompIt = compIt;
291 continue;
294 retval &= this->PackageOnePack(initialTopLevel, compIt->first);
297 if (retval) {
298 this->SetOption("GENERATE_SPEC_PARTS", "OFF");
300 if (mainCompIt != this->Components.end()) {
301 retval &= this->PackageOnePack(initialTopLevel, mainCompIt->first);
302 } else {
303 cmCPackLogger(cmCPackLog::LOG_ERROR,
304 "CPACK_RPM_MAIN_COMPONENT set"
305 << " to non existing component.\n");
306 retval = 0;
310 } else if (!this->IsOn("CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE") ||
311 this->Components.size() == 1) {
312 // The default behavior is to have one package by component group
313 // unless CPACK_COMPONENTS_IGNORE_GROUP is specified.
314 if (!ignoreGroup) {
315 std::map<std::string, cmCPackComponentGroup>::iterator compGIt;
316 for (compGIt = this->ComponentGroups.begin();
317 compGIt != this->ComponentGroups.end(); ++compGIt) {
318 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
319 "Packaging component group: " << compGIt->first
320 << std::endl);
321 retval &= this->PackageOnePack(initialTopLevel, compGIt->first);
323 // Handle Orphan components (components not belonging to any groups)
324 std::map<std::string, cmCPackComponent>::iterator compIt;
325 for (compIt = this->Components.begin(); compIt != this->Components.end();
326 ++compIt) {
327 // Does the component belong to a group?
328 if (compIt->second.Group == nullptr) {
329 cmCPackLogger(
330 cmCPackLog::LOG_VERBOSE,
331 "Component <"
332 << compIt->second.Name
333 << "> does not belong to any group, package it separately."
334 << std::endl);
335 retval &= this->PackageOnePack(initialTopLevel, compIt->first);
339 // CPACK_COMPONENTS_IGNORE_GROUPS is set
340 // We build 1 package per component
341 else {
342 std::map<std::string, cmCPackComponent>::iterator compIt;
343 for (compIt = this->Components.begin(); compIt != this->Components.end();
344 ++compIt) {
345 retval &= this->PackageOnePack(initialTopLevel, compIt->first);
348 } else {
349 cmCPackLogger(
350 cmCPackLog::LOG_ERROR,
351 "CPACK_RPM_MAIN_COMPONENT not set but"
352 << " it is mandatory with CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE"
353 << " being set.\n");
354 retval = 0;
357 if (retval) {
358 this->AddGeneratedPackageNames();
359 return retval;
362 return 0;
365 int cmCPackRPMGenerator::PackageComponentsAllInOne(
366 const std::string& compInstDirName)
368 int retval = 1;
369 /* Reset package file name list it will be populated during the
370 * component packaging run*/
371 this->packageFileNames.clear();
372 std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
374 if (this->IsOn("CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE")) {
375 this->SetOption("CPACK_RPM_DEBUGINFO_PACKAGE", "ON");
378 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
379 "Packaging all groups in one package..."
380 "(CPACK_COMPONENTS_ALL_[GROUPS_]IN_ONE_PACKAGE is set)"
381 << std::endl);
383 // The ALL GROUPS in ONE package case
384 std::string localToplevel(initialTopLevel);
385 std::string packageFileName(
386 cmSystemTools::GetParentDirectory(this->toplevel));
387 std::string outputFileName(
388 std::string(this->GetOption("CPACK_PACKAGE_FILE_NAME")) +
389 this->GetOutputExtension());
390 // all GROUP in one vs all COMPONENT in one
391 localToplevel += "/" + compInstDirName;
393 /* replace the TEMP DIRECTORY with the component one */
394 this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel);
395 packageFileName += "/" + outputFileName;
396 /* replace proposed CPACK_OUTPUT_FILE_NAME */
397 this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName);
398 /* replace the TEMPORARY package file name */
399 this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", packageFileName);
401 if (!compInstDirName.empty()) {
402 // Tell CPackRPM.cmake the path where the component is.
403 std::string component_path = cmStrCat('/', compInstDirName);
404 this->SetOption("CPACK_RPM_PACKAGE_COMPONENT_PART_PATH", component_path);
407 if (this->ReadListFile("Internal/CPack/CPackRPM.cmake")) {
408 this->AddGeneratedPackageNames();
409 } else {
410 cmCPackLogger(cmCPackLog::LOG_ERROR,
411 "Error while execution CPackRPM.cmake" << std::endl);
412 retval = 0;
415 return retval;
418 int cmCPackRPMGenerator::PackageFiles()
420 cmCPackLogger(cmCPackLog::LOG_DEBUG,
421 "Toplevel: " << this->toplevel << std::endl);
423 /* Are we in the component packaging case */
424 if (this->WantsComponentInstallation()) {
425 // CASE 1 : COMPONENT ALL-IN-ONE package
426 // If ALL COMPONENTS in ONE package has been requested
427 // then the package file is unique and should be open here.
428 if (this->componentPackageMethod == ONE_PACKAGE) {
429 return this->PackageComponentsAllInOne("ALL_COMPONENTS_IN_ONE");
431 // CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one)
432 // There will be 1 package for each component group
433 // however one may require to ignore component group and
434 // in this case you'll get 1 package for each component.
435 return this->PackageComponents(this->componentPackageMethod ==
436 ONE_PACKAGE_PER_COMPONENT);
438 // CASE 3 : NON COMPONENT package.
439 return this->PackageComponentsAllInOne("");
442 bool cmCPackRPMGenerator::SupportsComponentInstallation() const
444 return this->IsOn("CPACK_RPM_COMPONENT_INSTALL");
447 std::string cmCPackRPMGenerator::GetComponentInstallSuffix(
448 const std::string& componentName)
450 if (this->componentPackageMethod == ONE_PACKAGE_PER_COMPONENT) {
451 return componentName;
454 if (this->componentPackageMethod == ONE_PACKAGE) {
455 return { "ALL_COMPONENTS_IN_ONE" };
457 // We have to find the name of the COMPONENT GROUP
458 // the current COMPONENT belongs to.
459 std::string groupVar =
460 "CPACK_COMPONENT_" + cmSystemTools::UpperCase(componentName) + "_GROUP";
461 if (nullptr != this->GetOption(groupVar)) {
462 return *this->GetOption(groupVar);
464 return componentName;
467 std::string cmCPackRPMGenerator::GetComponentInstallDirNameSuffix(
468 const std::string& componentName)
470 return this->GetSanitizedDirOrFileName(
471 this->GetComponentInstallSuffix(componentName));