dyld-733.8.tar.gz
[apple/dyld.git] / dyld3 / shared-cache / Manifest.mm
1 /*
2  * Copyright (c) 2017 Apple Inc. All rights reserved.
3  *
4  * @APPLE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. Please obtain a copy of the License at
10  * http://www.opensource.apple.com/apsl/ and read it before using this
11  * file.
12  *
13  * The Original Code and all software distributed under the License are
14  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18  * Please see the License for the specific language governing rights and
19  * limitations under the License.
20  *
21  * @APPLE_LICENSE_HEADER_END@
22  */
23
24
25 extern "C" {
26 #include <Bom/Bom.h>
27 #include <Metabom/MBTypes.h>
28 #include <Metabom/MBEntry.h>
29 #include <Metabom/MBMetabom.h>
30 #include <Metabom/MBIterator.h>
31 };
32
33 #include <algorithm>
34 #include <CommonCrypto/CommonDigest.h>
35 #include <CommonCrypto/CommonDigestSPI.h>
36 #include <Foundation/Foundation.h>
37
38 #include "MachOFileAbstraction.hpp"
39 #include "FileAbstraction.hpp"
40 #include "Trie.hpp"
41 #include "FileUtils.h"
42 #include "StringUtils.h"
43 #include "MachOFile.h"
44 #include "MachOAnalyzer.h"
45
46 #include <mach-o/loader.h>
47 #include <mach-o/fat.h>
48
49 #include <array>
50 #include <vector>
51
52 #include "Manifest.h"
53 #include "ClosureFileSystemPhysical.h"
54
55 namespace {
56 //FIXME this should be in a class
57 static inline NSString* cppToObjStr(const std::string& str) { return [NSString stringWithUTF8String:str.c_str()]; }
58
59 template <class Set1, class Set2>
60 inline bool is_disjoint(const Set1& set1, const Set2& set2)
61 {
62     if (set1.empty() || set2.empty())
63         return true;
64
65     typename Set1::const_iterator it1 = set1.begin(), it1End = set1.end();
66     typename Set2::const_iterator it2 = set2.begin(), it2End = set2.end();
67
68     if (*it1 > *set2.rbegin() || *it2 > *set1.rbegin())
69         return true;
70
71     while (it1 != it1End && it2 != it2End) {
72         if (*it1 == *it2)
73             return false;
74         if (*it1 < *it2) {
75             it1++;
76         } else {
77             it2++;
78         }
79     }
80
81     return true;
82 }
83
84 } /* Anonymous namespace */
85
86 namespace dyld3 {
87 void Manifest::Results::exclude(const dyld3::MachOAnalyzer* mh, const std::string& reason)
88 {
89     UUID dylibUUID(mh);
90     dylibs[dylibUUID].uuid          = dylibUUID;
91     dylibs[dylibUUID].installname   = mh->installName();
92     dylibs[dylibUUID].included      = false;
93     dylibs[dylibUUID].exclusionInfo = reason;
94 }
95
96 void Manifest::Results::exclude(Manifest& manifest, const UUID& uuid, const std::string& reason)
97 {
98     const MachOAnalyzer* mh = manifest.machOForUUID(uuid);
99     dylibs[uuid].uuid          = uuid;
100     dylibs[uuid].installname   = mh->installName();
101     dylibs[uuid].included      = false;
102     dylibs[uuid].exclusionInfo = reason;
103 }
104
105 Manifest::CacheImageInfo& Manifest::Results::dylibForInstallname(const std::string& installname)
106 {
107     auto i = find_if(dylibs.begin(), dylibs.end(), [&installname](std::pair<UUID, CacheImageInfo> d) { return d.second.installname == installname; });
108     assert(i != dylibs.end());
109     return i->second;
110 }
111
112 bool Manifest::Architecture::operator==(const Architecture& O) const
113 {
114     for (auto& dylib : results.dylibs) {
115         if (dylib.second.included) {
116             auto Odylib = O.results.dylibs.find(dylib.first);
117             if (Odylib == O.results.dylibs.end()
118                 || Odylib->second.included == false
119                 || Odylib->second.uuid != dylib.second.uuid)
120                 return false;
121         }
122     }
123
124     for (const auto& Odylib : O.results.dylibs) {
125         if (Odylib.second.included) {
126             auto dylib = results.dylibs.find(Odylib.first);
127             if (dylib == results.dylibs.end()
128                 || dylib->second.included == false
129                 || dylib->second.uuid != Odylib.second.uuid)
130                 return false;
131         }
132     }
133
134     for (auto& bundle : results.bundles) {
135         if (bundle.second.included) {
136             auto Obundle = O.results.bundles.find(bundle.first);
137             if (Obundle == O.results.bundles.end()
138                 || Obundle->second.included == false
139                 || Obundle->second.uuid != bundle.second.uuid)
140                 return false;
141         }
142     }
143
144     for (const auto& Obundle : O.results.bundles) {
145         if (Obundle.second.included) {
146             auto bundle = results.bundles.find(Obundle.first);
147             if (bundle == results.bundles.end()
148                 || bundle->second.included == false
149                 || bundle->second.uuid != Obundle.second.uuid)
150                 return false;
151         }
152     }
153
154     for (auto& executable : results.executables) {
155         if (executable.second.included) {
156             auto Oexecutable = O.results.executables.find(executable.first);
157             if (Oexecutable == O.results.executables.end()
158                 || Oexecutable->second.included == false
159                 || Oexecutable->second.uuid != executable.second.uuid)
160                 return false;
161         }
162     }
163
164     for (const auto& Oexecutable : O.results.executables) {
165         if (Oexecutable.second.included) {
166             auto executable = results.executables.find(Oexecutable.first);
167             if (executable == results.executables.end()
168                 || executable->second.included == false
169                 || executable->second.uuid != Oexecutable.second.uuid)
170                 return false;
171         }
172     }
173
174     return true;
175 }
176
177 bool Manifest::Configuration::operator==(const Configuration& O) const
178 {
179     return architectures == O.architectures;
180 }
181
182 bool Manifest::Configuration::operator!=(const Configuration& other) const { return !(*this == other); }
183
184 const Manifest::Architecture& Manifest::Configuration::architecture(const std::string& architecture) const
185 {
186     assert(architectures.find(architecture) != architectures.end());
187     return architectures.find(architecture)->second;
188 }
189
190 void Manifest::Configuration::forEachArchitecture(std::function<void(const std::string& archName)> lambda) const
191 {
192     for (const auto& architecutre : architectures) {
193         lambda(architecutre.first);
194     }
195 }
196
197 bool Manifest::Architecture::operator!=(const Architecture& other) const { return !(*this == other); }
198
199 const std::map<std::string, Manifest::Project>& Manifest::projects()
200 {
201     return _projects;
202 }
203
204 const Manifest::Configuration& Manifest::configuration(const std::string& configuration) const
205 {
206     assert(_configurations.find(configuration) != _configurations.end());
207     return _configurations.find(configuration)->second;
208 }
209
210 void Manifest::forEachConfiguration(std::function<void(const std::string& configName)> lambda) const
211 {
212     for (const auto& configuration : _configurations) {
213         lambda(configuration.first);
214     }
215 }
216
217 void Manifest::addProjectSource(const std::string& project, const std::string& source, bool first)
218 {
219     auto& sources = _projects[project].sources;
220     if (std::find(sources.begin(), sources.end(), source) == sources.end()) {
221         if (first) {
222             sources.insert(sources.begin(), source);
223         } else {
224             sources.push_back(source);
225         }
226     }
227 }
228
229 const std::string Manifest::projectPath(const std::string& projectName)
230 {
231     auto project = _projects.find(projectName);
232     if (project == _projects.end())
233         return "";
234     if (project->second.sources.size() == 0)
235         return "";
236     return project->second.sources[0];
237 }
238
239 const bool Manifest::empty(void)
240 {
241     for (const auto& configuration : _configurations) {
242         if (configuration.second.architectures.size() != 0)
243             return false;
244     }
245     return true;
246 }
247
248 const std::string Manifest::dylibOrderFile() const { return _dylibOrderFile; };
249 void Manifest::setDylibOrderFile(const std::string& dylibOrderFile) { _dylibOrderFile = dylibOrderFile; };
250
251 const std::string Manifest::dirtyDataOrderFile() const { return _dirtyDataOrderFile; };
252 void Manifest::setDirtyDataOrderFile(const std::string& dirtyDataOrderFile) { _dirtyDataOrderFile = dirtyDataOrderFile; };
253
254 const std::string Manifest::metabomFile() const { return _metabomFile; };
255 void Manifest::setMetabomFile(const std::string& metabomFile) { _metabomFile = metabomFile; };
256
257 const Platform Manifest::platform() const { return _platform; };
258 void Manifest::setPlatform(const Platform platform) { _platform = platform; };
259
260 const std::string& Manifest::build() const { return _build; };
261 void Manifest::setBuild(const std::string& build) { _build = build; };
262 const uint32_t                             Manifest::version() const { return _manifestVersion; };
263 void Manifest::setVersion(const uint32_t manifestVersion) { _manifestVersion = manifestVersion; };
264
265 BuildQueueEntry Manifest::makeQueueEntry(const std::string& outputPath, const std::set<std::string>& configs, const std::string& arch, bool optimizeStubs,
266                                          const std::string& prefix, bool isLocallyBuiltCache, bool skipWrites, bool verbose)
267 {
268     dyld3::BuildQueueEntry retval;
269
270     DyldSharedCache::CreateOptions options;
271     options.outputFilePath    = skipWrites ? "" : outputPath;
272     options.outputMapFilePath = skipWrites ? "" : outputPath + ".map";
273     options.archs = &dyld3::GradedArchs::forName(arch.c_str());
274     options.platform = platform();
275     options.excludeLocalSymbols = true;
276     options.optimizeStubs = optimizeStubs;
277     options.optimizeObjC = true;
278     options.codeSigningDigestMode = (platform() == dyld3::Platform::watchOS) ?
279                                     DyldSharedCache::Agile : DyldSharedCache::SHA256only;
280     options.dylibsRemovedDuringMastering = true;
281     options.inodesAreSameAsRuntime = false;
282     options.cacheSupportsASLR = true;
283     options.forSimulator = false;
284     options.isLocallyBuiltCache = isLocallyBuiltCache;
285     options.verbose = verbose;
286     options.evictLeafDylibsOnOverflow = true;
287     options.loggingPrefix = prefix;
288     options.dylibOrdering = parseOrderFile(loadOrderFile(_dylibOrderFile));
289     options.dirtyDataSegmentOrdering = parseOrderFile(loadOrderFile(_dirtyDataOrderFile));
290
291     char rootsDir[PATH_MAX];
292     realpath("./Root/", rootsDir);
293     
294     dyld3::BuildQueueEntry queueEntry;
295     retval.configNames = configs;
296     retval.options = options;
297     retval.fileSystem = dyld3::closure::FileSystemPhysical(strdup(rootsDir));
298     retval.outputPath = outputPath;
299     retval.dylibsForCache = dylibsForCache(*configs.begin(), arch);
300     retval.otherDylibsAndBundles = otherDylibsAndBundles(*configs.begin(), arch);
301     retval.mainExecutables = mainExecutables(*configs.begin(), arch);
302
303     return retval;
304 }
305
306 bool Manifest::loadParser(const void* p, size_t sliceLength, uint64_t sliceOffset, const std::string& runtimePath, const std::string& buildPath, const std::set<std::string>& architectures)
307 {
308     assert(!_diags.hasError());
309
310     const MachOFile* mf = reinterpret_cast<const MachOFile*>(p);
311     const std::string archName = mf->archName();
312     if ( archName == "unknown" ) {
313         // Clear the error and punt
314         _diags.verbose("Dylib located at '%s' has unknown architecture\n", runtimePath.c_str());
315         return false;
316     }
317     if ( architectures.count(archName) == 0 )
318         return false;
319
320     const MachOAnalyzer* ma = reinterpret_cast<const MachOAnalyzer*>(p);
321     if ( !ma->validMachOForArchAndPlatform(_diags, sliceLength, runtimePath.c_str(), dyld3::GradedArchs::forName(archName.c_str()), _platform) ) {
322         // Clear the error and punt
323         _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
324         _diags.clearError();
325         return false;
326     }
327
328     // if this file uses zero-fill expansion, then mapping whole file in one blob will not work
329     // remapIfZeroFill() will remap the file
330     closure::FileSystemPhysical fileSystem;
331     closure::LoadedFileInfo info;
332     info.fileContent                = p;
333     info.fileContentLen             = sliceLength;
334     info.sliceOffset                = 0;
335     info.sliceLen                   = sliceLength;
336     info.inode                      = 0;
337     info.mtime                      = 0;
338     info.unload                     = nullptr;
339     ma = ma->remapIfZeroFill(_diags, fileSystem, info);
340
341     if (ma == nullptr) {
342         _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
343         _diags.clearError();
344         return false;
345     }
346
347     uuid_t uuid;
348     ma->getUuid(uuid);
349
350     if ( ma->isDylib() ) {
351         std::string installName = ma->installName();
352         auto index = std::make_pair(installName, archName);
353         auto i = _installNameMap.find(index);
354
355         if ( installName == "/System/Library/Caches/com.apple.xpc/sdk.dylib"
356             || installName == "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib" ) {
357             // HACK to deal with device specific dylibs. These must not be inseted into the installNameMap
358             _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, installName)));
359         } else if (i == _installNameMap.end()) {
360             _installNameMap.insert(std::make_pair(index, uuid));
361             _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, installName)));
362             if (installName[0] != '@' && installName != runtimePath) {
363                 _diags.warning("Dylib located at '%s' has  installname '%s'", runtimePath.c_str(), installName.c_str());
364             }
365         } else {
366             auto info = infoForUUID(i->second);
367             _diags.warning("Multiple dylibs claim installname '%s' ('%s' and '%s')", installName.c_str(), runtimePath.c_str(), info.runtimePath.c_str());
368
369             // This is the "Good" one, overwrite
370             if (runtimePath == installName) {
371                 _uuidMap.erase(uuid);
372                 _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, installName)));
373             }
374         }
375     } else {
376         _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, "")));
377     }
378     return true;
379 }
380
381 //FIXME: assert we have not errored first
382 bool Manifest::loadParsers(const std::string& buildPath, const std::string& runtimePath, const std::set<std::string>& architectures)
383 {
384     __block bool retval = false;
385     const void*  p = (uint8_t*)(-1);
386     struct stat  stat_buf;
387
388     std::tie(p, stat_buf) = fileCache.cacheLoad(_diags, buildPath);
389
390     if (p == (uint8_t*)(-1)) {
391         return false;
392     }
393
394     if ( const FatFile* fh = FatFile::isFatFile(p) ) {
395         fh->forEachSlice(_diags, stat_buf.st_size, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
396             if (loadParser(sliceStart, sliceSize, (uintptr_t)sliceStart-(uintptr_t)p, runtimePath, buildPath, architectures))
397                 retval = true;
398         });
399     } else {
400         return loadParser(p, stat_buf.st_size, 0, runtimePath, buildPath, architectures);
401     }
402     return retval;
403 }
404
405
406 const Manifest::UUIDInfo& Manifest::infoForUUID(const UUID& uuid) const {
407     auto i = _uuidMap.find(uuid);
408     assert(i != _uuidMap.end());
409     return i->second;
410 }
411
412 const Manifest::UUIDInfo Manifest::infoForInstallNameAndarch(const std::string& installName, const std::string arch) const  {
413     UUIDInfo retval;
414     auto uuidI = _installNameMap.find(std::make_pair(installName, arch));
415     if (uuidI == _installNameMap.end())
416         return UUIDInfo();
417
418     auto i = _uuidMap.find(uuidI->second);
419     if (i == _uuidMap.end())
420     return UUIDInfo();
421     return i->second;
422 }
423
424 const MachOAnalyzer* Manifest::machOForUUID(const UUID& uuid) const {
425     return infoForUUID(uuid).mh;
426 }
427
428 const std::string Manifest::buildPathForUUID(const UUID& uuid) {
429     return infoForUUID(uuid).buildPath;
430 }
431
432 const std::string Manifest::runtimePathForUUID(const UUID& uuid) {
433     return infoForUUID(uuid).runtimePath;
434 }
435
436 const std::string& Manifest::installNameForUUID(const UUID& uuid) {
437     return infoForUUID(uuid).installName;
438 }
439
440 Manifest::Manifest(Diagnostics& D, const std::string& path, bool populateIt) :
441     _diags(D)
442 {
443     _manifestDict = [NSMutableDictionary dictionaryWithContentsOfFile:cppToObjStr(path)];
444     if (!_manifestDict)
445         return;
446     NSString*            platStr = _manifestDict[@"platform"];
447
448     if (platStr == nullptr)
449         platStr = @"ios";
450     std::string platformString = [platStr UTF8String];
451     setMetabomFile([_manifestDict[@"metabomFile"] UTF8String]);
452
453     if (platformString == "ios") {
454         setPlatform(dyld3::Platform::iOS);
455     } else if ( (platformString == "tvos") || (platformString == "atv") ) {
456         setPlatform(dyld3::Platform::tvOS);
457     } else if ( (platformString == "watchos") || (platformString == "watch") ) {
458         setPlatform(dyld3::Platform::watchOS);
459     } else if ( (platformString == "bridgeos") || (platformString == "bridge") ) {
460         setPlatform(dyld3::Platform::bridgeOS);
461     } else if ( (platformString == "macos") || (platformString == "osx") ) {
462         setPlatform(dyld3::Platform::macOS);
463     } else {
464         //Fixme should we error?
465         setPlatform(dyld3::Platform::iOS);
466     }
467
468     for (NSString* project in _manifestDict[@"projects"]) {
469         for (NSString* source in _manifestDict[@"projects"][project]) {
470             addProjectSource([project UTF8String], [source UTF8String]);
471         }
472     }
473
474     for (NSString* configuration in _manifestDict[@"configurations"]) {
475         std::string configStr = [configuration UTF8String];
476         std::string configTag = [_manifestDict[@"configurations"][configuration][@"metabomTag"] UTF8String];
477
478         if (_manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) {
479             for (NSString* excludeTag in _manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) {
480                 _metabomExcludeTagMap[configStr].insert([excludeTag UTF8String]);
481                 _configurations[configStr].metabomExcludeTags.insert([excludeTag UTF8String]);
482             }
483         }
484
485         if (_manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) {
486             for (NSString* restrictTag in _manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) {
487                 _metabomRestrictedTagMap[configStr].insert([restrictTag UTF8String]);
488                 _configurations[configStr].metabomRestrictTags.insert([restrictTag UTF8String]);
489             }
490         }
491
492         _configurations[configStr].metabomTag = configTag;
493         _configurations[configStr].metabomTags.insert(configTag);
494         _configurations[configStr].platformName =
495             [_manifestDict[@"configurations"][configuration][@"platformName"] UTF8String];
496
497         if (endsWith(configStr, "InternalOS")) {
498             _configurations[configStr].disposition = "internal";
499             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("InternalOS"));
500         } else if (endsWith(configStr, "VendorOS")) {
501             _configurations[configStr].disposition = "internal";
502             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("VendorOS"));
503         } else if (endsWith(configStr, "VendorUIOS")) {
504             _configurations[configStr].disposition = "internal";
505             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("VendorUIOS"));
506         } else if (endsWith(configStr, "CarrierOS")) {
507             _configurations[configStr].disposition = "internal";
508             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("CarrierOS"));
509         } else if (endsWith(configStr, "FactoryOS")) {
510             _configurations[configStr].disposition = "internal";
511             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("FactoryOS"));
512         } else if (endsWith(configStr, "DesenseOS")) {
513             _configurations[configStr].disposition = "internal";
514             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DesenseOS"));
515         } else if (endsWith(configStr, "MinosOS")) {
516             _configurations[configStr].disposition = "minos";
517             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("MinosOS"));
518         } else if (endsWith(configStr, "DemoOS")) {
519             _configurations[configStr].disposition = "demo";
520             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DemoOS"));
521         } else if (endsWith(configStr, "MinosOS")) {
522             _configurations[configStr].disposition = "minos";
523             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("MinosOS"));
524         } else if (endsWith(configStr, "DeveloperOS")) {
525             _configurations[configStr].disposition = "user";
526             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DeveloperOS"));
527         } else if (endsWith(configStr, "OS")) {
528             _configurations[configStr].disposition = "user";
529             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("OS"));
530         }
531
532         for (NSString* architecture in _manifestDict[@"configurations"][configuration][@"architectures"]) {
533             //HACK until B&I stops mastering armv7s
534             if ([architecture isEqual:@"armv7s"]) break;
535             _configurations[configStr].architectures[[architecture UTF8String]] = Architecture();
536         }
537     }
538
539     setVersion([_manifestDict[@"manifest-version"] unsignedIntValue]);
540     setBuild([_manifestDict[@"build"] UTF8String]);
541     if (_manifestDict[@"dylibOrderFile"]) {
542         setDylibOrderFile([_manifestDict[@"dylibOrderFile"] UTF8String]);
543     }
544     if (_manifestDict[@"dirtyDataOrderFile"]) {
545         setDirtyDataOrderFile([_manifestDict[@"dirtyDataOrderFile"] UTF8String]);
546     }
547
548     if (populateIt)
549         populate(std::set<std::string>());
550 }
551
552 // Perform the initialization that populateIt=false omitted.
553 void Manifest::populate(const std::set<std::string>& overlays)
554 {
555     auto    metabom = MBMetabomOpen(metabomFile().c_str(), false);
556     auto    metabomEnumerator = MBIteratorNewWithPath(metabom, ".", "");
557     MBEntry entry;
558
559     // Collect every architecture from the configurations.
560     std::set<std::string> architectures;
561     for (auto& configuration : _configurations) {
562         for (auto& arch : configuration.second.architectures) {
563             architectures.insert(arch.first);
564         }
565     }
566
567     auto filterPath = [](std::string& entryPath) {
568         // Skip artifacts that happen to be in the build chain
569         if ( startsWith(entryPath, "/Applications/Xcode.app") ) {
570             return true;
571         }
572
573         // Skip variants we can't deal with
574         if ( endsWith(entryPath, "_profile.dylib") || endsWith(entryPath, "_debug.dylib") || endsWith(entryPath, "_profile") || endsWith(entryPath, "_debug") || endsWith(entryPath, "/CoreADI") ) {
575             return true;
576         }
577
578         // Skip images that are only used in InternalOS variants
579         if ( startsWith(entryPath, "/AppleInternal/") || startsWith(entryPath, "/usr/local/") || startsWith(entryPath, "/Developer/")) {
580             return true;
581         }
582
583         // Skip genCache generated dylibs
584         if ( endsWith(entryPath, "/System/Library/Caches/com.apple.xpc/sdk.dylib") || endsWith(entryPath, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib")) {
585             return true;
586         }
587
588         return false;
589     };
590
591     __block std::set<std::string> seenPaths;
592
593     // FIXME error handling (NULL metabom)
594
595     //First we iterate through the bom and build our objects
596     while ((entry = MBIteratorNext(metabomEnumerator))) {
597         BOMFSObject  fsObject = MBEntryGetFSObject(entry);
598         BOMFSObjType entryType = BOMFSObjectType(fsObject);
599         std::string  entryPath = BOMFSObjectPathName(fsObject);
600         if (entryPath[0] == '.') {
601             entryPath.erase(0, 1);
602         }
603
604         if (filterPath(entryPath))
605             continue;
606
607         MBTag tag;
608         auto  tagCount = MBEntryGetNumberOfProjectTags(entry);
609         bool isObjectFile = (entryType == BOMFileType) && BOMFSObjectIsBinaryObject(fsObject);
610         bool isSymlinkFile = entryType == BOMSymlinkType;
611         if ( (isObjectFile || isSymlinkFile) && MBEntryGetNumberOfProjectTags(entry) != 0 && tagCount != 0) {
612             if (tagCount == 1) {
613                 MBEntryGetProjectTags(entry, &tag);
614             } else {
615                 MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount);
616                 MBEntryGetProjectTags(entry, tags);
617
618                 //Sigh, we can have duplicate entries for the same tag, so build a set to work with
619                 std::set<std::string> tagStrs;
620                 std::map<std::string, MBTag> tagStrMap;
621                 for (auto i = 0; i < tagCount; ++i) {
622                     tagStrs.insert(MBMetabomGetProjectForTag(metabom, tags[i]));
623                     tagStrMap.insert(std::make_pair(MBMetabomGetProjectForTag(metabom, tags[i]), tags[i]));
624                 }
625
626                 if (tagStrs.size() > 1) {
627                     std::string projects;
628                     for (const auto& tagStr : tagStrs) {
629                         if (!projects.empty())
630                             projects += ", ";
631
632                         projects += "'" + tagStr + "'";
633                     }
634                     _diags.warning("Bom entry '%s' is claimed by multiple projects: %s, taking first entry", entryPath.c_str(), projects.c_str());
635                 }
636                 tag = tagStrMap[*tagStrs.begin()];
637                 free(tags);
638             }
639
640             std::string projectName = MBMetabomGetProjectForTag(metabom, tag);
641             tagCount = MBEntryGetNumberOfPackageTags(entry);
642             MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount);
643             MBEntryGetPackageTags(entry, tags);
644             std::set<std::string> tagStrs;
645
646             for (auto i = 0; i < tagCount; ++i) {
647                 tagStrs.insert(MBMetabomGetPackageForTag(metabom, tags[i]));
648             }
649
650             if (isObjectFile) {
651                 _metabomTagMap.insert(std::make_pair(entryPath, tagStrs));
652
653                 bool foundParser = false;
654                 for (const auto& overlay : overlays) {
655                     if (loadParsers(overlay + "/" + entryPath, entryPath, architectures)) {
656                         foundParser = true;
657                         _diags.verbose("Taking '%s' from overlay instead of dylib cache\n", entryPath.c_str());
658                         break;
659                     }
660                     if (_diags.hasError()) {
661                         _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
662                         _diags.clearError();
663                     }
664                 }
665
666                 if (!foundParser) {
667                     foundParser = loadParsers(projectPath(projectName) + "/" + entryPath, entryPath, architectures);
668                     if (_diags.hasError()) {
669                         _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
670                         _diags.clearError();
671                     }
672                 }
673
674                 if (foundParser)
675                     seenPaths.insert(entryPath);
676             } else if (isSymlinkFile) {
677                 const char* target = BOMFSObjectSymlinkTarget(fsObject);
678                 _symlinks.push_back({ entryPath, target });
679                 _metabomSymlinkTagMap.insert(std::make_pair(entryPath, tagStrs));
680             }
681         }
682     }
683
684     MBIteratorFree(metabomEnumerator);
685     MBMetabomFree(metabom);
686
687     // Add files from the overlays
688     for (const auto& overlay : overlays) {
689         iterateDirectoryTree("", overlay, ^bool(const std::string &dirPath) {
690             return false;
691         }, ^(const std::string &path, const struct stat &statBuf) {
692             std::string relativePath = path;
693             if (relativePath.size() < overlay.size())
694                 return;
695             relativePath = relativePath.substr(overlay.size());
696             if (relativePath.front() != '/')
697                 return;
698             if (filterPath(relativePath))
699                 return;
700             // Filter out dylibs we've seen already
701             if (seenPaths.count(relativePath))
702                 return;
703             if (loadParsers(path, relativePath, architectures)) {
704                 seenPaths.insert(relativePath);
705                 // Get the tags from libSystem, assuming that will work.
706                 std::set<std::string> tagStrs;
707                 auto it = _metabomTagMap.find("/usr/lib/libSystem.B.dylib");
708                 if (it != _metabomTagMap.end())
709                     tagStrs = it->second;
710                 _metabomTagMap.insert(std::make_pair(relativePath, tagStrs));
711                 _diags.verbose("Taking '%s' from overlay instead of dylib cache\n", relativePath.c_str());
712                 return;
713             }
714             if (_diags.hasError()) {
715                 _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
716                 _diags.clearError();
717             }
718         },
719         true, true);
720     }
721
722 }
723
724 void Manifest::insert(std::vector<DyldSharedCache::MappedMachO>& mappedMachOs, const CacheImageInfo& imageInfo) {
725     auto info = infoForUUID(imageInfo.uuid);
726     auto runtimePath = info.runtimePath;
727     mappedMachOs.emplace_back(runtimePath, info.mh, info.size, false, false, info.sliceFileOffset, 0, 0);
728 }
729
730 std::vector<DyldSharedCache::MappedMachO> Manifest::dylibsForCache(const std::string& configuration, const std::string& architecture)
731 {
732     std::vector<DyldSharedCache::MappedMachO> retval;
733     const auto&                               dylibs = _configurations[configuration].architectures[architecture].results.dylibs;
734     for (const auto& dylib : dylibs) {
735         if (dylib.second.included) {
736             insert(retval, dylib.second);
737         }
738     }
739     return retval;
740 }
741
742 std::vector<DyldSharedCache::MappedMachO> Manifest::otherDylibsAndBundles(const std::string& configuration, const std::string& architecture)
743 {
744     std::vector<DyldSharedCache::MappedMachO> retval;
745     const auto&                               dylibs = _configurations[configuration].architectures[architecture].results.dylibs;
746     for (const auto& dylib : dylibs) {
747         if (!dylib.second.included) {
748             const UUIDInfo& info = infoForUUID(dylib.second.uuid);
749             if ( ((MachOAnalyzer*)(info.mh))->canHavePrecomputedDlopenClosure(info.runtimePath.c_str(), ^(const char*) {}) )
750                 insert(retval, dylib.second);
751         }
752     }
753
754     const auto& bundles = _configurations[configuration].architectures[architecture].results.bundles;
755     for (const auto& bundle : bundles) {
756         const UUIDInfo& info = infoForUUID(bundle.second.uuid);
757         if ( ((MachOAnalyzer*)(info.mh))->canHavePrecomputedDlopenClosure(info.runtimePath.c_str(), ^(const char*) {}) )
758             insert(retval, bundle.second);
759     }
760
761     return retval;
762 }
763
764 std::vector<DyldSharedCache::MappedMachO> Manifest::mainExecutables(const std::string& configuration, const std::string& architecture)
765 {
766     std::vector<DyldSharedCache::MappedMachO> retval;
767     const auto&                               executables = _configurations[configuration].architectures[architecture].results.executables;
768     for (const auto& executable : executables) {
769         insert(retval, executable.second);
770     }
771
772     return retval;
773 }
774
775 #pragma clang diagnostic push
776 #pragma clang diagnostic ignored "-Wrange-loop-analysis"
777 bool Manifest::filterForConfig(const std::string& configName)
778 {
779     for (const auto configuration : _configurations) {
780         if (configName == configuration.first) {
781             std::map<std::string, Configuration> filteredConfigs;
782             filteredConfigs[configName] = configuration.second;
783
784             _configurations = filteredConfigs;
785
786             for (auto& arch : configuration.second.architectures) {
787                 arch.second.results = Manifest::Results();
788             }
789             return true;
790         }
791     }
792     return false;
793 }
794 #pragma clang diagnostic pop
795
796 std::set<std::string> Manifest::resultsForConfiguration(const std::string& configName) {
797     std::set<std::string> results;
798     NSDictionary* configurationResults = _manifestDict[@"results"][[NSString stringWithUTF8String:configName.c_str()]];
799     for (NSString* arch in configurationResults) {
800         NSDictionary* dylibs = configurationResults[arch][@"dylibs"];
801         for (NSString* dylib in dylibs) {
802             NSDictionary* dylibDict = dylibs[dylib];
803             if ([dylibDict[@"included"] boolValue])
804                 results.insert([dylib UTF8String]);
805         }
806     }
807     return results;
808 }
809
810 void Manifest::dedupeDispositions(void) {
811     // Since this is all hacky and inference based for now only do it for iOS until XBS
812     // is reved to give us real info. All the other platforms are way smaller anyway.
813     if (_platform != Platform::iOS)
814         return;
815
816     std::map<std::pair<std::string, std::string>, std::set<std::string>> dispositionSets;
817
818     for (const auto& configuration : _configurations) {
819         dispositionSets[std::make_pair(configuration.second.device, configuration.second.disposition)].insert(configuration.first);
820     }
821
822     for (const auto& dSet : dispositionSets) {
823         for (const auto &c1 : dSet.second) {
824             for (const auto &c2 : dSet.second) {
825                 _configurations[c1].metabomTags.insert(_configurations[c2].metabomTag);
826             }
827         }
828     }
829 }
830
831 void Manifest::calculateClosure()
832 {
833     auto closureSemaphore = dispatch_semaphore_create(32);
834     auto closureGroup = dispatch_group_create();
835     auto closureQueue = dispatch_queue_create("com.apple.dyld.cache.closure", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INITIATED, 0));
836
837     dedupeDispositions();
838     for (auto& config : _configurations) {
839         for (auto& arch : config.second.architectures) {
840             dispatch_semaphore_wait(closureSemaphore, DISPATCH_TIME_FOREVER);
841             dispatch_group_async(closureGroup, closureQueue, [&] {
842                 calculateClosure(config.first, arch.first);
843                 dispatch_semaphore_signal(closureSemaphore);
844             });
845         }
846     }
847
848     dispatch_group_wait(closureGroup, DISPATCH_TIME_FOREVER);
849 }
850
851 void Manifest::remove(const std::string& config, const std::string& arch)
852 {
853     if (_configurations.count(config))
854         _configurations[config].architectures.erase(arch);
855 }
856
857
858 void Manifest::calculateClosure(const std::string& configuration, const std::string& architecture)
859 {
860     __block auto&   configManifest = _configurations[configuration];
861     __block auto&   archManifest = _configurations[configuration].architectures[architecture];
862     __block std::set<UUID> newUuids;
863     std::set<UUID>         processedUuids;
864     std::set<UUID>         cachedUUIDs;
865
866     // Seed anchors
867     for (auto& uuidInfo : _uuidMap) {
868         auto info = uuidInfo.second;
869         if (info.arch != architecture) {
870             continue;
871         }
872         
873         auto i = _metabomTagMap.find(info.runtimePath);
874         assert(i != _metabomTagMap.end());
875         auto tags = i->second;
876         if (!is_disjoint(tags, configManifest.metabomTags)) {
877             newUuids.insert(info.uuid);
878         }
879     }
880
881     __block std::set<UUID>         removedUUIDs;
882
883     // Pull in all dependencies
884     while (!newUuids.empty()) {
885         std::set<UUID> uuidsToProcess = newUuids;
886         newUuids.clear();
887
888         for (const auto& uuid : uuidsToProcess) {
889             if (processedUuids.count(uuid) > 0) {
890                 continue;
891             }
892             processedUuids.insert(uuid);
893
894             const MachOAnalyzer* mh = machOForUUID(uuid);
895             auto runtimePath = runtimePathForUUID(uuid);
896             assert(mh != nullptr);
897
898             mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
899                 auto i = _installNameMap.find(std::make_pair(loadPath, architecture));
900                 if (i != _installNameMap.end())
901                 newUuids.insert(i->second);
902             });
903
904             if (mh->isDylib()) {
905                 // Add the dylib to the results
906                 if (archManifest.results.dylibs.count(uuid) == 0 ) {
907                     archManifest.results.dylibs[uuid].uuid = uuid;
908                     archManifest.results.dylibs[uuid].installname = mh->installName();
909                 }
910
911                 // HACK to insert device specific dylib closures into all caches
912                 if ( (strcmp(mh->installName(), "/System/Library/Caches/com.apple.xpc/sdk.dylib") == 0)
913                     || (strcmp(mh->installName(), "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") == 0) ) {
914                     archManifest.results.exclude(mh, "Device specific dylib");
915                     continue;
916                 }
917
918                 __block std::set<std::string> reasons;
919                 if (mh->canBePlacedInDyldCache(runtimePath.c_str(), ^(const char* reason) { reasons.insert(reason); })) {
920                     auto i = _metabomTagMap.find(runtimePath);
921                     assert(i != _metabomTagMap.end());
922                     auto restrictions = _metabomRestrictedTagMap.find(configuration);
923                     if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
924                         archManifest.results.exclude(mh, "Dylib '" + runtimePath + "' removed due to explict restriction");
925                     }
926
927                     // It can be placed in the cache, grab its dependents and queue them for inclusion
928                     cachedUUIDs.insert(uuid);
929                 } else {
930                     // It can't be placed in the cache, print out the reasons why
931                     std::string reasonString = "Rejected from cached dylibs: " + runtimePath + " " + architecture + " (\"";
932                     for (auto i = reasons.begin(); i != reasons.end(); ++i) {
933                         reasonString += *i;
934                         if (i != --reasons.end()) {
935                             reasonString += "\", \"";
936                         }
937                     }
938                     reasonString += "\")";
939                     archManifest.results.exclude(mh, reasonString);
940                     removedUUIDs.insert(uuid);
941                 }
942             } else if (mh->isBundle()) {
943                 if (archManifest.results.bundles.count(uuid) == 0) {
944                     archManifest.results.bundles[uuid].uuid = uuid;
945                 }
946             } else if (mh->isMainExecutable()) {
947                 //HACK exclude all launchd and installd variants until we can do something about xpcd_cache.dylib and friends
948                 if (runtimePath == "/sbin/launchd"
949                     || runtimePath == "/usr/local/sbin/launchd.debug"
950                     || runtimePath == "/usr/local/sbin/launchd.development"
951                     || runtimePath == "/usr/libexec/installd") {
952                     continue;
953                 }
954                 if (archManifest.results.executables.count(uuid) == 0) {
955                     archManifest.results.executables[uuid].uuid = uuid;
956                 }
957             }
958         }
959     }
960
961     __block bool                   doAgain = true;
962
963     //Trim out dylibs that are missing dependencies
964     while ( doAgain ) {
965         doAgain = false;
966         for (const auto& uuid : cachedUUIDs) {
967             __block std::set<std::string> badDependencies;
968             const dyld3::MachOAnalyzer* mh = machOForUUID(uuid);
969             mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
970                 if (isWeak)
971                     return;
972
973                 auto i = _installNameMap.find(std::make_pair(loadPath, architecture));
974                 if (i == _installNameMap.end() || removedUUIDs.count(i->second)) {
975                     removedUUIDs.insert(uuid);
976                     badDependencies.insert(loadPath);
977                     doAgain = true;
978                 }
979
980                 if (badDependencies.size()) {
981                     std::string reasonString = "Rejected from cached dylibs: " + std::string(mh->installName()) + " " + architecture + " (\"";
982                     for (auto i = badDependencies.begin(); i != badDependencies.end(); ++i) {
983                         reasonString += *i;
984                         if (i != --badDependencies.end()) {
985                             reasonString += "\", \"";
986                         }
987                     }
988                     reasonString += "\")";
989                     archManifest.results.exclude(mh, reasonString);
990                 }
991             });
992         }
993
994         for (const auto& removedUUID : removedUUIDs) {
995             cachedUUIDs.erase(removedUUID);
996         }
997     }
998
999     //Trim out excluded leaf dylibs
1000     __block std::set<std::string> linkedDylibs;
1001
1002     for(const auto& uuid : cachedUUIDs) {
1003         const dyld3::MachOAnalyzer* mh = machOForUUID(uuid);
1004         mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
1005             linkedDylibs.insert(loadPath);
1006         });
1007     }
1008
1009     for(const auto& uuid : cachedUUIDs) {
1010         auto info = infoForUUID(uuid);
1011         auto i = _metabomTagMap.find(info.runtimePath);
1012         assert(i != _metabomTagMap.end());
1013         auto exclusions = _metabomExcludeTagMap.find(configuration);
1014         if (exclusions == _metabomExcludeTagMap.end() || is_disjoint(exclusions->second, i->second))
1015             continue;
1016
1017         if (linkedDylibs.count(info.installName) != 0)
1018             continue;
1019
1020         archManifest.results.exclude(*this, info.uuid, "Dylib '" + info.runtimePath + "' excluded leaf node");
1021     }
1022 }
1023
1024 void Manifest::writeJSON(const std::string& path) {
1025     NSMutableDictionary* jsonDict = [[NSMutableDictionary alloc] init];
1026     for (auto& configuration : _configurations) {
1027         jsonDict[cppToObjStr(configuration.first)] = [[NSMutableDictionary alloc] init];
1028
1029         for (auto& arch : configuration.second.architectures) {
1030             NSMutableOrderedSet* includedDylibsSet = [[NSMutableOrderedSet alloc] init];
1031             NSMutableOrderedSet* executablesSet = [[NSMutableOrderedSet alloc] init];
1032             NSMutableOrderedSet* otherSet = [[NSMutableOrderedSet alloc] init];
1033             for (auto& dylib : arch.second.results.dylibs) {
1034                 NSString *runtimePath = cppToObjStr(runtimePathForUUID(dylib.second.uuid));
1035                 if (dylib.second.included) {
1036                     [includedDylibsSet addObject:runtimePath];
1037                 } else {
1038                     [otherSet addObject:runtimePath];
1039                 }
1040             }
1041
1042             for (auto& executable : arch.second.results.executables) {
1043                 NSString *runtimePath = cppToObjStr(runtimePathForUUID(executable.second.uuid));
1044                 [executablesSet addObject:runtimePath];
1045             }
1046
1047             for (auto& bundle : arch.second.results.bundles) {
1048                 NSString *runtimePath = cppToObjStr(runtimePathForUUID(bundle.second.uuid));
1049                 [otherSet addObject:runtimePath];
1050             }
1051
1052             [includedDylibsSet sortUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
1053                 return [obj1 compare:obj2];
1054             }];
1055
1056             [executablesSet sortUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
1057                 return [obj1 compare:obj2];
1058             }];
1059
1060             [otherSet sortUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
1061                 return [obj1 compare:obj2];
1062             }];
1063
1064             jsonDict[cppToObjStr(configuration.first)][cppToObjStr(arch.first)] = @{ @"cachedDylibs" : [includedDylibsSet array], @"mainExecutables" : [executablesSet array], @"other" : [otherSet array]};;
1065         }
1066     }
1067
1068     NSError* error = nil;
1069     NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDict options:0x0 error:&error];
1070     (void)[jsonData writeToFile:cppToObjStr(path) atomically:YES];
1071 }
1072
1073 void Manifest::write(const std::string& path)
1074 {
1075     if (path.empty())
1076         return;
1077
1078     NSMutableDictionary* cacheDict = [[NSMutableDictionary alloc] init];
1079     NSMutableDictionary* projectDict = [[NSMutableDictionary alloc] init];
1080     NSMutableDictionary* configurationsDict = [[NSMutableDictionary alloc] init];
1081     NSMutableDictionary* resultsDict = [[NSMutableDictionary alloc] init];
1082
1083     cacheDict[@"manifest-version"] = @(version());
1084     cacheDict[@"build"] = cppToObjStr(build());
1085     cacheDict[@"dylibOrderFile"] = cppToObjStr(dylibOrderFile());
1086     cacheDict[@"dirtyDataOrderFile"] = cppToObjStr(dirtyDataOrderFile());
1087     cacheDict[@"metabomFile"] = cppToObjStr(metabomFile());
1088
1089     cacheDict[@"projects"] = projectDict;
1090     cacheDict[@"results"] = resultsDict;
1091     cacheDict[@"configurations"] = configurationsDict;
1092
1093     for (const auto& project : projects()) {
1094         NSMutableArray* sources = [[NSMutableArray alloc] init];
1095
1096         for (const auto& source : project.second.sources) {
1097             [sources addObject:cppToObjStr(source)];
1098         }
1099
1100         projectDict[cppToObjStr(project.first)] = sources;
1101     }
1102
1103     for (auto& configuration : _configurations) {
1104         NSMutableArray* archArray = [[NSMutableArray alloc] init];
1105         for (auto& arch : configuration.second.architectures) {
1106             [archArray addObject:cppToObjStr(arch.first)];
1107         }
1108
1109         NSMutableArray* excludeTags = [[NSMutableArray alloc] init];
1110         for (const auto& excludeTag : configuration.second.metabomExcludeTags) {
1111             [excludeTags addObject:cppToObjStr(excludeTag)];
1112         }
1113
1114         configurationsDict[cppToObjStr(configuration.first)] = @{
1115             @"platformName" : cppToObjStr(configuration.second.platformName),
1116             @"metabomTag" : cppToObjStr(configuration.second.metabomTag),
1117             @"metabomExcludeTags" : excludeTags,
1118             @"architectures" : archArray
1119         };
1120     }
1121
1122     for (auto& configuration : _configurations) {
1123         NSMutableDictionary* archResultsDict = [[NSMutableDictionary alloc] init];
1124         for (auto& arch : configuration.second.architectures) {
1125             NSMutableDictionary* dylibsDict = [[NSMutableDictionary alloc] init];
1126             NSMutableArray* warningsArray = [[NSMutableArray alloc] init];
1127             NSMutableDictionary* devRegionsDict = [[NSMutableDictionary alloc] init];
1128             NSMutableDictionary* prodRegionsDict = [[NSMutableDictionary alloc] init];
1129             NSString* prodCDHash = cppToObjStr(arch.second.results.productionCache.cdHash);
1130             NSString* devCDHash = cppToObjStr(arch.second.results.developmentCache.cdHash);
1131
1132             for (auto& dylib : arch.second.results.dylibs) {
1133                 NSMutableDictionary* dylibDict = [[NSMutableDictionary alloc] init];
1134                 if (dylib.second.included) {
1135                     dylibDict[@"included"] = @YES;
1136                 } else {
1137                     dylibDict[@"included"] = @NO;
1138                     dylibDict[@"exclusionInfo"] = cppToObjStr(dylib.second.exclusionInfo);
1139                 }
1140                 dylibsDict[cppToObjStr(dylib.second.installname)] = dylibDict;
1141             }
1142
1143             for (auto& warning : arch.second.results.warnings) {
1144                 [warningsArray addObject:cppToObjStr(warning)];
1145             }
1146
1147             BOOL built = arch.second.results.failure.empty();
1148             archResultsDict[cppToObjStr(arch.first)] = @{
1149                 @"dylibs" : dylibsDict,
1150                 @"built" : @(built),
1151                 @"failure" : cppToObjStr(arch.second.results.failure),
1152                 @"productionCache" : @{ @"cdhash" : prodCDHash, @"regions" : prodRegionsDict },
1153                 @"developmentCache" : @{ @"cdhash" : devCDHash, @"regions" : devRegionsDict },
1154                 @"warnings" : warningsArray
1155             };
1156         }
1157         resultsDict[cppToObjStr(configuration.first)] = archResultsDict;
1158     }
1159
1160     switch (platform()) {
1161     case Platform::iOS:
1162         cacheDict[@"platform"] = @"ios";
1163         break;
1164     case Platform::tvOS:
1165         cacheDict[@"platform"] = @"tvos";
1166         break;
1167     case Platform::watchOS:
1168         cacheDict[@"platform"] = @"watchos";
1169         break;
1170     case Platform::bridgeOS:
1171         cacheDict[@"platform"] = @"bridgeos";
1172         break;
1173     case Platform::macOS:
1174         cacheDict[@"platform"] = @"macos";
1175         break;
1176     case Platform::unknown:
1177     case Platform::iOSMac:
1178     case Platform::iOS_simulator:
1179     case Platform::tvOS_simulator:
1180     case Platform::watchOS_simulator:
1181     case Platform::driverKit:
1182         cacheDict[@"platform"] = @"unknown";
1183         break;
1184     }
1185
1186     NSError* error = nil;
1187     NSData*  outData = [NSPropertyListSerialization dataWithPropertyList:cacheDict
1188                                                                  format:NSPropertyListBinaryFormat_v1_0
1189                                                                 options:0
1190                                                                   error:&error];
1191     (void)[outData writeToFile:cppToObjStr(path) atomically:YES];
1192 }
1193
1194
1195 void Manifest::forEachMachO(std::string configuration,
1196                             std::function<void(const std::string &buildPath, const std::string &runtimePath, const std::string &arch, bool shouldBeExcludedIfLeaf)> lambda) {
1197     for (auto& uuidInfo : _uuidMap) {
1198         auto i = _metabomTagMap.find(uuidInfo.second.runtimePath);
1199         assert(i != _metabomTagMap.end());
1200         auto restrictions = _metabomRestrictedTagMap.find(configuration);
1201         if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
1202             continue;
1203         }
1204         auto& configManifest = _configurations[configuration];
1205         auto exclusions = _metabomExcludeTagMap.find(configuration);
1206         bool isExcluded = (exclusions != _metabomExcludeTagMap.end()) && !is_disjoint(exclusions->second, i->second);
1207         bool isAnchor = !is_disjoint(i->second, configManifest.metabomTags);
1208         bool shouldBeExcludedIfLeaf = isExcluded || !isAnchor;
1209         lambda(uuidInfo.second.buildPath, uuidInfo.second.runtimePath, uuidInfo.second.arch, shouldBeExcludedIfLeaf);
1210     }
1211 }
1212
1213
1214 void Manifest::forEachSymlink(std::string configuration,
1215                             std::function<void(const std::string &fromPath, const std::string &toPath)> lambda) {
1216     for (const auto& symlink : _symlinks) {
1217         auto i = _metabomSymlinkTagMap.find(symlink.first);
1218         assert(i != _metabomSymlinkTagMap.end());
1219         auto restrictions = _metabomRestrictedTagMap.find(configuration);
1220         if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
1221             continue;
1222         }
1223         lambda(symlink.first, symlink.second);
1224     }
1225 }
1226
1227 } //namespace dyld3