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 "cmCPackBundleGenerator.h"
4
5 #include <sstream>
6 #include <vector>
7
8 #include "cmCPackLog.h"
9 #include "cmStringAlgorithms.h"
10 #include "cmSystemTools.h"
11 #include "cmValue.h"
12
13 cmCPackBundleGenerator::cmCPackBundleGenerator() = default;
14
15 cmCPackBundleGenerator::~cmCPackBundleGenerator() = default;
16
InitializeInternal()17 int cmCPackBundleGenerator::InitializeInternal()
18 {
19 cmValue name = this->GetOption("CPACK_BUNDLE_NAME");
20 if (!name) {
21 cmCPackLogger(cmCPackLog::LOG_ERROR,
22 "CPACK_BUNDLE_NAME must be set to use the Bundle generator."
23 << std::endl);
24
25 return 0;
26 }
27
28 if (this->GetOption("CPACK_BUNDLE_APPLE_CERT_APP")) {
29 const std::string codesign_path = cmSystemTools::FindProgram(
30 "codesign", std::vector<std::string>(), false);
31
32 if (codesign_path.empty()) {
33 cmCPackLogger(cmCPackLog::LOG_ERROR,
34 "Cannot locate codesign command" << std::endl);
35 return 0;
36 }
37 this->SetOptionIfNotSet("CPACK_COMMAND_CODESIGN", codesign_path);
38 }
39
40 return this->Superclass::InitializeInternal();
41 }
42
GetPackagingInstallPrefix()43 const char* cmCPackBundleGenerator::GetPackagingInstallPrefix()
44 {
45 this->InstallPrefix = cmStrCat('/', this->GetOption("CPACK_BUNDLE_NAME"),
46 ".app/Contents/Resources");
47
48 return this->InstallPrefix.c_str();
49 }
50
ConstructBundle()51 int cmCPackBundleGenerator::ConstructBundle()
52 {
53
54 // Get required arguments ...
55 cmValue cpack_bundle_name = this->GetOption("CPACK_BUNDLE_NAME");
56 if (cpack_bundle_name->empty()) {
57 cmCPackLogger(cmCPackLog::LOG_ERROR,
58 "CPACK_BUNDLE_NAME must be set." << std::endl);
59
60 return 0;
61 }
62
63 cmValue cpack_bundle_plist = this->GetOption("CPACK_BUNDLE_PLIST");
64 if (cpack_bundle_plist->empty()) {
65 cmCPackLogger(cmCPackLog::LOG_ERROR,
66 "CPACK_BUNDLE_PLIST must be set." << std::endl);
67
68 return 0;
69 }
70
71 cmValue cpack_bundle_icon = this->GetOption("CPACK_BUNDLE_ICON");
72 if (cpack_bundle_icon->empty()) {
73 cmCPackLogger(cmCPackLog::LOG_ERROR,
74 "CPACK_BUNDLE_ICON must be set." << std::endl);
75
76 return 0;
77 }
78
79 // Get optional arguments ...
80 cmValue cpack_bundle_startup_command =
81 this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND");
82
83 // The staging directory contains everything that will end-up inside the
84 // final disk image ...
85 std::string const staging = toplevel;
86
87 std::ostringstream contents;
88 contents << staging << "/" << cpack_bundle_name << ".app/"
89 << "Contents";
90
91 std::ostringstream application;
92 application << contents.str() << "/"
93 << "MacOS";
94
95 std::ostringstream resources;
96 resources << contents.str() << "/"
97 << "Resources";
98
99 // Install a required, user-provided bundle metadata file ...
100 std::ostringstream plist_source;
101 plist_source << cpack_bundle_plist;
102
103 std::ostringstream plist_target;
104 plist_target << contents.str() << "/"
105 << "Info.plist";
106
107 if (!this->CopyFile(plist_source, plist_target)) {
108 cmCPackLogger(
109 cmCPackLog::LOG_ERROR,
110 "Error copying plist. Check the value of CPACK_BUNDLE_PLIST."
111 << std::endl);
112
113 return 0;
114 }
115
116 // Install a user-provided bundle icon ...
117 std::ostringstream icon_source;
118 icon_source << cpack_bundle_icon;
119
120 std::ostringstream icon_target;
121 icon_target << resources.str() << "/" << cpack_bundle_name << ".icns";
122
123 if (!this->CopyFile(icon_source, icon_target)) {
124 cmCPackLogger(
125 cmCPackLog::LOG_ERROR,
126 "Error copying bundle icon. Check the value of CPACK_BUNDLE_ICON."
127 << std::endl);
128
129 return 0;
130 }
131
132 // Optionally a user-provided startup command (could be an
133 // executable or a script) ...
134 if (!cpack_bundle_startup_command->empty()) {
135 std::ostringstream command_source;
136 command_source << cpack_bundle_startup_command;
137
138 std::ostringstream command_target;
139 command_target << application.str() << "/" << cpack_bundle_name;
140
141 if (!this->CopyFile(command_source, command_target)) {
142 cmCPackLogger(cmCPackLog::LOG_ERROR,
143 "Error copying startup command. "
144 " Check the value of CPACK_BUNDLE_STARTUP_COMMAND."
145 << std::endl);
146
147 return 0;
148 }
149
150 cmSystemTools::SetPermissions(command_target.str().c_str(), 0777);
151 }
152
153 return 1;
154 }
155
PackageFiles()156 int cmCPackBundleGenerator::PackageFiles()
157 {
158 if (!this->ConstructBundle()) {
159 return 0;
160 }
161
162 if (!this->SignBundle(toplevel)) {
163 return 0;
164 }
165
166 return this->CreateDMG(toplevel, packageFileNames[0]);
167 }
168
SupportsComponentInstallation() const169 bool cmCPackBundleGenerator::SupportsComponentInstallation() const
170 {
171 return false;
172 }
173
SignBundle(const std::string & src_dir)174 int cmCPackBundleGenerator::SignBundle(const std::string& src_dir)
175 {
176 cmValue cpack_apple_cert_app =
177 this->GetOption("CPACK_BUNDLE_APPLE_CERT_APP");
178
179 // codesign the application.
180 if (!cpack_apple_cert_app->empty()) {
181 std::string output;
182 std::string bundle_path;
183 bundle_path =
184 cmStrCat(src_dir, '/', this->GetOption("CPACK_BUNDLE_NAME"), ".app");
185
186 // A list of additional files to sign, ie. frameworks and plugins.
187 const std::string sign_parameter =
188 this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_PARAMETER")
189 ? *this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_PARAMETER")
190 : "--deep -f";
191
192 cmValue sign_files = this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_FILES");
193
194 std::vector<std::string> relFiles = cmExpandedList(sign_files);
195
196 // sign the files supplied by the user, ie. frameworks.
197 for (auto const& file : relFiles) {
198 std::ostringstream temp_sign_file_cmd;
199 temp_sign_file_cmd << this->GetOption("CPACK_COMMAND_CODESIGN");
200 temp_sign_file_cmd << " " << sign_parameter << " -s \""
201 << cpack_apple_cert_app;
202 temp_sign_file_cmd << "\" -i ";
203 temp_sign_file_cmd << this->GetOption("CPACK_APPLE_BUNDLE_ID");
204 temp_sign_file_cmd << " \"";
205 temp_sign_file_cmd << bundle_path;
206 temp_sign_file_cmd << file << "\"";
207
208 if (!this->RunCommand(temp_sign_file_cmd, &output)) {
209 cmCPackLogger(cmCPackLog::LOG_ERROR,
210 "Error signing file:" << bundle_path << file << std::endl
211 << output << std::endl);
212
213 return 0;
214 }
215 }
216
217 // sign main binary
218 std::ostringstream temp_sign_binary_cmd;
219 temp_sign_binary_cmd << this->GetOption("CPACK_COMMAND_CODESIGN");
220 temp_sign_binary_cmd << " " << sign_parameter << " -s \""
221 << cpack_apple_cert_app;
222 temp_sign_binary_cmd << "\" \"" << bundle_path << "\"";
223
224 if (!this->RunCommand(temp_sign_binary_cmd, &output)) {
225 cmCPackLogger(cmCPackLog::LOG_ERROR,
226 "Error signing the application binary." << std::endl
227 << output
228 << std::endl);
229
230 return 0;
231 }
232
233 // sign app bundle
234 std::ostringstream temp_codesign_cmd;
235 temp_codesign_cmd << this->GetOption("CPACK_COMMAND_CODESIGN");
236 temp_codesign_cmd << " " << sign_parameter << " -s \""
237 << cpack_apple_cert_app << "\"";
238 if (this->GetOption("CPACK_BUNDLE_APPLE_ENTITLEMENTS")) {
239 temp_codesign_cmd << " --entitlements ";
240 temp_codesign_cmd << this->GetOption("CPACK_BUNDLE_APPLE_ENTITLEMENTS");
241 }
242 temp_codesign_cmd << " \"" << bundle_path << "\"";
243
244 if (!this->RunCommand(temp_codesign_cmd, &output)) {
245 cmCPackLogger(cmCPackLog::LOG_ERROR,
246 "Error signing the application package." << std::endl
247 << output
248 << std::endl);
249
250 return 0;
251 }
252
253 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
254 "- Application has been codesigned" << std::endl);
255 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
256 (this->GetOption("CPACK_BUNDLE_APPLE_ENTITLEMENTS")
257 ? "with entitlement sandboxing"
258 : "without entitlement sandboxing")
259 << std::endl);
260 }
261
262 return 1;
263 }
264