dyld-732.8.tar.gz
[apple/dyld.git] / dyld3 / shared-cache / mrm_shared_cache_builder.cpp
1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
2 *
3 * Copyright (c) 2017 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 "mrm_shared_cache_builder.h"
26 #include "CacheBuilder.h"
27 #include "ClosureFileSystem.h"
28 #include "FileUtils.h"
29 #include <pthread.h>
30 #include <memory>
31 #include <vector>
32 #include <map>
33 #include <sys/stat.h>
34
35 static const uint64_t kMinBuildVersion = 1; //The minimum version BuildOptions struct we can support
36 static const uint64_t kMaxBuildVersion = 1; //The maximum version BuildOptions struct we can support
37
38 static const uint32_t MajorVersion = 1;
39 static const uint32_t MinorVersion = 0;
40
41 namespace dyld3 {
42 namespace closure {
43
44 struct FileInfo {
45 const char* path;
46 const uint8_t* data;
47 const uint64_t length;
48 FileFlags flags;
49 uint64_t mtime;
50 uint64_t inode;
51 };
52
53 class FileSystemMRM : public FileSystem {
54 public:
55 FileSystemMRM() : FileSystem() { }
56
57 bool getRealPath(const char possiblePath[MAXPATHLEN], char realPath[MAXPATHLEN]) const override {
58 Diagnostics diag;
59 std::string resolvedPath = symlinkResolver.realPath(diag, possiblePath);
60 if (diag.hasError()) {
61 diag.verbose("MRM error: %s\n", diag.errorMessage().c_str());
62 diag.clearError();
63 return false;
64 }
65
66 // FIXME: Should we only return real paths of files which point to macho's? For now that is what we are doing
67 auto it = fileMap.find(resolvedPath);
68 if (it == fileMap.end())
69 return false;
70
71 memcpy(realPath, resolvedPath.c_str(), std::min((size_t)MAXPATHLEN, resolvedPath.size() + 1));
72 return true;
73 }
74
75 bool loadFile(const char* path, LoadedFileInfo& info, char realerPath[MAXPATHLEN], void (^error)(const char* format, ...)) const override {
76 Diagnostics diag;
77 std::string resolvedPath = symlinkResolver.realPath(diag, path);
78 if (diag.hasError()) {
79 diag.verbose("MRM error: %s\n", diag.errorMessage().c_str());
80 diag.clearError();
81 return false;
82 }
83
84 auto it = fileMap.find(resolvedPath);
85 if (it == fileMap.end())
86 return false;
87
88 if (resolvedPath == path)
89 realerPath[0] = '\0';
90 else
91 memcpy(realerPath, resolvedPath.c_str(), std::min((size_t)MAXPATHLEN, resolvedPath.size() + 1));
92
93 // The file exists at this exact path. Lets use it!
94 const FileInfo& fileInfo = files[it->second];
95
96 info.fileContent = fileInfo.data;
97 info.fileContentLen = fileInfo.length;
98 info.sliceOffset = 0;
99 info.sliceLen = fileInfo.length;
100 info.isSipProtected = false;
101 info.inode = fileInfo.inode;
102 info.mtime = fileInfo.mtime;
103 info.unload = nullptr;
104 info.path = path;
105 return true;
106 }
107
108 void unloadFile(const LoadedFileInfo& info) const override {
109 if (info.unload)
110 info.unload(info);
111 }
112
113 void unloadPartialFile(LoadedFileInfo& info, uint64_t keepStartOffset, uint64_t keepLength) const override {
114 // Note we don't actually unload the data here, but we do want to update the offsets for other data structures to track where we are
115 info.fileContent = (const void*)((char*)info.fileContent + keepStartOffset);
116 info.fileContentLen = keepLength;
117 }
118
119 bool fileExists(const char* path, uint64_t* inode=nullptr, uint64_t* mtime=nullptr,
120 bool* issetuid=nullptr, bool* inodesMatchRuntime = nullptr) const override {
121 Diagnostics diag;
122 std::string resolvedPath = symlinkResolver.realPath(diag, path);
123 if (diag.hasError()) {
124 diag.verbose("MRM error: %s\n", diag.errorMessage().c_str());
125 diag.clearError();
126 return false;
127 }
128
129 auto it = fileMap.find(resolvedPath);
130 if (it == fileMap.end())
131 return false;
132
133 // The file exists at this exact path. Lets use it!
134 const FileInfo& fileInfo = files[it->second];
135 if (inode)
136 *inode = fileInfo.inode;
137 if (mtime)
138 *mtime = fileInfo.mtime;
139 if (issetuid)
140 *issetuid = false;
141 if (inodesMatchRuntime)
142 *inodesMatchRuntime = false;
143 return true;
144 }
145
146 // MRM file APIs
147 bool addFile(const char* path, uint8_t* data, uint64_t size, Diagnostics& diag, FileFlags fileFlags) {
148 auto iteratorAndInserted = fileMap.insert(std::make_pair(path, files.size()));
149 if (!iteratorAndInserted.second) {
150 diag.error("Already have content for path: '%s'", path);
151 return false;
152 }
153
154 symlinkResolver.addFile(diag, path);
155 if (diag.hasError())
156 return false;
157
158 // on iOS, inode is just a placeholder
159 // Note its safe to just use the index here as we only compare it during closure building
160 // and never record it in the closures
161 uint64_t inode = files.size() + 1;
162 uint64_t mtime = 0;
163
164 files.push_back((FileInfo){ path, data, size, fileFlags, mtime, inode });
165 return true;
166 }
167
168 bool addSymlink(const char* fromPath, const char* toPath, Diagnostics& diag) {
169 symlinkResolver.addSymlink(diag, fromPath, toPath);
170 return !diag.hasError();
171 }
172
173 void forEachFileInfo(std::function<void(const char* path, FileFlags fileFlags)> lambda) {
174 for (const FileInfo& fileInfo : files)
175 lambda(fileInfo.path, fileInfo.flags);
176 }
177
178 size_t fileCount() const {
179 return files.size();
180 }
181
182 std::vector<DyldSharedCache::FileAlias> getResolvedSymlinks(Diagnostics& diag) {
183 return symlinkResolver.getResolvedSymlinks(diag);
184 }
185
186 private:
187 std::vector<FileInfo> files;
188 std::map<std::string, uint64_t> fileMap;
189 SymlinkResolver symlinkResolver;
190 };
191
192 } // namespace closure
193 } // namespace dyld3
194
195 struct BuildInstance {
196 std::unique_ptr<DyldSharedCache::CreateOptions> options;
197 std::unique_ptr<CacheBuilder> builder;
198 std::vector<CacheBuilder::InputFile> inputFiles;
199 std::vector<const char*> errors;
200 std::vector<const char*> warnings;
201 std::vector<std::string> errorStrings; // Owns the data for the errors
202 std::vector<std::string> warningStrings; // Owns the data for the warnings
203 uint8_t* cacheData = nullptr;
204 uint64_t cacheSize = 0;
205 std::string jsonMap;
206 std::string cdHash; // Owns the data for the cdHash
207 std::string cdHashType; // Owns the data for the cdHashType
208 std::string uuid; // Owns the data for the uuid
209 };
210
211 struct BuildFileResult {
212 std::string path;
213 const uint8_t* data;
214 uint64_t size;
215 };
216
217 struct SharedCacheBuilder {
218 SharedCacheBuilder(const BuildOptions_v1* options);
219 const BuildOptions_v1* options;
220 dyld3::closure::FileSystemMRM fileSystem;
221
222 std::string dylibOrderFileData;
223 std::string dirtyDataOrderFileData;
224
225 // An array of builders and their options as we may have more than one builder for a given device variant.
226 std::vector<BuildInstance> builders;
227
228 // The paths in all of the caches
229 // We keep this here to own the std::string path data
230 std::map<std::string, uint32_t> dylibsInCaches;
231
232 // The results from all of the builders
233 // We keep this in a vector to own the data.
234 std::vector<FileResult*> fileResults;
235 std::vector<FileResult> fileResultStorage;
236
237 // The results from all of the builders
238 // We keep this in a vector to own the data.
239 std::vector<CacheResult*> cacheResults;
240 std::vector<CacheResult> cacheResultStorage;
241
242 // The files to remove. These are in every copy of the caches we built
243 std::vector<const char*> filesToRemove;
244
245 std::vector<const char*> errors;
246 std::vector<std::string> errorStorage;
247 pthread_mutex_t lock;
248
249 enum State {
250 AcceptingFiles,
251 Building,
252 FinishedBuilding
253 };
254
255 State state = AcceptingFiles;
256
257 void runSync(void (^block)()) {
258 pthread_mutex_lock(&lock);
259 block();
260 pthread_mutex_unlock(&lock);
261 }
262
263 __attribute__((format(printf, 2, 3)))
264 void error(const char* format, ...) {
265 va_list list;
266 va_start(list, format);
267 Diagnostics diag;
268 diag.error(format, list);
269 va_end(list);
270
271 errorStorage.push_back(diag.errorMessage());
272 errors.push_back(errorStorage.back().data());
273 }
274 };
275
276 SharedCacheBuilder::SharedCacheBuilder(const BuildOptions_v1* options) : options(options), lock(PTHREAD_MUTEX_INITIALIZER) {
277
278 }
279
280 void validiateBuildOptions(const BuildOptions_v1* options, SharedCacheBuilder& builder) {
281 if (options->version < kMinBuildVersion) {
282 builder.error("Builder version %llu is less than minimum supported version of %llu", options->version, kMinBuildVersion);
283 }
284 if (options->version > kMaxBuildVersion) {
285 builder.error("Builder version %llu is greater than maximum supported version of %llu", options->version, kMaxBuildVersion);
286 }
287 if (!options->updateName) {
288 builder.error("updateName must not be null");
289 }
290 if (!options->deviceName) {
291 builder.error("deviceName must not be null");
292 }
293 switch (options->disposition) {
294 case Disposition::Unknown:
295 case Disposition::InternalDevelopment:
296 case Disposition::Customer:
297 break;
298 default:
299 builder.error("unknown disposition value");
300 break;
301 }
302 switch (options->platform) {
303 case Platform::unknown:
304 builder.error("platform must not be unknown");
305 break;
306 case Platform::macOS:
307 case Platform::iOS:
308 case Platform::tvOS:
309 case Platform::watchOS:
310 case Platform::bridgeOS:
311 case Platform::iOSMac:
312 case Platform::iOS_simulator:
313 case Platform::tvOS_simulator:
314 case Platform::watchOS_simulator:
315 break;
316 default:
317 builder.error("unknown platform value");
318 break;
319 }
320 if (!options->archs) {
321 builder.error("archs must not be null");
322 }
323 if (!options->numArchs) {
324 builder.error("numArchs must not be 0");
325 }
326 }
327
328 void getVersion(uint32_t *major, uint32_t *minor) {
329 *major = MajorVersion;
330 *minor = MinorVersion;
331 }
332
333 struct SharedCacheBuilder* createSharedCacheBuilder(const BuildOptions_v1* options) {
334 SharedCacheBuilder* builder = new SharedCacheBuilder(options);
335
336 // Check the option struct values are valid
337 validiateBuildOptions(options, *builder);
338
339 return builder;
340 }
341
342 bool addFile(struct SharedCacheBuilder* builder, const char* path, uint8_t* data, uint64_t size, FileFlags fileFlags) {
343 __block bool success = false;
344 builder->runSync(^() {
345 if (builder->state != SharedCacheBuilder::AcceptingFiles) {
346 builder->error("Cannot add file: '%s' as we have already started building", path);
347 return;
348 }
349 size_t pathLength = strlen(path);
350 if (pathLength == 0) {
351 builder->error("Empty path");
352 return;
353 }
354 if (pathLength >= MAXPATHLEN) {
355 builder->error("Path is too long: '%s'", path);
356 return;
357 }
358 if (data == nullptr) {
359 builder->error("Data cannot be null for file: '%s'", path);
360 return;
361 }
362 switch (fileFlags) {
363 case NoFlags:
364 case MustBeInCache:
365 case ShouldBeExcludedFromCacheIfUnusedLeaf:
366 case RequiredClosure:
367 break;
368 case DylibOrderFile:
369 builder->dylibOrderFileData = std::string((char*)data, size);
370 success = true;
371 return;
372 case DirtyDataOrderFile:
373 builder->dirtyDataOrderFileData = std::string((char*)data, size);
374 success = true;
375 return;
376 default:
377 builder->error("unknown file flags value");
378 break;
379 }
380 Diagnostics diag;
381 if (!builder->fileSystem.addFile(path, data, size, diag, fileFlags)) {
382 builder->errorStorage.push_back(diag.errorMessage());
383 builder->errors.push_back(builder->errorStorage.back().data());
384 return;
385 }
386 success = true;
387 });
388 return success;
389 }
390
391 bool addSymlink(struct SharedCacheBuilder* builder, const char* fromPath, const char* toPath) {
392 __block bool success = false;
393 builder->runSync(^() {
394 if (builder->state != SharedCacheBuilder::AcceptingFiles) {
395 builder->error("Cannot add file: '%s' as we have already started building", fromPath);
396 return;
397 }
398 size_t pathLength = strlen(fromPath);
399 if (pathLength == 0) {
400 builder->error("Empty path");
401 return;
402 }
403 if (pathLength >= MAXPATHLEN) {
404 builder->error("Path is too long: '%s'", fromPath);
405 return;
406 }
407 Diagnostics diag;
408 if (!builder->fileSystem.addSymlink(fromPath, toPath, diag)) {
409 builder->errorStorage.push_back(diag.errorMessage());
410 builder->errors.push_back(builder->errorStorage.back().data());
411 return;
412 }
413 success = true;
414 });
415 return success;
416 }
417
418 static bool platformExcludeLocalSymbols(Platform platform) {
419 switch (platform) {
420 case Platform::unknown:
421 case Platform::macOS:
422 return false;
423 case Platform::iOS:
424 case Platform::tvOS:
425 case Platform::watchOS:
426 case Platform::bridgeOS:
427 return true;
428 case Platform::iOSMac:
429 case Platform::iOS_simulator:
430 case Platform::tvOS_simulator:
431 case Platform::watchOS_simulator:
432 return false;
433 }
434 }
435
436 static DyldSharedCache::CodeSigningDigestMode platformCodeSigningDigestMode(Platform platform) {
437 switch (platform) {
438 case Platform::unknown:
439 case Platform::macOS:
440 case Platform::iOS:
441 case Platform::tvOS:
442 return DyldSharedCache::SHA256only;
443 case Platform::watchOS:
444 return DyldSharedCache::Agile;
445 case Platform::bridgeOS:
446 case Platform::iOSMac:
447 case Platform::iOS_simulator:
448 case Platform::tvOS_simulator:
449 case Platform::watchOS_simulator:
450 return DyldSharedCache::SHA256only;
451 }
452 }
453
454 static bool platformIsForSimulator(Platform platform) {
455 switch (platform) {
456 case Platform::unknown:
457 case Platform::macOS:
458 case Platform::iOS:
459 case Platform::tvOS:
460 case Platform::watchOS:
461 case Platform::bridgeOS:
462 case Platform::iOSMac:
463 return false;
464 case Platform::iOS_simulator:
465 case Platform::tvOS_simulator:
466 case Platform::watchOS_simulator:
467 return true;
468 }
469 }
470
471 static const char* dispositionName(Disposition disposition) {
472 switch (disposition) {
473 case Disposition::Unknown:
474 return "";
475 case Disposition::InternalDevelopment:
476 return "Internal";
477 case Disposition::Customer:
478 return "Customer";
479 case Disposition::InternalMinDevelopment:
480 return "InternalMinDevelopment";
481 }
482 }
483
484 bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) {
485 __block bool success = false;
486 builder->runSync(^() {
487 if (builder->state != SharedCacheBuilder::AcceptingFiles) {
488 builder->error("Builder has already been run");
489 return;
490 }
491 builder->state = SharedCacheBuilder::Building;
492 if (builder->fileSystem.fileCount() == 0) {
493 builder->error("Cannot run builder with no files");
494 }
495
496 Diagnostics diag;
497 std::vector<DyldSharedCache::FileAlias> aliases = builder->fileSystem.getResolvedSymlinks(diag);
498 if (diag.hasError()) {
499 diag.verbose("Symlink resolver error: %s\n", diag.errorMessage().c_str());
500 }
501
502 if (!builder->errors.empty()) {
503 builder->error("Skipping running shared cache builder due to previous errors");
504 return;
505 }
506
507 __block std::vector<CacheBuilder::InputFile> inputFiles;
508 builder->fileSystem.forEachFileInfo(^(const char* path, FileFlags fileFlags) {
509 CacheBuilder::InputFile::State state = CacheBuilder::InputFile::Unset;
510 switch (fileFlags) {
511 case FileFlags::NoFlags:
512 state = CacheBuilder::InputFile::Unset;
513 break;
514 case FileFlags::MustBeInCache:
515 state = CacheBuilder::InputFile::MustBeIncluded;
516 break;
517 case FileFlags::ShouldBeExcludedFromCacheIfUnusedLeaf:
518 state = CacheBuilder::InputFile::MustBeExcludedIfUnused;
519 break;
520 case FileFlags::RequiredClosure:
521 state = CacheBuilder::InputFile::MustBeIncluded;
522 break;
523 case FileFlags::DylibOrderFile:
524 case FileFlags::DirtyDataOrderFile:
525 builder->error("Order files should not be in the file system");
526 return;
527 }
528 inputFiles.emplace_back((CacheBuilder::InputFile){ path, state });
529 });
530
531 auto addCacheConfiguration = ^(bool isOptimized) {
532 for (uint64_t i = 0; i != builder->options->numArchs; ++i) {
533 auto options = std::make_unique<DyldSharedCache::CreateOptions>((DyldSharedCache::CreateOptions){});
534 const char *cacheSuffix = (isOptimized ? "" : ".development");
535 std::string runtimePath = (builder->options->platform == Platform::macOS) ? "/private/var/db/dyld/" : "/System/Library/Caches/com.apple.dyld/";
536 options->outputFilePath = runtimePath + "dyld_shared_cache_" + builder->options->archs[i] + cacheSuffix;
537 options->outputMapFilePath = options->outputFilePath + ".json";
538 options->archs = &dyld3::GradedArchs::forName(builder->options->archs[i]);
539 options->platform = (dyld3::Platform)builder->options->platform;
540 options->excludeLocalSymbols = platformExcludeLocalSymbols(builder->options->platform);
541 options->optimizeStubs = isOptimized;
542 options->optimizeObjC = true;
543 options->codeSigningDigestMode = platformCodeSigningDigestMode(builder->options->platform);
544 options->dylibsRemovedDuringMastering = true;
545 options->inodesAreSameAsRuntime = false;
546 options->cacheSupportsASLR = true;
547 options->forSimulator = platformIsForSimulator(builder->options->platform);
548 options->isLocallyBuiltCache = builder->options->isLocallyBuiltCache;
549 options->verbose = builder->options->verboseDiagnostics;
550 options->evictLeafDylibsOnOverflow = true;
551 options->loggingPrefix = std::string(builder->options->deviceName) + dispositionName(builder->options->disposition) + "." + builder->options->archs[i] + cacheSuffix;
552 options->dylibOrdering = parseOrderFile(builder->dylibOrderFileData);
553 options->dirtyDataSegmentOrdering = parseOrderFile(builder->dirtyDataOrderFileData);
554
555 auto cacheBuilder = std::make_unique<CacheBuilder>(*options.get(), builder->fileSystem);
556 builder->builders.emplace_back((BuildInstance) { std::move(options), std::move(cacheBuilder), inputFiles });
557 }
558 };
559
560 // Enqueue a cache for each configuration
561 switch (builder->options->disposition) {
562 case Disposition::Unknown:
563 case Disposition::InternalDevelopment:
564 addCacheConfiguration(false);
565 addCacheConfiguration(true);
566 break;
567 case Disposition::Customer:
568 addCacheConfiguration(true);
569 break;
570 case Disposition::InternalMinDevelopment:
571 addCacheConfiguration(false);
572 break;
573 }
574
575 // FIXME: This step can run in parallel.
576 for (auto& buildInstance : builder->builders) {
577 CacheBuilder* cacheBuilder = buildInstance.builder.get();
578 cacheBuilder->build(buildInstance.inputFiles, aliases);
579
580 // First put the warnings in to a vector to own them.
581 buildInstance.warningStrings.reserve(cacheBuilder->warnings().size());
582 for (const std::string& warning : cacheBuilder->warnings())
583 buildInstance.warningStrings.push_back(warning);
584
585 // Then copy to a vector to reference the owner
586 buildInstance.warnings.reserve(buildInstance.warningStrings.size());
587 for (const std::string& warning : buildInstance.warningStrings)
588 buildInstance.warnings.push_back(warning.c_str());
589
590 if (!cacheBuilder->errorMessage().empty()) {
591 // First put the errors in to a vector to own them.
592 buildInstance.errorStrings.push_back(cacheBuilder->errorMessage());
593
594 // Then copy to a vector to reference the owner
595 buildInstance.errors.reserve(buildInstance.errorStrings.size());
596 for (const std::string& error : buildInstance.errorStrings)
597 buildInstance.errors.push_back(error.c_str());
598 }
599
600 if (cacheBuilder->errorMessage().empty()) {
601 cacheBuilder->writeBuffer(buildInstance.cacheData, buildInstance.cacheSize);
602 buildInstance.jsonMap = cacheBuilder->getMapFileBuffer(builder->options->deviceName);
603 buildInstance.cdHash = cacheBuilder->cdHashFirst();
604 buildInstance.uuid = cacheBuilder->uuid();
605 switch (buildInstance.options->codeSigningDigestMode) {
606 case DyldSharedCache::SHA256only:
607 buildInstance.cdHashType = "sha256";
608 break;
609 case DyldSharedCache::SHA1only:
610 buildInstance.cdHashType = "sha1";
611 break;
612 case DyldSharedCache::Agile:
613 buildInstance.cdHashType = "sha1";
614 break;
615 }
616 }
617 }
618
619 // Now that we have run all of the builds, collect the results
620 // First push file results for each of the shared caches we built
621 for (auto& buildInstance : builder->builders) {
622 CacheBuilder* cacheBuilder = buildInstance.builder.get();
623
624 CacheResult cacheBuildResult;
625 cacheBuildResult.version = 1;
626 cacheBuildResult.loggingPrefix = buildInstance.options->loggingPrefix.c_str();
627 cacheBuildResult.deviceConfiguration = buildInstance.options->loggingPrefix.c_str();
628 cacheBuildResult.warnings = buildInstance.warnings.empty() ? nullptr : buildInstance.warnings.data();
629 cacheBuildResult.numWarnings = buildInstance.warnings.size();
630 cacheBuildResult.errors = buildInstance.errors.empty() ? nullptr : buildInstance.errors.data();
631 cacheBuildResult.numErrors = buildInstance.errors.size();
632 cacheBuildResult.uuidString = buildInstance.uuid.c_str();
633 cacheBuildResult.mapJSON = buildInstance.jsonMap.c_str();
634
635 builder->cacheResultStorage.emplace_back(cacheBuildResult);
636
637 if (!cacheBuilder->errorMessage().empty())
638 continue;
639
640 FileResult cacheFileResult;
641 cacheFileResult.version = 1;
642 cacheFileResult.path = buildInstance.options->outputFilePath.c_str();
643 cacheFileResult.behavior = AddFile;
644 cacheFileResult.data = buildInstance.cacheData;
645 cacheFileResult.size = buildInstance.cacheSize;
646 cacheFileResult.hashArch = buildInstance.options->archs->name();
647 cacheFileResult.hashType = buildInstance.cdHashType.c_str();
648 cacheFileResult.hash = buildInstance.cdHash.c_str();
649
650 builder->fileResultStorage.emplace_back(cacheFileResult);
651
652 cacheBuilder->forEachCacheDylib(^(const std::string &path) {
653 ++builder->dylibsInCaches[path.c_str()];
654 });
655 }
656
657 // Copy from the storage to the vector we can return to the API.
658 for (auto &fileResult : builder->fileResultStorage)
659 builder->fileResults.push_back(&fileResult);
660 for (auto &cacheResult : builder->cacheResultStorage)
661 builder->cacheResults.push_back(&cacheResult);
662
663
664 // Add entries to tell us to remove all of the dylibs from disk which are in every cache.
665 const size_t numCaches = builder->builders.size();
666 for (const auto& dylibAndCount : builder->dylibsInCaches) {
667 if (dylibAndCount.second == numCaches) {
668 builder->filesToRemove.push_back(dylibAndCount.first.c_str());
669 }
670 }
671
672 // Quit if we had any errors.
673 for (auto& buildInstance : builder->builders) {
674 CacheBuilder* cacheBuilder = buildInstance.builder.get();
675 if (!cacheBuilder->errorMessage().empty())
676 return;
677 }
678
679 builder->state = SharedCacheBuilder::FinishedBuilding;
680 success = true;
681 });
682 return success;
683 }
684
685 const char* const* getErrors(const struct SharedCacheBuilder* builder, uint64_t* errorCount) {
686 if (builder->errors.empty())
687 return nullptr;
688 *errorCount = builder->errors.size();
689 return builder->errors.data();
690 }
691
692 const struct FileResult* const* getFileResults(struct SharedCacheBuilder* builder, uint64_t* resultCount) {
693 if (builder->fileResults.empty())
694 return nullptr;
695 *resultCount = builder->fileResults.size();
696 return builder->fileResults.data();
697 }
698
699 const struct CacheResult* const* getCacheResults(struct SharedCacheBuilder* builder, uint64_t* resultCount) {
700 if (builder->cacheResults.empty())
701 return nullptr;
702 *resultCount = builder->cacheResults.size();
703 return builder->cacheResults.data();
704 }
705
706 const char* const* getFilesToRemove(const struct SharedCacheBuilder* builder, uint64_t* fileCount) {
707 if (builder->filesToRemove.empty())
708 return nullptr;
709 *fileCount = builder->filesToRemove.size();
710 return builder->filesToRemove.data();
711 }
712
713 void destroySharedCacheBuilder(struct SharedCacheBuilder* builder) {
714 for (auto& buildInstance : builder->builders) {
715 CacheBuilder* cacheBuilder = buildInstance.builder.get();
716 cacheBuilder->deleteBuffer();
717 }
718 for (auto &fileResult : builder->fileResultStorage) {
719 free((void*)fileResult.data);
720 fileResult.data = nullptr;
721 }
722 delete builder;
723 }