CMake Nightly Date Stamp
[kiteware-cmake.git] / Source / CPack / cmCPackProductBuildGenerator.cxx
blobae3c50e543a7f1811330f696c28d37076e3ad615
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"
5 #include <cstddef>
6 #include <map>
7 #include <sstream>
9 #include "cmCPackComponentGroup.h"
10 #include "cmCPackLog.h"
11 #include "cmDuration.h"
12 #include "cmGeneratedFileStream.h"
13 #include "cmStringAlgorithms.h"
14 #include "cmSystemTools.h"
15 #include "cmValue.h"
17 cmCPackProductBuildGenerator::cmCPackProductBuildGenerator()
19 this->componentPackageMethod = ONE_PACKAGE;
22 cmCPackProductBuildGenerator::~cmCPackProductBuildGenerator() = default;
24 int cmCPackProductBuildGenerator::PackageFiles()
26 // TODO: Use toplevel
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);
39 return 0;
42 if (!this->Components.empty()) {
43 std::map<std::string, cmCPackComponent>::iterator compIt;
44 for (compIt = this->Components.begin(); compIt != this->Components.end();
45 ++compIt) {
46 std::string packageDir = cmStrCat(toplevel, '/', compIt->first);
47 if (!this->GenerateComponentPackage(basePackageDir,
48 GetPackageName(compIt->second),
49 packageDir, &compIt->second)) {
50 return 0;
53 } else {
54 if (!this->GenerateComponentPackage(basePackageDir,
55 this->GetOption("CPACK_PACKAGE_NAME"),
56 toplevel, nullptr)) {
57 return 0;
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);
70 return 0;
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"
80 << std::endl);
81 return 0;
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")) {
92 identityName = n;
94 std::string keychainPath;
95 if (cmValue p = this->GetOption("CPACK_PRODUCTBUILD_KEYCHAIN_PATH")) {
96 keychainPath = p;
98 std::string identifier;
99 if (cmValue i = this->GetOption("CPACK_PRODUCTBUILD_IDENTIFIER")) {
100 identifier = i;
103 pkgCmd << productbuild << " --distribution \"" << packageDirFileName
104 << "/Contents/distribution.dist\""
105 " --package-path \""
106 << packageDirFileName
107 << "/Contents/Packages"
108 "\""
109 " --resources \""
110 << resDir
111 << "\""
112 " --version \""
113 << version << "\""
114 << (identifier.empty()
115 ? std::string{}
116 : cmStrCat(" --identifier \"", identifier, '"'))
117 << (identityName.empty() ? std::string{}
118 : cmStrCat(" --sign \"", identityName, '"'))
119 << (keychainPath.empty()
120 ? std::string{}
121 : cmStrCat(" --keychain \"", keychainPath, '"'))
122 << " \"" << packageFileNames[0] << '"';
124 // Run ProductBuild
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);
138 return 0;
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);
146 return 0;
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);
159 std::string output;
160 int retVal = 1;
161 bool res = cmSystemTools::RunSingleCommand(
162 command, &output, &output, &retVal, nullptr, this->GeneratorVerbose,
163 cmDuration::zero());
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);
174 return false;
176 return true;
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
187 << std::endl);
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;
195 if (component) {
196 resDir += '/';
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
204 << std::endl);
205 return false;
208 // if preflight, postflight, or postupgrade are set
209 // then copy them into the script directory and make
210 // them executable
211 if (preflight) {
212 this->CopyInstallScript(scriptDir, preflight, "preinstall");
214 if (postflight) {
215 this->CopyInstallScript(scriptDir, postflight, "postinstall");
218 // The command that will be used to run ProductBuild
219 std::ostringstream pkgCmd;
221 std::string pkgId;
222 if (cmValue n = this->GetOption("CPACK_PRODUCTBUILD_IDENTIFIER")) {
223 pkgId = n;
224 } else {
225 pkgId = cmStrCat("com.", this->GetOption("CPACK_PACKAGE_VENDOR"), '.',
226 this->GetOption("CPACK_PACKAGE_NAME"));
228 if (component) {
229 pkgId += '.';
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")) {
237 identityName = n;
239 std::string keychainPath;
240 if (cmValue p = this->GetOption("CPACK_PKGBUILD_KEYCHAIN_PATH")) {
241 keychainPath = p;
244 pkgCmd << pkgbuild << " --root \"" << packageDir
245 << "\""
246 " --identifier \""
247 << pkgId
248 << "\""
249 " --scripts \""
250 << scriptDir
251 << "\""
252 " --version \""
253 << version
254 << "\""
255 " --install-location \"/\""
256 << (identityName.empty() ? std::string{}
257 : cmStrCat(" --sign \"", identityName, "\""))
258 << (keychainPath.empty()
259 ? std::string{}
260 : cmStrCat(" --keychain \"", keychainPath, "\""))
261 << " \"" << packageFile << '"';
263 if (component && !component->Plist.empty()) {
264 pkgCmd << " --component-plist \"" << component->Plist << "\"";
267 // Run ProductBuild
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);
277 scriptname += '_';
279 scriptname += "SCRIPT";
281 return this->GetOption(scriptname);