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"
12 #include "cmCPackComponentGroup.h"
13 #include "cmCPackGenerator.h"
14 #include "cmCPackLog.h"
15 #include "cmCryptoHash.h"
16 #include "cmStringAlgorithms.h"
17 #include "cmSystemTools.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"));
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
));
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
68 std::string sanitizedPkgNameSuffix
=
69 this->GetSanitizedDirOrFileName(packageName
, false);
70 // Determine the sanitized packaging directory-name that can be used on the
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
);
109 std::string
cmCPackRPMGenerator::GetSanitizedDirOrFileName(
110 const std::string
& name
, bool isFullName
) const
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
)
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;
141 std::map
<std::string
, cmCPackComponent
>::iterator compIt
;
142 for (compIt
= this->Components
.begin(); compIt
!= this->Components
.end();
144 std::string
component(compIt
->first
);
145 std::transform(component
.begin(), component
.end(), component
.begin(),
148 if (this->IsOn("CPACK_RPM_" + compIt
->first
+ "_DEBUGINFO_PACKAGE") ||
149 this->IsOn("CPACK_RPM_" + component
+ "_DEBUGINFO_PACKAGE")) {
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(),
162 if (this->IsOn("CPACK_RPM_" + compGIt
->first
+ "_DEBUGINFO_PACKAGE") ||
163 this->IsOn("CPACK_RPM_" + component
+ "_DEBUGINFO_PACKAGE")) {
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")) {
191 cmCPackLogger(cmCPackLog::LOG_VERBOSE
,
193 << "CPACK_RPM_DEBUGINFO_PACKAGE because "
194 << "CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE is set but "
196 << "CPACK_RPM_<component>_DEBUGINFO_PACKAGE variables "
197 << "are set." << std::endl
);
198 this->SetOption("CPACK_RPM_DEBUGINFO_PACKAGE", "ON");
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.
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(),
223 if (mainComponentUpper
== component
) {
224 // main component will be handled last
225 mainCompGIt
= compGIt
;
229 cmCPackLogger(cmCPackLog::LOG_VERBOSE
,
230 "Packaging component group: " << compGIt
->first
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();
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(),
245 if (mainComponentUpper
== component
) {
246 // main component will be handled last
252 cmCPackLog::LOG_VERBOSE
,
254 << compIt
->second
.Name
255 << "> does not belong to any group, package it separately."
257 retval
&= this->PackageOnePack(initialTopLevel
, compIt
->first
);
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
);
269 cmCPackLogger(cmCPackLog::LOG_ERROR
,
270 "CPACK_RPM_MAIN_COMPONENT set"
271 << " to non existing component.\n");
276 // CPACK_COMPONENTS_IGNORE_GROUPS is set
277 // We build 1 package per component
279 auto mainCompIt
= this->Components
.end();
281 std::map
<std::string
, cmCPackComponent
>::iterator compIt
;
282 for (compIt
= this->Components
.begin(); compIt
!= this->Components
.end();
284 std::string
component(compIt
->first
);
285 std::transform(component
.begin(), component
.end(), component
.begin(),
288 if (mainComponentUpper
== component
) {
289 // main component will be handled last
294 retval
&= this->PackageOnePack(initialTopLevel
, compIt
->first
);
298 this->SetOption("GENERATE_SPEC_PARTS", "OFF");
300 if (mainCompIt
!= this->Components
.end()) {
301 retval
&= this->PackageOnePack(initialTopLevel
, mainCompIt
->first
);
303 cmCPackLogger(cmCPackLog::LOG_ERROR
,
304 "CPACK_RPM_MAIN_COMPONENT set"
305 << " to non existing component.\n");
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.
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
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();
327 // Does the component belong to a group?
328 if (compIt
->second
.Group
== nullptr) {
330 cmCPackLog::LOG_VERBOSE
,
332 << compIt
->second
.Name
333 << "> does not belong to any group, package it separately."
335 retval
&= this->PackageOnePack(initialTopLevel
, compIt
->first
);
339 // CPACK_COMPONENTS_IGNORE_GROUPS is set
340 // We build 1 package per component
342 std::map
<std::string
, cmCPackComponent
>::iterator compIt
;
343 for (compIt
= this->Components
.begin(); compIt
!= this->Components
.end();
345 retval
&= this->PackageOnePack(initialTopLevel
, compIt
->first
);
350 cmCPackLog::LOG_ERROR
,
351 "CPACK_RPM_MAIN_COMPONENT not set but"
352 << " it is mandatory with CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE"
358 this->AddGeneratedPackageNames();
365 int cmCPackRPMGenerator::PackageComponentsAllInOne(
366 const std::string
& compInstDirName
)
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)"
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();
410 cmCPackLogger(cmCPackLog::LOG_ERROR
,
411 "Error while execution CPackRPM.cmake" << std::endl
);
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
));