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 "cmCPackProductBuildGenerator.h"
9 #include "cmCPackComponentGroup.h"
10 #include "cmCPackLog.h"
11 #include "cmDuration.h"
12 #include "cmGeneratedFileStream.h"
13 #include "cmStringAlgorithms.h"
14 #include "cmSystemTools.h"
17 cmCPackProductBuildGenerator::cmCPackProductBuildGenerator()
19 this->componentPackageMethod
= ONE_PACKAGE
;
22 cmCPackProductBuildGenerator::~cmCPackProductBuildGenerator() = default;
24 int cmCPackProductBuildGenerator::PackageFiles()
27 // It is used! Is this an obsolete comment?
29 std::string packageDirFileName
=
30 this->GetOption("CPACK_TEMPORARY_DIRECTORY");
32 // Create the directory where component packages will be built.
33 std::string basePackageDir
=
34 cmStrCat(packageDirFileName
, "/Contents/Packages");
35 if (!cmsys::SystemTools::MakeDirectory(basePackageDir
.c_str())) {
36 cmCPackLogger(cmCPackLog::LOG_ERROR
,
37 "Problem creating component packages directory: "
38 << basePackageDir
<< std::endl
);
42 if (!this->Components
.empty()) {
43 std::map
<std::string
, cmCPackComponent
>::iterator compIt
;
44 for (compIt
= this->Components
.begin(); compIt
!= this->Components
.end();
46 std::string packageDir
= cmStrCat(toplevel
, '/', compIt
->first
);
47 if (!this->GenerateComponentPackage(basePackageDir
,
48 GetPackageName(compIt
->second
),
49 packageDir
, &compIt
->second
)) {
54 if (!this->GenerateComponentPackage(basePackageDir
,
55 this->GetOption("CPACK_PACKAGE_NAME"),
61 std::string resDir
= cmStrCat(packageDirFileName
, "/Contents");
63 if (this->IsSet("CPACK_PRODUCTBUILD_RESOURCES_DIR")) {
64 std::string userResDir
=
65 this->GetOption("CPACK_PRODUCTBUILD_RESOURCES_DIR");
67 if (!cmSystemTools::CopyADirectory(userResDir
, resDir
)) {
68 cmCPackLogger(cmCPackLog::LOG_ERROR
,
69 "Problem copying the resource files" << std::endl
);
74 // Copy or create all of the resource files we need.
75 if (!this->CopyCreateResourceFile("License", resDir
) ||
76 !this->CopyCreateResourceFile("ReadMe", resDir
) ||
77 !this->CopyCreateResourceFile("Welcome", resDir
)) {
78 cmCPackLogger(cmCPackLog::LOG_ERROR
,
79 "Problem copying the License, ReadMe and Welcome files"
84 // combine package(s) into a distribution
85 WriteDistributionFile(packageDirFileName
.c_str(), "PRODUCTBUILD");
86 std::ostringstream pkgCmd
;
88 std::string version
= this->GetOption("CPACK_PACKAGE_VERSION");
89 std::string productbuild
= this->GetOption("CPACK_COMMAND_PRODUCTBUILD");
90 std::string identityName
;
91 if (cmValue n
= this->GetOption("CPACK_PRODUCTBUILD_IDENTITY_NAME")) {
94 std::string keychainPath
;
95 if (cmValue p
= this->GetOption("CPACK_PRODUCTBUILD_KEYCHAIN_PATH")) {
98 std::string identifier
;
99 if (cmValue i
= this->GetOption("CPACK_PRODUCTBUILD_IDENTIFIER")) {
103 pkgCmd
<< productbuild
<< " --distribution \"" << packageDirFileName
104 << "/Contents/distribution.dist\""
106 << packageDirFileName
107 << "/Contents/Packages"
114 << (identifier
.empty()
116 : cmStrCat(" --identifier \"", identifier
, '"'))
117 << (identityName
.empty() ? std::string
{}
118 : cmStrCat(" --sign \"", identityName
, '"'))
119 << (keychainPath
.empty()
121 : cmStrCat(" --keychain \"", keychainPath
, '"'))
122 << " \"" << packageFileNames
[0] << '"';
125 return RunProductBuild(pkgCmd
.str());
128 int cmCPackProductBuildGenerator::InitializeInternal()
130 this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/Applications");
132 std::vector
<std::string
> no_paths
;
133 std::string program
=
134 cmSystemTools::FindProgram("pkgbuild", no_paths
, false);
135 if (program
.empty()) {
136 cmCPackLogger(cmCPackLog::LOG_ERROR
,
137 "Cannot find pkgbuild executable" << std::endl
);
140 this->SetOptionIfNotSet("CPACK_COMMAND_PKGBUILD", program
);
142 program
= cmSystemTools::FindProgram("productbuild", no_paths
, false);
143 if (program
.empty()) {
144 cmCPackLogger(cmCPackLog::LOG_ERROR
,
145 "Cannot find productbuild executable" << std::endl
);
148 this->SetOptionIfNotSet("CPACK_COMMAND_PRODUCTBUILD", program
);
150 return this->Superclass::InitializeInternal();
153 bool cmCPackProductBuildGenerator::RunProductBuild(const std::string
& command
)
155 std::string tmpFile
= cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"),
156 "/ProductBuildOutput.log");
158 cmCPackLogger(cmCPackLog::LOG_VERBOSE
, "Execute: " << command
<< std::endl
);
161 bool res
= cmSystemTools::RunSingleCommand(
162 command
, &output
, &output
, &retVal
, nullptr, this->GeneratorVerbose
,
164 cmCPackLogger(cmCPackLog::LOG_VERBOSE
, "Done running command" << std::endl
);
165 if (!res
|| retVal
) {
166 cmGeneratedFileStream
ofs(tmpFile
);
167 ofs
<< "# Run command: " << command
<< std::endl
168 << "# Output:" << std::endl
169 << output
<< std::endl
;
170 cmCPackLogger(cmCPackLog::LOG_ERROR
,
171 "Problem running command: " << command
<< std::endl
172 << "Please check " << tmpFile
173 << " for errors" << std::endl
);
179 bool cmCPackProductBuildGenerator::GenerateComponentPackage(
180 const std::string
& packageFileDir
, const std::string
& packageFileName
,
181 const std::string
& packageDir
, const cmCPackComponent
* component
)
183 std::string packageFile
= cmStrCat(packageFileDir
, '/', packageFileName
);
185 cmCPackLogger(cmCPackLog::LOG_OUTPUT
,
186 "- Building component package: " << packageFile
189 const char* comp_name
= component
? component
->Name
.c_str() : nullptr;
191 cmValue preflight
= this->GetComponentScript("PREFLIGHT", comp_name
);
192 cmValue postflight
= this->GetComponentScript("POSTFLIGHT", comp_name
);
194 std::string resDir
= packageFileDir
;
197 resDir
+= component
->Name
;
199 std::string scriptDir
= cmStrCat(resDir
, "/scripts");
201 if (!cmsys::SystemTools::MakeDirectory(scriptDir
.c_str())) {
202 cmCPackLogger(cmCPackLog::LOG_ERROR
,
203 "Problem creating installer directory: " << scriptDir
208 // if preflight, postflight, or postupgrade are set
209 // then copy them into the script directory and make
212 this->CopyInstallScript(scriptDir
, preflight
, "preinstall");
215 this->CopyInstallScript(scriptDir
, postflight
, "postinstall");
218 // The command that will be used to run ProductBuild
219 std::ostringstream pkgCmd
;
222 if (cmValue n
= this->GetOption("CPACK_PRODUCTBUILD_IDENTIFIER")) {
225 pkgId
= cmStrCat("com.", this->GetOption("CPACK_PACKAGE_VENDOR"), '.',
226 this->GetOption("CPACK_PACKAGE_NAME"));
230 pkgId
+= component
->Name
;
233 std::string version
= this->GetOption("CPACK_PACKAGE_VERSION");
234 std::string pkgbuild
= this->GetOption("CPACK_COMMAND_PKGBUILD");
235 std::string identityName
;
236 if (cmValue n
= this->GetOption("CPACK_PKGBUILD_IDENTITY_NAME")) {
239 std::string keychainPath
;
240 if (cmValue p
= this->GetOption("CPACK_PKGBUILD_KEYCHAIN_PATH")) {
244 pkgCmd
<< pkgbuild
<< " --root \"" << packageDir
255 " --install-location \"/\""
256 << (identityName
.empty() ? std::string
{}
257 : cmStrCat(" --sign \"", identityName
, "\""))
258 << (keychainPath
.empty()
260 : cmStrCat(" --keychain \"", keychainPath
, "\""))
261 << " \"" << packageFile
<< '"';
263 if (component
&& !component
->Plist
.empty()) {
264 pkgCmd
<< " --component-plist \"" << component
->Plist
<< "\"";
268 return RunProductBuild(pkgCmd
.str());
271 cmValue
cmCPackProductBuildGenerator::GetComponentScript(
272 const char* script
, const char* component_name
)
274 std::string scriptname
= cmStrCat("CPACK_", script
, '_');
275 if (component_name
) {
276 scriptname
+= cmSystemTools::UpperCase(component_name
);
279 scriptname
+= "SCRIPT";
281 return this->GetOption(scriptname
);