]> git.saurik.com Git - apple/dyld.git/blob - dyld3/shared-cache/dyld_shared_cache_builder.mm
dyld-635.2.tar.gz
[apple/dyld.git] / dyld3 / shared-cache / dyld_shared_cache_builder.mm
1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
2 *
3 * Copyright (c) 2016 Apple Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/mman.h>
28 #include <sys/resource.h>
29 #include <mach/mach.h>
30 #include <mach/mach_time.h>
31 #include <limits.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <math.h>
36 #include <fcntl.h>
37 #include <dlfcn.h>
38 #include <signal.h>
39 #include <errno.h>
40 #include <sys/uio.h>
41 #include <unistd.h>
42 #include <sys/param.h>
43 #include <sys/sysctl.h>
44 #include <sys/resource.h>
45 #include <dirent.h>
46 #include <libgen.h>
47 #include <pthread.h>
48 #include <fts.h>
49
50 #include <vector>
51 #include <array>
52 #include <set>
53 #include <map>
54 #include <unordered_set>
55 #include <algorithm>
56
57 #include <spawn.h>
58
59 #include <Bom/Bom.h>
60
61 #include "Manifest.h"
62 #include "Diagnostics.h"
63 #include "DyldSharedCache.h"
64 #include "BuilderUtils.h"
65 #include "FileUtils.h"
66 #include "JSONWriter.h"
67 #include "StringUtils.h"
68 #include "mrm_shared_cache_builder.h"
69
70 #if !__has_feature(objc_arc)
71 #error The use of libdispatch in this files requires it to be compiled with ARC in order to avoid leaks
72 #endif
73
74 extern char** environ;
75
76 static dispatch_queue_t build_queue;
77
78 int runCommandAndWait(Diagnostics& diags, const char* args[])
79 {
80 pid_t pid;
81 int status;
82 int res = posix_spawn(&pid, args[0], nullptr, nullptr, (char**)args, environ);
83 if (res != 0)
84 diags.error("Failed to spawn %s: %s (%d)", args[0], strerror(res), res);
85
86 do {
87 res = waitpid(pid, &status, 0);
88 } while (res == -1 && errno == EINTR);
89 if (res != -1) {
90 if (WIFEXITED(status)) {
91 res = WEXITSTATUS(status);
92 } else {
93 res = -1;
94 }
95 }
96
97 return res;
98 }
99
100 void processRoots(Diagnostics& diags, std::set<std::string>& roots, const char *tempRootsDir)
101 {
102 std::set<std::string> processedRoots;
103 struct stat sb;
104 int res = 0;
105 const char* args[8];
106
107 for (const auto& root : roots) {
108 res = stat(root.c_str(), &sb);
109
110 if (res == 0 && S_ISDIR(sb.st_mode)) {
111 processedRoots.insert(root);
112 continue;
113 }
114
115 char tempRootDir[MAXPATHLEN];
116 strlcpy(tempRootDir, tempRootsDir, MAXPATHLEN);
117 strlcat(tempRootDir, "/XXXXXXXX", MAXPATHLEN);
118 mkdtemp(tempRootDir);
119
120 if (endsWith(root, ".cpio") || endsWith(root, ".cpio.gz") || endsWith(root, ".cpgz") || endsWith(root, ".cpio.bz2") || endsWith(root, ".cpbz2") || endsWith(root, ".pax") || endsWith(root, ".pax.gz") || endsWith(root, ".pgz") || endsWith(root, ".pax.bz2") || endsWith(root, ".pbz2")) {
121 args[0] = (char*)"/usr/bin/ditto";
122 args[1] = (char*)"-x";
123 args[2] = (char*)root.c_str();
124 args[3] = tempRootDir;
125 args[4] = nullptr;
126 } else if (endsWith(root, ".tar")) {
127 args[0] = (char*)"/usr/bin/tar";
128 args[1] = (char*)"xf";
129 args[2] = (char*)root.c_str();
130 args[3] = (char*)"-C";
131 args[4] = tempRootDir;
132 args[5] = nullptr;
133 } else if (endsWith(root, ".tar.gz") || endsWith(root, ".tgz")) {
134 args[0] = (char*)"/usr/bin/tar";
135 args[1] = (char*)"xzf";
136 args[2] = (char*)root.c_str();
137 args[3] = (char*)"-C";
138 args[4] = tempRootDir;
139 args[5] = nullptr;
140 } else if (endsWith(root, ".tar.bz2")
141 || endsWith(root, ".tbz2")
142 || endsWith(root, ".tbz")) {
143 args[0] = (char*)"/usr/bin/tar";
144 args[1] = (char*)"xjf";
145 args[2] = (char*)root.c_str();
146 args[3] = (char*)"-C";
147 args[4] = tempRootDir;
148 args[5] = nullptr;
149 } else if (endsWith(root, ".xar")) {
150 args[0] = (char*)"/usr/bin/xar";
151 args[1] = (char*)"-xf";
152 args[2] = (char*)root.c_str();
153 args[3] = (char*)"-C";
154 args[4] = tempRootDir;
155 args[5] = nullptr;
156 } else if (endsWith(root, ".zip")) {
157 args[0] = (char*)"/usr/bin/ditto";
158 args[1] = (char*)"-xk";
159 args[2] = (char*)root.c_str();
160 args[3] = tempRootDir;
161 args[4] = nullptr;
162 } else {
163 diags.error("unknown archive type: %s", root.c_str());
164 continue;
165 }
166
167 if (res != runCommandAndWait(diags, args)) {
168 fprintf(stderr, "Could not expand archive %s: %s (%d)", root.c_str(), strerror(res), res);
169 exit(-1);
170 }
171 for (auto& existingRoot : processedRoots) {
172 if (existingRoot == tempRootDir)
173 return;
174 }
175
176 processedRoots.insert(tempRootDir);
177 }
178
179 roots = processedRoots;
180 }
181
182 bool writeRootList(const std::string& dstRoot, const std::set<std::string>& roots)
183 {
184 mkpath_np(dstRoot.c_str(), 0755);
185 if (roots.size() == 0)
186 return false;
187
188 std::string rootFile = dstRoot + "/roots.txt";
189 FILE* froots = ::fopen(rootFile.c_str(), "w");
190 if (froots == NULL)
191 return false;
192
193 for (auto& root : roots) {
194 fprintf(froots, "%s\n", root.c_str());
195 }
196
197 ::fclose(froots);
198 return true;
199 }
200
201 BOMCopierCopyOperation filteredCopyExcludingPaths(BOMCopier copier, const char* path, BOMFSObjType type, off_t size)
202 {
203 std::string absolutePath = &path[1];
204 void *userData = BOMCopierUserData(copier);
205 std::set<std::string> *cachePaths = (std::set<std::string>*)userData;
206 if (cachePaths->count(absolutePath)) {
207 return BOMCopierSkipFile;
208 }
209 return BOMCopierContinue;
210 }
211
212 BOMCopierCopyOperation filteredCopyIncludingPaths(BOMCopier copier, const char* path, BOMFSObjType type, off_t size)
213 {
214 std::string absolutePath = &path[1];
215 void *userData = BOMCopierUserData(copier);
216 std::set<std::string> *cachePaths = (std::set<std::string>*)userData;
217 for (const std::string& cachePath : *cachePaths) {
218 if (startsWith(cachePath, absolutePath))
219 return BOMCopierContinue;
220 }
221 if (cachePaths->count(absolutePath)) {
222 return BOMCopierContinue;
223 }
224 return BOMCopierSkipFile;
225 }
226
227 static std::string dispositionToString(Disposition disposition) {
228 switch (disposition) {
229 case Unknown:
230 return "Unknown";
231 case InternalDevelopment:
232 return "InternalDevelopment";
233 case Customer:
234 return "Customer";
235 case InternalMinDevelopment:
236 return "InternalMinDevelopment";
237 }
238 }
239
240 static std::string platformToString(Platform platform) {
241 switch (platform) {
242 case unknown:
243 return "unknown";
244 case macOS:
245 return "macOS";
246 case iOS:
247 return "iOS";
248 case tvOS:
249 return "tvOS";
250 case watchOS:
251 return "watchOS";
252 case bridgeOS:
253 return "bridgeOS";
254 case iOSMac:
255 return "iOSMac";
256 case iOS_simulator:
257 return "iOS_simulator";
258 case tvOS_simulator:
259 return "tvOS_simulator";
260 case watchOS_simulator:
261 return "watchOS_simulator";
262 }
263 }
264
265 static dyld3::json::Node getBuildOptionsNode(BuildOptions_v1 buildOptions) {
266 dyld3::json::Node buildOptionsNode;
267 buildOptionsNode.map["version"].value = dyld3::json::decimal(buildOptions.version);
268 buildOptionsNode.map["updateName"].value = buildOptions.updateName;
269 buildOptionsNode.map["deviceName"].value = buildOptions.deviceName;
270 buildOptionsNode.map["disposition"].value = dispositionToString(buildOptions.disposition);
271 buildOptionsNode.map["platform"].value = platformToString(buildOptions.platform);
272 for (unsigned i = 0; i != buildOptions.numArchs; ++i) {
273 dyld3::json::Node archNode;
274 archNode.value = buildOptions.archs[i];
275 buildOptionsNode.map["archs"].array.push_back(archNode);
276 }
277 buildOptionsNode.map["verboseDiagnostics"].value = buildOptions.verboseDiagnostics ? "true" : "false";
278 return buildOptionsNode;
279 }
280
281 int main(int argc, const char* argv[])
282 {
283 @autoreleasepool {
284 __block Diagnostics diags;
285 std::set<std::string> roots;
286 std::string dylibCacheDir;
287 std::string artifactDir;
288 std::string release;
289 bool emitDevCaches = true;
290 bool emitElidedDylibs = true;
291 bool listConfigs = false;
292 bool copyRoots = false;
293 bool debug = false;
294 bool useMRM = false;
295 std::string dstRoot;
296 std::string emitJSONPath;
297 std::string configuration;
298 std::string resultPath;
299 std::string baselineDifferenceResultPath;
300 bool baselineCopyRoots = false;
301 char* tempRootsDir = strdup("/tmp/dyld_shared_cache_builder.XXXXXX");
302
303 mkdtemp(tempRootsDir);
304
305 for (int i = 1; i < argc; ++i) {
306 const char* arg = argv[i];
307 if (arg[0] == '-') {
308 if (strcmp(arg, "-debug") == 0) {
309 diags = Diagnostics(true);
310 debug = true;
311 } else if (strcmp(arg, "-list_configs") == 0) {
312 listConfigs = true;
313 } else if (strcmp(arg, "-root") == 0) {
314 roots.insert(realPath(argv[++i]));
315 } else if (strcmp(arg, "-copy_roots") == 0) {
316 copyRoots = true;
317 } else if (strcmp(arg, "-dylib_cache") == 0) {
318 dylibCacheDir = realPath(argv[++i]);
319 } else if (strcmp(arg, "-artifact") == 0) {
320 artifactDir = realPath(argv[++i]);
321 } else if (strcmp(arg, "-no_development_cache") == 0) {
322 emitDevCaches = false;
323 } else if (strcmp(arg, "-no_overflow_dylibs") == 0) {
324 emitElidedDylibs = false;
325 } else if (strcmp(arg, "-development_cache") == 0) {
326 emitDevCaches = true;
327 } else if (strcmp(arg, "-overflow_dylibs") == 0) {
328 emitElidedDylibs = true;
329 } else if (strcmp(arg, "-mrm") == 0) {
330 useMRM = true;
331 } else if (strcmp(arg, "-emit_json") == 0) {
332 emitJSONPath = realPath(argv[++i]);
333 } else if (strcmp(arg, "-dst_root") == 0) {
334 dstRoot = realPath(argv[++i]);
335 } else if (strcmp(arg, "-release") == 0) {
336 release = argv[++i];
337 } else if (strcmp(arg, "-results") == 0) {
338 resultPath = realPath(argv[++i]);
339 } else if (strcmp(arg, "-baseline_diff_results") == 0) {
340 baselineDifferenceResultPath = realPath(argv[++i]);
341 } else if (strcmp(arg, "-baseline_copy_roots") == 0) {
342 baselineCopyRoots = true;
343 } else {
344 //usage();
345 fprintf(stderr, "unknown option: %s\n", arg);
346 exit(-1);
347 }
348 } else {
349 if (!configuration.empty()) {
350 fprintf(stderr, "You may only specify one configuration\n");
351 exit(-1);
352 }
353 configuration = argv[i];
354 }
355 }
356
357 time_t mytime = time(0);
358 fprintf(stderr, "Started: %s", asctime(localtime(&mytime)));
359 writeRootList(dstRoot, roots);
360 processRoots(diags, roots, tempRootsDir);
361
362 struct rlimit rl = { OPEN_MAX, OPEN_MAX };
363 (void)setrlimit(RLIMIT_NOFILE, &rl);
364
365 if (dylibCacheDir.empty() && artifactDir.empty() && release.empty()) {
366 fprintf(stderr, "you must specify either -dylib_cache, -artifact or -release\n");
367 exit(-1);
368 } else if (!dylibCacheDir.empty() && !release.empty()) {
369 fprintf(stderr, "you may not use -dylib_cache and -release at the same time\n");
370 exit(-1);
371 } else if (!dylibCacheDir.empty() && !artifactDir.empty()) {
372 fprintf(stderr, "you may not use -dylib_cache and -artifact at the same time\n");
373 exit(-1);
374 }
375
376 if ((configuration.empty() || dstRoot.empty()) && !listConfigs) {
377 fprintf(stderr, "Must specify a configuration and a valid -dst_root OR -list_configs\n");
378 exit(-1);
379 }
380
381 if (!baselineDifferenceResultPath.empty() && (roots.size() > 1)) {
382 fprintf(stderr, "Cannot use -baseline_diff_results with more that one -root\n");
383 exit(-1);
384 }
385
386 if (!artifactDir.empty()) {
387 // Find the dylib cache dir from inside the artifact dir
388 struct stat stat_buf;
389 if (stat(artifactDir.c_str(), &stat_buf) != 0) {
390 fprintf(stderr, "Could not find artifact path '%s'\n", artifactDir.c_str());
391 exit(-1);
392 }
393 std::string dir = artifactDir + "/AppleInternal/Developer/DylibCaches";
394 if (stat(dir.c_str(), &stat_buf) != 0) {
395 fprintf(stderr, "Could not find artifact path '%s'\n", dir.c_str());
396 exit(-1);
397 }
398
399 if (!release.empty()) {
400 // Use the given release
401 dylibCacheDir = dir + "/" + release + ".dlc";
402 } else {
403 // Find a release directory
404 __block std::vector<std::string> subDirectories;
405 iterateDirectoryTree("", dir, ^(const std::string& dirPath) {
406 subDirectories.push_back(dirPath);
407 return false;
408 }, nullptr, false, false);
409
410 if (subDirectories.empty()) {
411 fprintf(stderr, "Could not find dlc subdirectories inside '%s'\n", dir.c_str());
412 exit(-1);
413 }
414
415 if (subDirectories.size() > 1) {
416 fprintf(stderr, "Found too many subdirectories inside artifact path '%s'. Use -release to select one\n", dir.c_str());
417 exit(-1);
418 }
419
420 dylibCacheDir = subDirectories.front();
421 }
422 }
423
424 if (dylibCacheDir.empty()) {
425 dylibCacheDir = std::string("/AppleInternal/Developer/DylibCaches/") + release + ".dlc";
426 }
427
428 //Move into the dir so we can use relative path manifests
429 chdir(dylibCacheDir.c_str());
430
431 dispatch_async(dispatch_get_main_queue(), ^{
432 // If we only want a list of configuations, then tell the manifest to only parse the data and not
433 // actually get all the macho's.
434 bool onlyParseManifest = listConfigs && configuration.empty();
435 auto manifest = dyld3::Manifest(diags, dylibCacheDir + "/Manifest.plist", roots, onlyParseManifest);
436
437 if (manifest.build().empty()) {
438 fprintf(stderr, "No manifest found at '%s/Manifest.plist'\n", dylibCacheDir.c_str());
439 exit(-1);
440 }
441 fprintf(stderr, "Building Caches for %s\n", manifest.build().c_str());
442
443 if (listConfigs) {
444 manifest.forEachConfiguration([](const std::string& configName) {
445 printf("%s\n", configName.c_str());
446 });
447 // If we weren't passed a configuration then exit
448 if (configuration.empty())
449 exit(0);
450 }
451
452 if (!manifest.filterForConfig(configuration)) {
453 fprintf(stderr, "No config %s. Please run with -list_configs to see configurations available for this %s.\n",
454 configuration.c_str(), manifest.build().c_str());
455 exit(-1);
456 }
457
458 (void)mkpath_np((dstRoot + "/System/Library/Caches/com.apple.dyld/").c_str(), 0755);
459 bool cacheBuildSuccess = false;
460 if (useMRM) {
461
462 FILE* jsonFile = nullptr;
463 if (!emitJSONPath.empty()) {
464 jsonFile = fopen(emitJSONPath.c_str(), "w");
465 if (!jsonFile) {
466 diags.verbose("can't open file '%s', errno=%d\n", emitJSONPath.c_str(), errno);
467 return;
468 }
469 }
470 dyld3::json::Node buildInvocationNode;
471
472 // Find the archs for the configuration we want.
473 __block std::set<std::string> validArchs;
474 manifest.configuration(configuration).forEachArchitecture(^(const std::string& path) {
475 validArchs.insert(path);
476 });
477
478 if (validArchs.size() != 1) {
479 fprintf(stderr, "MRM doesn't support more than one arch per configuration: %s\n",
480 configuration.c_str());
481 exit(-1);
482 }
483
484 const char* archs[validArchs.size()];
485 uint64_t archIndex = 0;
486 for (const std::string& arch : validArchs) {
487 archs[archIndex++] = arch.c_str();
488 }
489
490 BuildOptions_v1 buildOptions;
491 buildOptions.version = 1;
492 buildOptions.updateName = manifest.build().c_str();
493 buildOptions.deviceName = configuration.c_str();
494 buildOptions.disposition = Disposition::Unknown;
495 buildOptions.platform = (Platform)manifest.platform();
496 buildOptions.archs = archs;
497 buildOptions.numArchs = validArchs.size();
498 buildOptions.verboseDiagnostics = debug;
499 buildOptions.isLocallyBuiltCache = true;
500
501 __block struct SharedCacheBuilder* sharedCacheBuilder = createSharedCacheBuilder(&buildOptions);
502 buildInvocationNode.map["build-options"] = getBuildOptionsNode(buildOptions);
503
504 std::set<std::string> requiredBinaries = {
505 "/usr/lib/libSystem.B.dylib"
506 };
507
508 // Get the file data for every MachO in the BOM.
509 __block dyld3::json::Node filesNode;
510 __block std::vector<std::pair<const void*, size_t>> mappedFiles;
511 manifest.forEachMachO(configuration, ^(const std::string &buildPath, const std::string &runtimePath, const std::string &arch, bool shouldBeExcludedIfLeaf) {
512
513 // Filter based on arch as the Manifest adds the file once for each UUID.
514 if (!validArchs.count(arch))
515 return;
516
517 struct stat stat_buf;
518 int fd = ::open(buildPath.c_str(), O_RDONLY, 0);
519 if (fd == -1) {
520 diags.verbose("can't open file '%s', errno=%d\n", buildPath.c_str(), errno);
521 return;
522 }
523
524 if (fstat(fd, &stat_buf) == -1) {
525 diags.verbose("can't stat open file '%s', errno=%d\n", buildPath.c_str(), errno);
526 ::close(fd);
527 return;
528 }
529
530 const void* buffer = mmap(NULL, (size_t)stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
531 if (buffer == MAP_FAILED) {
532 diags.verbose("mmap() for file at %s failed, errno=%d\n", buildPath.c_str(), errno);
533 ::close(fd);
534 }
535 ::close(fd);
536
537 mappedFiles.emplace_back(buffer, (size_t)stat_buf.st_size);
538 FileFlags fileFlags = FileFlags::NoFlags;
539 if (requiredBinaries.count(runtimePath))
540 fileFlags = FileFlags::RequiredClosure;
541 addFile(sharedCacheBuilder, runtimePath.c_str(), (uint8_t*)buffer, (size_t)stat_buf.st_size, fileFlags);
542
543 dyld3::json::Node fileNode;
544 fileNode.map["path"].value = runtimePath;
545 fileNode.map["flags"].value = "NoFlags";
546 filesNode.array.push_back(fileNode);
547 });
548
549 __block dyld3::json::Node symlinksNode;
550 manifest.forEachSymlink(configuration, ^(const std::string &fromPath, const std::string &toPath) {
551 addSymlink(sharedCacheBuilder, fromPath.c_str(), toPath.c_str());
552
553 dyld3::json::Node symlinkNode;
554 symlinkNode.map["from-path"].value = fromPath;
555 symlinkNode.map["to-path"].value = toPath;
556 symlinksNode.array.push_back(symlinkNode);
557 });
558
559 buildInvocationNode.map["symlinks"] = symlinksNode;
560
561 std::string orderFileData;
562 if (!manifest.dylibOrderFile().empty()) {
563 orderFileData = loadOrderFile(manifest.dylibOrderFile());
564 if (!orderFileData.empty()) {
565 addFile(sharedCacheBuilder, "*order file data*", (uint8_t*)orderFileData.data(), orderFileData.size(), FileFlags::DylibOrderFile);
566 dyld3::json::Node fileNode;
567 fileNode.map["path"].value = manifest.dylibOrderFile();
568 fileNode.map["flags"].value = "DylibOrderFile";
569 filesNode.array.push_back(fileNode);
570 }
571 }
572
573 std::string dirtyDataOrderFileData;
574 if (!manifest.dirtyDataOrderFile().empty()) {
575 dirtyDataOrderFileData = loadOrderFile(manifest.dirtyDataOrderFile());
576 if (!dirtyDataOrderFileData.empty()) {
577 addFile(sharedCacheBuilder, "*dirty data order file data*", (uint8_t*)dirtyDataOrderFileData.data(), dirtyDataOrderFileData.size(), FileFlags::DirtyDataOrderFile);
578 dyld3::json::Node fileNode;
579 fileNode.map["path"].value = manifest.dirtyDataOrderFile();
580 fileNode.map["flags"].value = "DirtyDataOrderFile";
581 filesNode.array.push_back(fileNode);
582 }
583 }
584
585 buildInvocationNode.map["files"] = filesNode;
586
587 if (jsonFile) {
588 dyld3::json::printJSON(buildInvocationNode, 0, jsonFile);
589 fclose(jsonFile);
590 jsonFile = nullptr;
591 }
592
593 cacheBuildSuccess = runSharedCacheBuilder(sharedCacheBuilder);
594
595 if (!cacheBuildSuccess) {
596 for (uint64 i = 0, e = getErrorCount(sharedCacheBuilder); i != e; ++i) {
597 const char* errorMessage = getError(sharedCacheBuilder, i);
598 fprintf(stderr, "ERROR: %s\n", errorMessage);
599 }
600 }
601
602 // Now emit each cache we generated, or the errors for them.
603 for (uint64 i = 0, e = getCacheResultCount(sharedCacheBuilder); i != e; ++i) {
604 BuildResult result;
605 getCacheResult(sharedCacheBuilder, i, &result);
606 if (result.numErrors) {
607 for (uint64_t errorIndex = 0; errorIndex != result.numErrors; ++errorIndex) {
608 fprintf(stderr, "[%s] ERROR: %s\n", result.loggingPrefix, result.errors[errorIndex]);
609 }
610 cacheBuildSuccess = false;
611 continue;
612 }
613 if (result.numWarnings) {
614 for (uint64_t warningIndex = 0; warningIndex != result.numWarnings; ++warningIndex) {
615 fprintf(stderr, "[%s] WARNING: %s\n", result.loggingPrefix, result.warnings[warningIndex]);
616 }
617 }
618 }
619
620 // If we built caches, then write everything out.
621 // TODO: Decide if we should we write any good caches anyway?
622 if (cacheBuildSuccess) {
623 for (uint64 i = 0, e = getFileResultCount(sharedCacheBuilder); i != e; ++i) {
624 FileResult result;
625 getFileResult(sharedCacheBuilder, i, &result);
626
627 if (!result.data)
628 continue;
629
630 const std::string path = dstRoot + result.path;
631 std::string pathTemplate = path + "-XXXXXX";
632 size_t templateLen = strlen(pathTemplate.c_str())+2;
633 char pathTemplateSpace[templateLen];
634 strlcpy(pathTemplateSpace, pathTemplate.c_str(), templateLen);
635 int fd = mkstemp(pathTemplateSpace);
636 if ( fd != -1 ) {
637 ::ftruncate(fd, result.size);
638 uint64_t writtenSize = pwrite(fd, result.data, result.size, 0);
639 if ( writtenSize == result.size ) {
640 ::fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); // mkstemp() makes file "rw-------", switch it to "rw-r--r--"
641 if ( ::rename(pathTemplateSpace, path.c_str()) == 0) {
642 ::close(fd);
643 continue; // success
644 }
645 }
646 else {
647 fprintf(stderr, "ERROR: could not write file %s\n", pathTemplateSpace);
648 cacheBuildSuccess = false;
649 }
650 ::close(fd);
651 ::unlink(pathTemplateSpace);
652 }
653 else {
654 fprintf(stderr, "ERROR: could not open file %s\n", pathTemplateSpace);
655 cacheBuildSuccess = false;
656 }
657 }
658 }
659
660 destroySharedCacheBuilder(sharedCacheBuilder);
661
662 for (auto mappedFile : mappedFiles)
663 ::munmap((void*)mappedFile.first, mappedFile.second);
664 } else {
665 manifest.calculateClosure();
666
667 cacheBuildSuccess = build(diags, manifest, dstRoot, false, debug, false, false, emitDevCaches, true);
668 }
669
670 if (!cacheBuildSuccess) {
671 exit(-1);
672 }
673
674 // Compare this cache to the baseline cache and see if we have any roots to copy over
675 if (!baselineDifferenceResultPath.empty() || baselineCopyRoots) {
676 std::set<std::string> baselineDylibs = manifest.resultsForConfiguration(configuration);
677
678 std::set<std::string> newDylibs;
679 manifest.forEachConfiguration([&manifest, &newDylibs](const std::string& configName) {
680 for (auto& arch : manifest.configuration(configName).architectures) {
681 for (auto& dylib : arch.second.results.dylibs) {
682 if (dylib.second.included) {
683 newDylibs.insert(manifest.installNameForUUID(dylib.first));
684 }
685 }
686 }
687 });
688
689 if (baselineCopyRoots) {
690 // Work out the set of dylibs in the old cache but not the new one
691 std::set<std::string> dylibsMissingFromNewCache;
692 for (const std::string& baselineDylib : baselineDylibs) {
693 if (!newDylibs.count(baselineDylib))
694 dylibsMissingFromNewCache.insert(baselineDylib);
695 }
696
697 if (!dylibsMissingFromNewCache.empty()) {
698 BOMCopier copier = BOMCopierNewWithSys(BomSys_default());
699 BOMCopierSetUserData(copier, (void*)&dylibsMissingFromNewCache);
700 BOMCopierSetCopyFileStartedHandler(copier, filteredCopyIncludingPaths);
701 std::string dylibCacheRootDir = realFilePath(dylibCacheDir + "/Root");
702 if (dylibCacheRootDir == "") {
703 fprintf(stderr, "Could not find dylib Root directory to copy baseline roots from\n");
704 exit(1);
705 }
706 BOMCopierCopy(copier, dylibCacheRootDir.c_str(), dstRoot.c_str());
707 BOMCopierFree(copier);
708
709 for (const std::string& dylibMissingFromNewCache : dylibsMissingFromNewCache) {
710 diags.verbose("Dylib missing from new cache: '%s'\n", dylibMissingFromNewCache.c_str());
711 }
712 }
713 }
714
715 if (!baselineDifferenceResultPath.empty()) {
716 auto cppToObjStr = [](const std::string& str) {
717 return [NSString stringWithUTF8String:str.c_str()];
718 };
719
720 // Work out the set of dylibs in the cache and taken from the -root
721 NSMutableArray<NSString*>* dylibsFromRoots = [NSMutableArray array];
722 for (auto& root : roots) {
723 for (const std::string& dylibInstallName : newDylibs) {
724 struct stat sb;
725 std::string filePath = root + "/" + dylibInstallName;
726 if (!stat(filePath.c_str(), &sb)) {
727 [dylibsFromRoots addObject:cppToObjStr(dylibInstallName)];
728 }
729 }
730 }
731
732 // Work out the set of dylibs in the new cache but not in the baseline cache.
733 NSMutableArray<NSString*>* dylibsMissingFromBaselineCache = [NSMutableArray array];
734 for (const std::string& newDylib : newDylibs) {
735 if (!baselineDylibs.count(newDylib))
736 [dylibsMissingFromBaselineCache addObject:cppToObjStr(newDylib)];
737 }
738
739 NSMutableDictionary* cacheDict = [[NSMutableDictionary alloc] init];
740 cacheDict[@"root-paths-in-cache"] = dylibsFromRoots;
741 cacheDict[@"device-paths-to-delete"] = dylibsMissingFromBaselineCache;
742
743 NSError* error = nil;
744 NSData* outData = [NSPropertyListSerialization dataWithPropertyList:cacheDict
745 format:NSPropertyListBinaryFormat_v1_0
746 options:0
747 error:&error];
748 (void)[outData writeToFile:cppToObjStr(baselineDifferenceResultPath) atomically:YES];
749 }
750 }
751
752 if (copyRoots) {
753 std::set<std::string> cachePaths;
754 manifest.forEachConfiguration([&manifest, &cachePaths](const std::string& configName) {
755 for (auto& arch : manifest.configuration(configName).architectures) {
756 for (auto& dylib : arch.second.results.dylibs) {
757 if (dylib.second.included) {
758 cachePaths.insert(manifest.installNameForUUID(dylib.first));
759 }
760 }
761 }
762 });
763
764 BOMCopier copier = BOMCopierNewWithSys(BomSys_default());
765 BOMCopierSetUserData(copier, (void*)&cachePaths);
766 BOMCopierSetCopyFileStartedHandler(copier, filteredCopyExcludingPaths);
767 for (auto& root : roots) {
768 BOMCopierCopy(copier, root.c_str(), dstRoot.c_str());
769 }
770 BOMCopierFree(copier);
771 }
772
773 int err = sync_volume_np(dstRoot.c_str(), SYNC_VOLUME_FULLSYNC | SYNC_VOLUME_WAIT);
774 if (err) {
775 fprintf(stderr, "Volume sync failed errnor=%d (%s)\n", err, strerror(err));
776 }
777
778 // Now that all the build commands have been issued lets put a barrier in after then which can tear down the app after
779 // everything is written.
780
781 if (!resultPath.empty()) {
782 manifest.write(resultPath);
783 }
784
785 const char* args[8];
786 args[0] = (char*)"/bin/rm";
787 args[1] = (char*)"-rf";
788 args[2] = (char*)tempRootsDir;
789 args[3] = nullptr;
790 (void)runCommandAndWait(diags, args);
791
792 for (const std::string& warn : diags.warnings()) {
793 fprintf(stderr, "dyld_shared_cache_builder: warning: %s\n", warn.c_str());
794 }
795 exit(0);
796 });
797 }
798
799 dispatch_main();
800
801 return 0;
802 }