5 // Created by Louis Gerbarg on 7/23/15.
12 #include <Metabom/MBTypes.h>
13 #include <Metabom/MBEntry.h>
14 #include <Metabom/MBMetabom.h>
15 #include <Metabom/MBIterator.h>
17 #endif /* BOM_SUPPORT */
21 #include <Foundation/Foundation.h>
24 #include "MachOFileAbstraction.hpp"
25 #include "FileAbstraction.hpp"
29 #include <mach-o/loader.h>
30 #include <mach-o/fat.h>
36 #include "dsc_iterator.h"
38 #include "mega-dylib-utils.h"
41 //FIXME this should be in a class
42 static bool rootless = true;
44 static inline NSString* cppToObjStr(const std::string& str) { return [NSString stringWithUTF8String:str.c_str()]; }
47 std::string effectivePath(const std::string source, const std::string target)
50 return normalize_absolute_file_path(target);
52 std::size_t found = source.find_last_of('/');
53 return normalize_absolute_file_path(source.substr(0, found) + "/" + target);
56 std::string checkSymlink(const std::string path, const std::pair<std::string, std::string>& symlink, const std::set<std::string>& directories)
58 if (directories.count(symlink.second) == 0) {
59 if (path == symlink.second)
62 auto res = std::mismatch(symlink.second.begin(), symlink.second.end(), path.begin());
63 if (res.first == symlink.second.end()) {
64 std::string alias = normalize_absolute_file_path(symlink.first + std::string(res.second, path.end()));
75 Manifest::Manifest(const std::string& path)
76 : Manifest(path, std::set<std::string>())
80 Manifest::Manifest(const std::string& path, const std::set<std::string>& overlays)
82 NSMutableDictionary* manifestDict = [NSMutableDictionary dictionaryWithContentsOfFile:cppToObjStr(path)];
83 std::map<std::string, std::string> metabomTagMap;
84 std::map<std::string, std::set<std::string>> metabomExcludeTagMap;
85 std::map<std::string, std::set<std::string>> metabomRestrictedTagMap;
86 metabomFile = [manifestDict[@"metabomFile"] UTF8String];
88 for (NSString* project in manifestDict[@"projects"]) {
89 for (NSString* source in manifestDict[@"projects"][project]) {
90 projects[[project UTF8String]].sources.push_back([source UTF8String]);
94 for (NSString* configuration in manifestDict[@"configurations"]) {
95 std::string configStr = [configuration UTF8String];
96 std::string configTag = [manifestDict[@"configurations"][configuration][@"metabomTag"] UTF8String];
97 metabomTagMap[configTag] = configStr;
99 if (manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) {
100 for (NSString* excludeTag in manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) {
101 metabomExcludeTagMap[configStr].insert([excludeTag UTF8String]);
102 configurations[configStr].metabomExcludeTags.insert([excludeTag UTF8String]);
106 if (manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) {
107 for (NSString* restrictTag in manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) {
108 metabomRestrictedTagMap[configStr].insert([restrictTag UTF8String]);
109 configurations[configStr].metabomRestrictTags.insert([restrictTag UTF8String]);
113 configurations[configStr].metabomTag = configTag;
114 configurations[configStr].platformName =
115 [manifestDict[@"configurations"][configuration][@"platformName"] UTF8String];
118 manifest_version = [manifestDict[@"manifest-version"] unsignedIntValue];
119 build = [manifestDict[@"build"] UTF8String];
120 if (manifestDict[@"dylibOrderFile"]) {
121 dylibOrderFile = [manifestDict[@"dylibOrderFile"] UTF8String];
123 if (manifestDict[@"dirtyDataOrderFile"]) {
124 dirtyDataOrderFile = [manifestDict[@"dirtyDataOrderFile"] UTF8String];
127 auto metabom = MBMetabomOpen(metabomFile.c_str(), false);
128 auto metabomEnumerator = MBIteratorNewWithPath(metabom, ".", "");
131 std::map<std::string, std::string> symlinks;
132 std::set<std::string> directories;
134 // FIXME error handling (NULL metabom)
136 while ((entry = MBIteratorNext(metabomEnumerator))) {
137 auto fsObject = MBEntryGetFSObject(entry);
138 std::string entryPath = BOMFSObjectPathName(fsObject);
139 if (entryPath[0] == '.') {
140 entryPath.erase(0, 1);
142 auto entryType = BOMFSObjectType(fsObject);
147 auto tagCount = MBEntryGetNumberOfProjectTags(entry);
149 if (!BOMFSObjectIsBinaryObject(fsObject))
154 } else if (tagCount == 1) {
155 MBEntryGetProjectTags(entry, &tag);
157 MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount);
158 MBEntryGetProjectTags(entry, tags);
160 //Sigh, we can have duplicate entries for the same tag, so build a set to work with
161 std::set<std::string> tagStrs;
162 std::map<std::string, MBTag> tagStrMap;
163 for (auto i = 0; i < tagCount; ++i) {
164 tagStrs.insert(MBMetabomGetProjectForTag(metabom, tags[i]));
165 tagStrMap.insert(std::make_pair(MBMetabomGetProjectForTag(metabom, tags[i]), tags[i]));
168 if (tagStrs.size() > 1) {
169 std::string projects;
170 for (const auto& tagStr : tagStrs) {
171 if (!projects.empty())
174 projects += "'" + tagStr + "'";
176 warning("Bom entry '%s' is claimed by multiple projects: %s, taking first entry", entryPath.c_str(), projects.c_str());
178 tag = tagStrMap[*tagStrs.begin()];
182 std::string projectName = MBMetabomGetProjectForTag(metabom, tag);
184 // FIXME we need to actually walk down the searchpaths
185 auto project = projects.find(projectName);
186 if (project == projects.end())
188 if (project->second.sources.size() == 0)
190 std::string projectPath = project->second.sources[0];
191 std::map<std::string, MachOProxy*> proxies;
193 for (const auto& overlay : overlays) {
194 proxies = MachOProxy::findDylibInfo(overlay + "/" + entryPath);
195 if (proxies.size() > 0)
199 if (proxies.size() == 0) {
200 proxies = MachOProxy::findDylibInfo(projectPath + "/" + entryPath);
203 for (auto& proxy : proxies) {
204 assert(proxy.second != nullptr);
205 if (proxy.second->isExecutable()) {
206 architectureFiles[proxy.first].executables.insert(std::make_pair(entryPath, File(proxy.second)));
209 if (!proxy.second->isDylib())
211 assert(proxy.second->installName != "");
212 proxy.second->addAlias(entryPath);
213 architectureFiles[proxy.first].dylibs.insert(
214 std::make_pair(proxy.second->installName, File(proxy.second)));
215 auto tagCount = MBEntryGetNumberOfPackageTags(entry);
216 MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount);
217 MBEntryGetPackageTags(entry, tags);
218 std::set<std::string> tagStrs;
220 for (auto i = 0; i < tagCount; ++i) {
221 tagStrs.insert(MBMetabomGetPackageForTag(metabom, tags[i]));
224 for (const auto& tagStr : tagStrs) {
225 // Does the configuration exist
226 auto configuration = metabomTagMap.find(tagStr);
227 if (configuration == metabomTagMap.end())
230 auto restrictions = metabomRestrictedTagMap.find(configuration->second);
231 if (restrictions != metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, tagStrs)) {
232 configurations[configuration->second].restrictedInstallnames.insert(proxy.second->installName);
234 // Is the configuration excluded
235 auto exclusions = metabomExcludeTagMap.find(configuration->second);
236 if (exclusions != metabomExcludeTagMap.end() && !is_disjoint(exclusions->second, tagStrs))
239 if ([manifestDict[@"configurations"][cppToObjStr(configuration->second)][@"architectures"]
240 containsObject:cppToObjStr(proxy.first)]) {
241 configurations[configuration->second.c_str()].architectures[proxy.first].anchors.push_back(
242 proxy.second->installName);
249 case BOMSymlinkType: {
250 if (!has_prefix(entryPath, "/usr/lib/") && !has_prefix(entryPath, "/System/Library/"))
252 const char* target = BOMFSObjectSymlinkTarget(fsObject);
254 symlinks[entryPath] = effectivePath(entryPath, target);
257 case BOMDirectoryType: {
258 if (!has_prefix(entryPath, "/usr/lib/") && !has_prefix(entryPath, "/System/Library/"))
260 directories.insert(entryPath);
267 MBIteratorFree(metabomEnumerator);
268 MBMetabomFree(metabom);
270 dispatch_queue_t symlinkQueue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, NULL);
271 dispatch_group_t symlinkGroup = dispatch_group_create();
273 for (auto& fileSet : architectureFiles) {
274 cacheBuilderDispatchGroupAsync(symlinkGroup, symlinkQueue, [&] {
275 for (auto& file : fileSet.second.dylibs) {
276 bool aliasAdded = true;
277 auto proxy = file.second.proxy;
282 for (auto& symlink : symlinks) {
283 std::set<std::string> newAliases;
284 auto alias = checkSymlink(proxy->installName, symlink, directories);
286 newAliases.insert(alias);
289 for (auto& existingAlias : proxy->installNameAliases) {
290 alias = checkSymlink(existingAlias, symlink, directories);
292 newAliases.insert(alias);
296 for (auto& alias : newAliases) {
297 if (proxy->addAlias(alias)) {
306 dispatch_group_wait(symlinkGroup, DISPATCH_TIME_FOREVER);
308 for (auto& fileSet : architectureFiles) {
309 for (auto& file : fileSet.second.dylibs) {
310 auto proxy = file.second.proxy;
312 for (const auto& dependency : proxy->dependencies) {
313 auto dependencyProxy = dylibProxy(dependency, fileSet.first);
314 if (dependencyProxy == nullptr)
317 dependencyProxy->dependents.insert(proxy->installName);
324 void Manifest::calculateClosure( bool enforceRootless ) {
325 rootless = enforceRootless;
327 for ( auto& config : configurations ) {
328 for ( auto& arch : config.second.architectures ) {
329 calculateClosure( config.first, arch.first );
334 Manifest::File* Manifest::dylibForInstallName( const std::string& installname, const std::string& arch ) {
335 auto archIter = architectureFiles.find( arch );
336 if ( archIter == architectureFiles.end() ) return nullptr;
338 auto& files = archIter->second.dylibs;
339 auto dylibIterator = files.find( installname );
341 if ( dylibIterator != files.end() ) return &dylibIterator->second;
343 for ( auto& candidate : files ) {
344 if ( candidate.second.proxy->installNameAliases.count( installname ) > 0 ) {
345 dylibIterator = files.find( candidate.first );
346 return &dylibIterator->second;
349 // Check if we can fallback to an interworkable architecture
350 std::string fallbackArchStr = fallbackArchStringForArchString( arch );
351 if ( !fallbackArchStr.empty() ) {
352 return dylibForInstallName( installname, fallbackArchStr );
359 MachOProxy* Manifest::dylibProxy( const std::string& installname, const std::string& arch ) {
360 auto dylib = dylibForInstallName( installname, arch );
362 if ( dylib != nullptr ) {
363 assert( dylib->proxy != nullptr );
371 Manifest::sameContentsAsCacheAtPath(const std::string& configuration, const std::string& architecture, const std::string& path) const {
372 __block std::set<std::pair<std::string, std::array<char, 16>>> cacheDylibs;
373 std::set<std::pair<std::string, std::array<char, 16>>> manifestDylibs;
375 if ( ::stat(path.c_str(), &statbuf) == -1 ) {
376 // <rdar://problem/25912438> don't warn if there is no existing cache file
377 if ( errno != ENOENT )
378 warning("stat() failed for dyld shared cache at %s, errno=%d", path.c_str(), errno);
382 int cache_fd = ::open(path.c_str(), O_RDONLY);
383 if ( cache_fd < 0 ) {
384 warning("open() failed for shared cache file at %s, errno=%d", path.c_str(), errno);
388 const void *mappedCache = ::mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0);
389 if (mappedCache == MAP_FAILED) {
391 warning("mmap() for shared cache at %s failed, errno=%d", path.c_str(), errno);
396 if (configurations.count(configuration) == 0
397 || configurations.find(configuration)->second.architectures.count(architecture) == 0)
400 for (auto& dylib : configurations.find(configuration)->second.architectures.find(architecture)->second.results.dylibs) {
401 if ( dylib.second.included == true) {
402 std::pair<std::string, std::array<char, 16>> dylibPair;
403 dylibPair.first = dylib.first;
404 bcopy((const void *)&dylib.second.uuid[0], &dylibPair.second[0], sizeof(uuid_t));
405 manifestDylibs.insert(dylibPair);
406 auto file = architectureFiles.find(architecture)->second.dylibs.find(dylib.first);
407 if (file != architectureFiles.find(architecture)->second.dylibs.end()) {
408 for ( auto& alias : file->second.proxy->installNameAliases ) {
409 std::pair<std::string, std::array<char, 16>> aliasPair;
410 aliasPair.first = alias;
411 bcopy((const void *)&dylib.second.uuid[0], &aliasPair.second[0], sizeof(uuid_t));
412 manifestDylibs.insert(aliasPair);
418 (void)dyld_shared_cache_iterate(mappedCache, (uint32_t)statbuf.st_size,
419 ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo){
420 std::pair<std::string, std::array<char, 16>> dylibPair;
421 dylibPair.first = dylibInfo->path;
422 bcopy((const void *)&dylibInfo->uuid[0], &dylibPair.second[0], sizeof(uuid_t));
423 cacheDylibs.insert(dylibPair);
426 return (manifestDylibs == cacheDylibs);
429 void Manifest::removeDylib( MachOProxy* proxy, const std::string& reason, const std::string& configuration,
430 const std::string& architecture, std::unordered_set<std::string>& processedInstallnames ) {
431 auto configIter = configurations.find( configuration );
432 if ( configIter == configurations.end() ) return;
433 auto archIter = configIter->second.architectures.find( architecture );
434 if ( archIter == configIter->second.architectures.end() ) return;
435 auto& archManifest = archIter->second;
437 if ( archManifest.results.dylibs.count( proxy->installName ) == 0 ) {
438 bcopy( &proxy->uuid[0], &archManifest.results.dylibs[proxy->installName].uuid[0], sizeof( uuid_t ) );
439 processedInstallnames.insert( proxy->installName );
441 archManifest.results.dylibs[proxy->installName].exclude( reason );
443 processedInstallnames.insert( proxy->installName );
444 for ( auto& alias : proxy->installNameAliases ) {
445 processedInstallnames.insert( alias );
448 for ( const auto& dependent : proxy->dependents ) {
449 auto dependentProxy = dylibProxy( dependent, architecture );
450 auto dependentResultIter = archManifest.results.dylibs.find( dependentProxy->installName );
451 if ( dependentProxy &&
452 ( dependentResultIter == archManifest.results.dylibs.end() || dependentResultIter->second.included == true ) ) {
453 removeDylib( dependentProxy, "Missing dependency: " + proxy->installName, configuration, architecture,
454 processedInstallnames );
459 MachOProxy* Manifest::removeLargestLeafDylib( const std::string& configuration, const std::string& architecture ) {
460 std::set<std::string> activeInstallnames;
461 std::set<MachOProxy*> leafDylibs;
463 auto configIter = configurations.find( configuration );
464 if ( configIter == configurations.end() ) terminate( "Internal error" );
466 auto archIter = configIter->second.architectures.find( architecture );
467 if ( archIter == configIter->second.architectures.end() ) terminate( "Internal error" );
469 for ( const auto& dylibInfo : archIter->second.results.dylibs ) {
470 if ( dylibInfo.second.included ) {
471 activeInstallnames.insert( dylibInfo.first );
474 for ( const auto& installname : activeInstallnames ) {
475 auto dylib = dylibProxy( installname, architecture );
476 bool dependents = false;
477 for ( const auto& depedent : dylib->dependents ) {
478 if ( depedent != dylib->installName && activeInstallnames.count( depedent ) ) {
484 leafDylibs.insert( dylib );
487 if ( leafDylibs.empty() ) {
488 terminate( "No leaf dylibs to evict" );
490 MachOProxy* largestLeafDylib = nullptr;
491 for ( const auto& dylib : leafDylibs ) {
492 if ( largestLeafDylib == nullptr || dylib->fileSize > largestLeafDylib->fileSize ) {
493 largestLeafDylib = dylib;
496 std::unordered_set<std::string> empty;
497 removeDylib( largestLeafDylib, "VM space overflow", configuration, architecture, empty );
498 return largestLeafDylib;
501 static void recursiveInvalidate(const std::string& invalidName, std::unordered_map<std::string, std::unordered_set<std::string>>& usesOf, std::unordered_set<std::string>& unusableInstallNames)
503 if ( unusableInstallNames.count(invalidName) )
505 unusableInstallNames.insert(invalidName);
506 for (const std::string& name : usesOf[invalidName] ) {
507 recursiveInvalidate(name, usesOf, unusableInstallNames);
511 void Manifest::pruneClosure()
513 for (auto& config : configurations) {
514 for (auto& arch : config.second.architectures) {
515 pruneClosure(config.first, arch.first);
520 void Manifest::pruneClosure(const std::string& configuration, const std::string& architecture)
522 auto configIter = configurations.find(configuration);
523 if ( configIter == configurations.end() )
525 auto archIter = configIter->second.architectures.find(architecture);
526 if ( archIter == configIter->second.architectures.end() )
528 auto& archManifest = archIter->second;
530 // build reverse dependency map and list of excluded dylibs
531 std::unordered_map<std::string, std::unordered_set<std::string>> reverseDep;
532 std::unordered_set<std::string> unusableStart;
533 for (const auto& dylib : archManifest.results.dylibs) {
534 const std::string dylibInstallName = dylib.first;
535 if ( dylib.second.included ) {
536 if ( MachOProxy* proxy = dylibProxy(dylibInstallName, architecture) ) {
537 for (const std::string& dependentPath : proxy->dependencies) {
538 reverseDep[dependentPath].insert(dylibInstallName);
543 unusableStart.insert(dylibInstallName);
547 // mark unusable, all dylibs depending on the initially unusable dylibs
548 std::unordered_set<std::string> newUnusable;
549 for (const std::string& unusable : unusableStart) {
550 recursiveInvalidate(unusable, reverseDep, newUnusable);
553 // remove unusable dylibs from manifest
554 std::unordered_set<std::string> dummy;
555 for (const std::string& unusable : newUnusable) {
556 if ( MachOProxy* proxy = dylibProxy(unusable, architecture) )
557 removeDylib(proxy, "Missing dependency: " + unusable, configuration, architecture, dummy);
558 warning("can't use: %s because dependent dylib cannot be used", unusable.c_str());
562 void Manifest::calculateClosure( const std::string& configuration, const std::string& architecture ) {
563 auto& archManifest = configurations[configuration].architectures[architecture];
564 auto archFileIter = architectureFiles.find( architecture );
565 assert( archFileIter != architectureFiles.end() );
566 auto files = archFileIter->second.dylibs;
568 std::unordered_set<std::string> newInstallnames;
570 for ( auto& anchor : archManifest.anchors ) {
571 newInstallnames.insert(anchor.installname);
574 std::unordered_set<std::string> processedInstallnames;
576 while (!newInstallnames.empty()) {
577 std::unordered_set<std::string> installnamesToProcess = newInstallnames;
578 newInstallnames.clear();
580 for (const std::string& installname : installnamesToProcess) {
581 if (processedInstallnames.count(installname) > 0) {
585 auto proxy = dylibProxy( installname, architecture );
587 if ( proxy == nullptr ) {
589 archManifest.results.dylibs[installname].exclude( "Could not find file for install name" );
590 warning("Could not find file for install name (%s)", installname.c_str());
594 if (configurations[configuration].restrictedInstallnames.count(installname) != 0) {
595 removeDylib(proxy, "Dylib '" + installname + "' removed due to explict restriction", configuration, architecture,
596 processedInstallnames);
599 // Validate we have all are depedencies
600 for ( const auto& dependency : proxy->dependencies ) {
601 if ( !dylibProxy( dependency, architecture ) ) {
602 removeDylib( proxy, "Missing dependency: " + dependency, configuration, architecture,
603 processedInstallnames );
608 // assert(info->installName == installname);
609 if ( archManifest.results.dylibs.count( proxy->installName ) == 0 ) {
610 bcopy( &proxy->uuid[0], &archManifest.results.dylibs[proxy->installName].uuid[0], sizeof( uuid_t ) );
611 processedInstallnames.insert( proxy->installName );
613 auto fileIter = files.find( proxy->installName );
614 if ( fileIter != files.end() ) {
615 for ( auto& aliasName : fileIter->second.proxy->installNameAliases ) {
616 processedInstallnames.insert( aliasName );
621 for ( const auto& dependency : proxy->dependencies ) {
622 if ( processedInstallnames.count( dependency ) == 0 ) {
623 newInstallnames.insert( dependency );
630 void Manifest::write( const std::string& path ) {
631 if ( path.empty() ) return;
633 NSMutableDictionary* cacheDict = [[NSMutableDictionary alloc] init];
634 NSMutableDictionary* projectDict = [[NSMutableDictionary alloc] init];
635 NSMutableDictionary* configurationsDict = [[NSMutableDictionary alloc] init];
636 NSMutableDictionary* resultsDict = [[NSMutableDictionary alloc] init];
638 cacheDict[@"manifest-version"] = @( manifest_version );
639 cacheDict[@"build"] = cppToObjStr( build );
640 cacheDict[@"dylibOrderFile"] = cppToObjStr( dylibOrderFile );
641 cacheDict[@"dirtyDataOrderFile"] = cppToObjStr( dirtyDataOrderFile );
642 cacheDict[@"metabomFile"] = cppToObjStr( metabomFile );
644 cacheDict[@"projects"] = projectDict;
645 cacheDict[@"results"] = resultsDict;
646 cacheDict[@"configurations"] = configurationsDict;
648 for ( const auto& project : projects ) {
649 NSMutableArray* sources = [[NSMutableArray alloc] init];
651 for ( const auto& source : project.second.sources ) {
652 [sources addObject:cppToObjStr( source )];
655 projectDict[cppToObjStr( project.first )] = sources;
658 for ( auto& configuration : configurations ) {
659 NSMutableArray* archArray = [[NSMutableArray alloc] init];
660 for ( auto& arch : configuration.second.architectures ) {
661 [archArray addObject:cppToObjStr( arch.first )];
664 NSMutableArray* excludeTags = [[NSMutableArray alloc] init];
665 for ( const auto& excludeTag : configuration.second.metabomExcludeTags ) {
666 [excludeTags addObject:cppToObjStr( excludeTag )];
669 configurationsDict[cppToObjStr( configuration.first )] = @{
670 @"platformName" : cppToObjStr( configuration.second.platformName ),
671 @"metabomTag" : cppToObjStr( configuration.second.metabomTag ),
672 @"metabomExcludeTags" : excludeTags,
673 @"architectures" : archArray
677 for ( auto& configuration : configurations ) {
678 NSMutableDictionary* archResultsDict = [[NSMutableDictionary alloc] init];
679 for ( auto& arch : configuration.second.architectures ) {
680 NSMutableDictionary* dylibsDict = [[NSMutableDictionary alloc] init];
681 NSMutableArray* warningsArray = [[NSMutableArray alloc] init];
682 NSMutableDictionary* devRegionsDict = [[NSMutableDictionary alloc] init];
683 NSMutableDictionary* prodRegionsDict = [[NSMutableDictionary alloc] init];
684 NSString *prodCDHash = cppToObjStr(arch.second.results.productionCache.cdHash);
685 NSString *devCDHash = cppToObjStr(arch.second.results.developmentCache.cdHash);
687 for ( auto& dylib : arch.second.results.dylibs ) {
688 NSMutableDictionary* dylibDict = [[NSMutableDictionary alloc] init];
689 if ( dylib.second.included ) {
690 NSMutableDictionary* segments = [[NSMutableDictionary alloc] init];
691 dylibDict[@"included"] = @YES;
692 for ( auto& segment : dylib.second.segments ) {
693 segments[cppToObjStr( segment.name )] =
694 @{ @"startAddr" : @( segment.startAddr ),
695 @"endAddr" : @( segment.endAddr ) };
697 dylibDict[@"segments"] = segments;
699 dylibDict[@"included"] = @NO;
700 dylibDict[@"exclusionInfo"] = cppToObjStr(dylib.second.exclusionInfo);
702 dylibsDict[cppToObjStr( dylib.first )] = dylibDict;
705 for ( auto& region : arch.second.results.developmentCache.regions ) {
706 devRegionsDict[cppToObjStr( region.name )] =
707 @{ @"startAddr" : @( region.startAddr ),
708 @"endAddr" : @( region.endAddr ) };
711 for ( auto& region : arch.second.results.productionCache.regions ) {
712 prodRegionsDict[cppToObjStr( region.name )] =
713 @{ @"startAddr" : @( region.startAddr ),
714 @"endAddr" : @( region.endAddr ) };
717 for ( auto& warning : arch.second.results.warnings ) {
718 [warningsArray addObject:cppToObjStr( warning )];
721 BOOL built = arch.second.results.failure.empty();
722 archResultsDict[cppToObjStr( arch.first )] = @{
723 @"dylibs" : dylibsDict,
724 @"built" : @( built ),
725 @"failure" : cppToObjStr( arch.second.results.failure ),
726 @"productionCache" : @{@"cdhash" : prodCDHash, @"regions" : prodRegionsDict},
727 @"developmentCache" : @{@"cdhash" : devCDHash, @"regions" : devRegionsDict},
728 @"warnings" : warningsArray
731 resultsDict[cppToObjStr( configuration.first )] = archResultsDict;
734 NSError* error = nil;
735 NSData *outData = [NSPropertyListSerialization dataWithPropertyList:cacheDict
736 format:NSPropertyListBinaryFormat_v1_0
739 (void)[outData writeToFile:cppToObjStr(path) atomically:YES];