dyld-625.13.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.archName = arch;
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.pathPrefixes = { "./Root/" };
289     options.dylibOrdering = parseOrderFile(loadOrderFile(_dylibOrderFile));
290     options.dirtyDataSegmentOrdering = parseOrderFile(loadOrderFile(_dirtyDataOrderFile));
291
292     dyld3::BuildQueueEntry queueEntry;
293     retval.configNames = configs;
294     retval.options = options;
295     retval.outputPath = outputPath;
296     retval.dylibsForCache = dylibsForCache(*configs.begin(), arch);
297     retval.otherDylibsAndBundles = otherDylibsAndBundles(*configs.begin(), arch);
298     retval.mainExecutables = mainExecutables(*configs.begin(), arch);
299
300     return retval;
301 }
302
303 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)
304 {
305     assert(!_diags.hasError());
306
307     const MachOFile* mf = reinterpret_cast<const MachOFile*>(p);
308     const std::string archName = mf->archName();
309     if ( archName == "unknown" ) {
310         // Clear the error and punt
311         _diags.verbose("Dylib located at '%s' has unknown architecture\n", runtimePath.c_str());
312         return false;
313     }
314     if ( architectures.count(archName) == 0 )
315         return false;
316
317     const MachOAnalyzer* ma = reinterpret_cast<const MachOAnalyzer*>(p);
318     if ( !ma->validMachOForArchAndPlatform(_diags, sliceLength, runtimePath.c_str(), archName.c_str(), _platform) ) {
319         // Clear the error and punt
320         _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
321         _diags.clearError();
322         return false;
323     }
324
325     // if this file uses zero-fill expansion, then mapping whole file in one blob will not work
326     // remapIfZeroFill() will remap the file
327     closure::FileSystemPhysical fileSystem;
328     closure::LoadedFileInfo info;
329     info.fileContent                = p;
330     info.fileContentLen             = sliceLength;
331     info.sliceOffset                = 0;
332     info.sliceLen                   = sliceLength;
333     info.inode                      = 0;
334     info.mtime                      = 0;
335     info.unload                     = nullptr;
336     ma = ma->remapIfZeroFill(_diags, fileSystem, info);
337
338     if (ma == nullptr) {
339         _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
340         _diags.clearError();
341         return false;
342     }
343
344     uuid_t uuid;
345     ma->getUuid(uuid);
346
347     if ( ma->isDylib() ) {
348         std::string installName = ma->installName();
349         auto index = std::make_pair(installName, archName);
350         auto i = _installNameMap.find(index);
351
352         if ( installName == "/System/Library/Caches/com.apple.xpc/sdk.dylib"
353             || installName == "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib" ) {
354             // HACK to deal with device specific dylibs. These must not be inseted into the installNameMap
355             _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, installName)));
356         } else if (i == _installNameMap.end()) {
357             _installNameMap.insert(std::make_pair(index, uuid));
358             _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, installName)));
359             if (installName[0] != '@' && installName != runtimePath) {
360                 _diags.warning("Dylib located at '%s' has  installname '%s'", runtimePath.c_str(), installName.c_str());
361             }
362         } else {
363             auto info = infoForUUID(i->second);
364             _diags.warning("Multiple dylibs claim installname '%s' ('%s' and '%s')", installName.c_str(), runtimePath.c_str(), info.runtimePath.c_str());
365
366             // This is the "Good" one, overwrite
367             if (runtimePath == installName) {
368                 _uuidMap.erase(uuid);
369                 _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, installName)));
370             }
371         }
372     } else {
373         _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, "")));
374     }
375     return true;
376 }
377
378 //FIXME: assert we have not errored first
379 bool Manifest::loadParsers(const std::string& buildPath, const std::string& runtimePath, const std::set<std::string>& architectures)
380 {
381     __block bool retval = false;
382     const void*  p = (uint8_t*)(-1);
383     struct stat  stat_buf;
384
385     std::tie(p, stat_buf) = fileCache.cacheLoad(_diags, buildPath);
386
387     if (p == (uint8_t*)(-1)) {
388         return false;
389     }
390
391     if ( const FatFile* fh = FatFile::isFatFile(p) ) {
392         fh->forEachSlice(_diags, stat_buf.st_size, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
393             if (loadParser(sliceStart, sliceSize, (uintptr_t)sliceStart-(uintptr_t)p, runtimePath, buildPath, architectures))
394                 retval = true;
395         });
396     } else {
397         return loadParser(p, stat_buf.st_size, 0, runtimePath, buildPath, architectures);
398     }
399     return retval;
400 }
401
402
403 const Manifest::UUIDInfo& Manifest::infoForUUID(const UUID& uuid) const {
404     auto i = _uuidMap.find(uuid);
405     assert(i != _uuidMap.end());
406     return i->second;
407 }
408
409 const Manifest::UUIDInfo Manifest::infoForInstallNameAndarch(const std::string& installName, const std::string arch) const  {
410     UUIDInfo retval;
411     auto uuidI = _installNameMap.find(std::make_pair(installName, arch));
412     if (uuidI == _installNameMap.end())
413         return UUIDInfo();
414
415     auto i = _uuidMap.find(uuidI->second);
416     if (i == _uuidMap.end())
417     return UUIDInfo();
418     return i->second;
419 }
420
421 const MachOAnalyzer* Manifest::machOForUUID(const UUID& uuid) const {
422     return infoForUUID(uuid).mh;
423 }
424
425 const std::string Manifest::buildPathForUUID(const UUID& uuid) {
426     return infoForUUID(uuid).buildPath;
427 }
428
429 const std::string Manifest::runtimePathForUUID(const UUID& uuid) {
430     return infoForUUID(uuid).runtimePath;
431 }
432
433 const std::string& Manifest::installNameForUUID(const UUID& uuid) {
434     return infoForUUID(uuid).installName;
435 }
436
437
438 Manifest::Manifest(Diagnostics& D, const std::string& path, bool onlyParseManifest)  : Manifest(D, path, std::set<std::string>(), onlyParseManifest)
439 {
440 }
441
442 Manifest::Manifest(Diagnostics& D, const std::string& path, const std::set<std::string>& overlays, bool onlyParseManifest) :
443     _diags(D)
444 {
445     _manifestDict = [NSMutableDictionary dictionaryWithContentsOfFile:cppToObjStr(path)];
446     if (!_manifestDict)
447         return;
448     NSString*            platStr = _manifestDict[@"platform"];
449     std::set<std::string> architectures;
450
451     if (platStr == nullptr)
452         platStr = @"ios";
453     std::string platformString = [platStr UTF8String];
454     setMetabomFile([_manifestDict[@"metabomFile"] UTF8String]);
455
456     if (platformString == "ios") {
457         setPlatform(dyld3::Platform::iOS);
458     } else if ( (platformString == "tvos") || (platformString == "atv") ) {
459         setPlatform(dyld3::Platform::tvOS);
460     } else if ( (platformString == "watchos") || (platformString == "watch") ) {
461         setPlatform(dyld3::Platform::watchOS);
462     } else if ( (platformString == "bridgeos") || (platformString == "bridge") ) {
463         setPlatform(dyld3::Platform::bridgeOS);
464     } else if ( (platformString == "macos") || (platformString == "osx") ) {
465         setPlatform(dyld3::Platform::macOS);
466     } else {
467         //Fixme should we error?
468         setPlatform(dyld3::Platform::iOS);
469     }
470
471     for (NSString* project in _manifestDict[@"projects"]) {
472         for (NSString* source in _manifestDict[@"projects"][project]) {
473             addProjectSource([project UTF8String], [source UTF8String]);
474         }
475     }
476
477     for (NSString* configuration in _manifestDict[@"configurations"]) {
478         std::string configStr = [configuration UTF8String];
479         std::string configTag = [_manifestDict[@"configurations"][configuration][@"metabomTag"] UTF8String];
480
481         if (_manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) {
482             for (NSString* excludeTag in _manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) {
483                 _metabomExcludeTagMap[configStr].insert([excludeTag UTF8String]);
484                 _configurations[configStr].metabomExcludeTags.insert([excludeTag UTF8String]);
485             }
486         }
487
488         if (_manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) {
489             for (NSString* restrictTag in _manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) {
490                 _metabomRestrictedTagMap[configStr].insert([restrictTag UTF8String]);
491                 _configurations[configStr].metabomRestrictTags.insert([restrictTag UTF8String]);
492             }
493         }
494
495         _configurations[configStr].metabomTag = configTag;
496         _configurations[configStr].metabomTags.insert(configTag);
497         _configurations[configStr].platformName =
498             [_manifestDict[@"configurations"][configuration][@"platformName"] UTF8String];
499
500         if (endsWith(configStr, "InternalOS")) {
501             _configurations[configStr].disposition = "internal";
502             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("InternalOS"));
503         } else if (endsWith(configStr, "VendorOS")) {
504             _configurations[configStr].disposition = "internal";
505             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("VendorOS"));
506         } else if (endsWith(configStr, "VendorUIOS")) {
507             _configurations[configStr].disposition = "internal";
508             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("VendorUIOS"));
509         } else if (endsWith(configStr, "CarrierOS")) {
510             _configurations[configStr].disposition = "internal";
511             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("CarrierOS"));
512         } else if (endsWith(configStr, "FactoryOS")) {
513             _configurations[configStr].disposition = "internal";
514             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("FactoryOS"));
515         } else if (endsWith(configStr, "DesenseOS")) {
516             _configurations[configStr].disposition = "internal";
517             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DesenseOS"));
518         } else if (endsWith(configStr, "MinosOS")) {
519             _configurations[configStr].disposition = "minos";
520             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("MinosOS"));
521         } else if (endsWith(configStr, "DemoOS")) {
522             _configurations[configStr].disposition = "demo";
523             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DemoOS"));
524         } else if (endsWith(configStr, "MinosOS")) {
525             _configurations[configStr].disposition = "minos";
526             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("MinosOS"));
527         } else if (endsWith(configStr, "DeveloperOS")) {
528             _configurations[configStr].disposition = "user";
529             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DeveloperOS"));
530         } else if (endsWith(configStr, "OS")) {
531             _configurations[configStr].disposition = "user";
532             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("OS"));
533         }
534
535         for (NSString* architecutre in _manifestDict[@"configurations"][configuration][@"architectures"]) {
536             //HACK until B&I stops mastering armv7s
537             if ([architecutre isEqual:@"armv7s"]) break;
538             _configurations[configStr].architectures[[architecutre UTF8String]] = Architecture();
539             architectures.insert([architecutre UTF8String]);
540         }
541     }
542
543     setVersion([_manifestDict[@"manifest-version"] unsignedIntValue]);
544     setBuild([_manifestDict[@"build"] UTF8String]);
545     if (_manifestDict[@"dylibOrderFile"]) {
546         setDylibOrderFile([_manifestDict[@"dylibOrderFile"] UTF8String]);
547     }
548     if (_manifestDict[@"dirtyDataOrderFile"]) {
549         setDirtyDataOrderFile([_manifestDict[@"dirtyDataOrderFile"] UTF8String]);
550     }
551
552     if (onlyParseManifest)
553         return;
554
555     auto    metabom = MBMetabomOpen(metabomFile().c_str(), false);
556     auto    metabomEnumerator = MBIteratorNewWithPath(metabom, ".", "");
557     MBEntry entry;
558
559     // FIXME error handling (NULL metabom)
560
561     //First we iterate through the bom and build our objects
562     while ((entry = MBIteratorNext(metabomEnumerator))) {
563         BOMFSObject  fsObject = MBEntryGetFSObject(entry);
564         BOMFSObjType entryType = BOMFSObjectType(fsObject);
565         std::string  entryPath = BOMFSObjectPathName(fsObject);
566         if (entryPath[0] == '.') {
567             entryPath.erase(0, 1);
568         }
569
570         // Skip artifacts that happen to be in the build chain
571         if ( startsWith(entryPath, "/Applications/Xcode.app") ) {
572             continue;
573         }
574
575         // Skip variants we can't deal with
576         if ( endsWith(entryPath, "_profile.dylib") || endsWith(entryPath, "_debug.dylib") || endsWith(entryPath, "_profile") || endsWith(entryPath, "_debug") || endsWith(entryPath, "/CoreADI") ) {
577             continue;
578         }
579
580         // Skip images that are only used in InternalOS variants
581         if ( startsWith(entryPath, "/AppleInternal/") || startsWith(entryPath, "/usr/local/") || startsWith(entryPath, "/Developer/")) {
582             continue;
583         }
584         
585         // Skip genCache generated dylibs
586         if ( endsWith(entryPath, "/System/Library/Caches/com.apple.xpc/sdk.dylib") || endsWith(entryPath, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib")) {
587             continue;
588         }
589
590         MBTag tag;
591         auto  tagCount = MBEntryGetNumberOfProjectTags(entry);
592         bool isObjectFile = (entryType == BOMFileType) && BOMFSObjectIsBinaryObject(fsObject);
593         bool isSymlinkFile = entryType == BOMSymlinkType;
594         if ( (isObjectFile || isSymlinkFile) && MBEntryGetNumberOfProjectTags(entry) != 0 && tagCount != 0) {
595             if (tagCount == 1) {
596                 MBEntryGetProjectTags(entry, &tag);
597             } else {
598                 MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount);
599                 MBEntryGetProjectTags(entry, tags);
600
601                 //Sigh, we can have duplicate entries for the same tag, so build a set to work with
602                 std::set<std::string> tagStrs;
603                 std::map<std::string, MBTag> tagStrMap;
604                 for (auto i = 0; i < tagCount; ++i) {
605                     tagStrs.insert(MBMetabomGetProjectForTag(metabom, tags[i]));
606                     tagStrMap.insert(std::make_pair(MBMetabomGetProjectForTag(metabom, tags[i]), tags[i]));
607                 }
608
609                 if (tagStrs.size() > 1) {
610                     std::string projects;
611                     for (const auto& tagStr : tagStrs) {
612                         if (!projects.empty())
613                             projects += ", ";
614
615                         projects += "'" + tagStr + "'";
616                     }
617                     _diags.warning("Bom entry '%s' is claimed by multiple projects: %s, taking first entry", entryPath.c_str(), projects.c_str());
618                 }
619                 tag = tagStrMap[*tagStrs.begin()];
620                 free(tags);
621             }
622
623             std::string projectName = MBMetabomGetProjectForTag(metabom, tag);
624             tagCount = MBEntryGetNumberOfPackageTags(entry);
625             MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount);
626             MBEntryGetPackageTags(entry, tags);
627             std::set<std::string> tagStrs;
628
629             for (auto i = 0; i < tagCount; ++i) {
630                 tagStrs.insert(MBMetabomGetPackageForTag(metabom, tags[i]));
631             }
632
633             if (isObjectFile) {
634                 _metabomTagMap.insert(std::make_pair(entryPath, tagStrs));
635
636                 bool foundParser = false;
637                 for (const auto& overlay : overlays) {
638                     if (loadParsers(overlay + "/" + entryPath, entryPath, architectures)) {
639                         foundParser = true;
640                         _diags.verbose("Taking '%s' from overlay instead of dylib cache\n", entryPath.c_str());
641                         break;
642                     }
643                     if (_diags.hasError()) {
644                         _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
645                         _diags.clearError();
646                     }
647                 }
648
649                 if (!foundParser) {
650                     (void)loadParsers(projectPath(projectName) + "/" + entryPath, entryPath, architectures);
651                     if (_diags.hasError()) {
652                         _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
653                         _diags.clearError();
654                     } else {
655                         foundParser = true;
656                     }
657                 }
658             } else if (isSymlinkFile) {
659                 const char* target = BOMFSObjectSymlinkTarget(fsObject);
660                 _symlinks.push_back({ entryPath, target });
661                 _metabomSymlinkTagMap.insert(std::make_pair(entryPath, tagStrs));
662             }
663         }
664     }
665
666     MBIteratorFree(metabomEnumerator);
667     MBMetabomFree(metabom);
668 }
669
670 void Manifest::insert(std::vector<DyldSharedCache::MappedMachO>& mappedMachOs, const CacheImageInfo& imageInfo) {
671     auto info = infoForUUID(imageInfo.uuid);
672     auto runtimePath = info.runtimePath;
673     mappedMachOs.emplace_back(runtimePath, info.mh, info.size, false, false, info.sliceFileOffset, 0, 0);
674 }
675
676 std::vector<DyldSharedCache::MappedMachO> Manifest::dylibsForCache(const std::string& configuration, const std::string& architecture)
677 {
678     std::vector<DyldSharedCache::MappedMachO> retval;
679     const auto&                               dylibs = _configurations[configuration].architectures[architecture].results.dylibs;
680     for (const auto& dylib : dylibs) {
681         if (dylib.second.included) {
682             insert(retval, dylib.second);
683         }
684     }
685     return retval;
686 }
687
688 std::vector<DyldSharedCache::MappedMachO> Manifest::otherDylibsAndBundles(const std::string& configuration, const std::string& architecture)
689 {
690     std::vector<DyldSharedCache::MappedMachO> retval;
691     const auto&                               dylibs = _configurations[configuration].architectures[architecture].results.dylibs;
692     for (const auto& dylib : dylibs) {
693         if (!dylib.second.included) {
694             const UUIDInfo& info = infoForUUID(dylib.second.uuid);
695             if ( ((MachOAnalyzer*)(info.mh))->canHavePrecomputedDlopenClosure(info.runtimePath.c_str(), ^(const char*) {}) )
696                 insert(retval, dylib.second);
697         }
698     }
699
700     const auto& bundles = _configurations[configuration].architectures[architecture].results.bundles;
701     for (const auto& bundle : bundles) {
702         const UUIDInfo& info = infoForUUID(bundle.second.uuid);
703         if ( ((MachOAnalyzer*)(info.mh))->canHavePrecomputedDlopenClosure(info.runtimePath.c_str(), ^(const char*) {}) )
704             insert(retval, bundle.second);
705     }
706
707     return retval;
708 }
709
710 std::vector<DyldSharedCache::MappedMachO> Manifest::mainExecutables(const std::string& configuration, const std::string& architecture)
711 {
712     std::vector<DyldSharedCache::MappedMachO> retval;
713     const auto&                               executables = _configurations[configuration].architectures[architecture].results.executables;
714     for (const auto& executable : executables) {
715         insert(retval, executable.second);
716     }
717
718     return retval;
719 }
720
721 #pragma clang diagnostic push
722 #pragma clang diagnostic ignored "-Wrange-loop-analysis"
723 bool Manifest::filterForConfig(const std::string& configName)
724 {
725     for (const auto configuration : _configurations) {
726         if (configName == configuration.first) {
727             std::map<std::string, Configuration> filteredConfigs;
728             filteredConfigs[configName] = configuration.second;
729
730             _configurations = filteredConfigs;
731
732             for (auto& arch : configuration.second.architectures) {
733                 arch.second.results = Manifest::Results();
734             }
735             return true;
736         }
737     }
738     return false;
739 }
740 #pragma clang diagnostic pop
741
742 std::set<std::string> Manifest::resultsForConfiguration(const std::string& configName) {
743     std::set<std::string> results;
744     NSDictionary* configurationResults = _manifestDict[@"results"][[NSString stringWithUTF8String:configName.c_str()]];
745     for (NSString* arch in configurationResults) {
746         NSDictionary* dylibs = configurationResults[arch][@"dylibs"];
747         for (NSString* dylib in dylibs) {
748             NSDictionary* dylibDict = dylibs[dylib];
749             if ([dylibDict[@"included"] boolValue])
750                 results.insert([dylib UTF8String]);
751         }
752     }
753     return results;
754 }
755
756 void Manifest::dedupeDispositions(void) {
757     // Since this is all hacky and inference based for now only do it for iOS until XBS
758     // is reved to give us real info. All the other platforms are way smaller anyway.
759     if (_platform != Platform::iOS)
760         return;
761
762     std::map<std::pair<std::string, std::string>, std::set<std::string>> dispositionSets;
763
764     for (const auto& configuration : _configurations) {
765         dispositionSets[std::make_pair(configuration.second.device, configuration.second.disposition)].insert(configuration.first);
766     }
767
768     for (const auto& dSet : dispositionSets) {
769         for (const auto &c1 : dSet.second) {
770             for (const auto &c2 : dSet.second) {
771                 _configurations[c1].metabomTags.insert(_configurations[c2].metabomTag);
772             }
773         }
774     }
775 }
776
777 void Manifest::calculateClosure()
778 {
779     auto closureSemaphore = dispatch_semaphore_create(32);
780     auto closureGroup = dispatch_group_create();
781     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));
782
783     dedupeDispositions();
784     for (auto& config : _configurations) {
785         for (auto& arch : config.second.architectures) {
786             dispatch_semaphore_wait(closureSemaphore, DISPATCH_TIME_FOREVER);
787             dispatch_group_async(closureGroup, closureQueue, [&] {
788                 calculateClosure(config.first, arch.first);
789                 dispatch_semaphore_signal(closureSemaphore);
790             });
791         }
792     }
793
794     dispatch_group_wait(closureGroup, DISPATCH_TIME_FOREVER);
795 }
796
797 void Manifest::remove(const std::string& config, const std::string& arch)
798 {
799     if (_configurations.count(config))
800         _configurations[config].architectures.erase(arch);
801 }
802
803
804 void Manifest::calculateClosure(const std::string& configuration, const std::string& architecture)
805 {
806     __block auto&   configManifest = _configurations[configuration];
807     __block auto&   archManifest = _configurations[configuration].architectures[architecture];
808     __block std::set<UUID> newUuids;
809     std::set<UUID>         processedUuids;
810     std::set<UUID>         cachedUUIDs;
811
812     // Seed anchors
813     for (auto& uuidInfo : _uuidMap) {
814         auto info = uuidInfo.second;
815         if (info.arch != architecture) {
816             continue;
817         }
818         
819         auto i = _metabomTagMap.find(info.runtimePath);
820         assert(i != _metabomTagMap.end());
821         auto tags = i->second;
822         if (!is_disjoint(tags, configManifest.metabomTags)) {
823             newUuids.insert(info.uuid);
824         }
825     }
826
827     // Pull in all dependencies
828     while (!newUuids.empty()) {
829         std::set<UUID> uuidsToProcess = newUuids;
830         newUuids.clear();
831
832         for (const auto& uuid : uuidsToProcess) {
833             if (processedUuids.count(uuid) > 0) {
834                 continue;
835             }
836             processedUuids.insert(uuid);
837
838             const MachOAnalyzer* mh = machOForUUID(uuid);
839             auto runtimePath = runtimePathForUUID(uuid);
840             assert(mh != nullptr);
841
842             mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
843                 auto i = _installNameMap.find(std::make_pair(loadPath, architecture));
844                 if (i != _installNameMap.end())
845                 newUuids.insert(i->second);
846             });
847
848             if (mh->isDylib()) {
849                 // Add the dylib to the results
850                 if (archManifest.results.dylibs.count(uuid) == 0 ) {
851                     archManifest.results.dylibs[uuid].uuid = uuid;
852                     archManifest.results.dylibs[uuid].installname = mh->installName();
853                 }
854
855                 // HACK to insert device specific dylib closures into all caches
856                 if ( (strcmp(mh->installName(), "/System/Library/Caches/com.apple.xpc/sdk.dylib") == 0)
857                     || (strcmp(mh->installName(), "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") == 0) ) {
858                     archManifest.results.exclude(mh, "Device specific dylib");
859                     continue;
860                 }
861
862                 __block std::set<std::string> reasons;
863                 if (mh->canBePlacedInDyldCache(runtimePath.c_str(), ^(const char* reason) { reasons.insert(reason); })) {
864                     auto i = _metabomTagMap.find(runtimePath);
865                     assert(i != _metabomTagMap.end());
866                     auto restrictions = _metabomRestrictedTagMap.find(configuration);
867                     if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
868                         archManifest.results.exclude(mh, "Dylib '" + runtimePath + "' removed due to explict restriction");
869                     }
870
871                     // It can be placed in the cache, grab its dependents and queue them for inclusion
872                     cachedUUIDs.insert(uuid);
873                 } else {
874                     // It can't be placed in the cache, print out the reasons why
875                     std::string reasonString = "Rejected from cached dylibs: " + runtimePath + " " + architecture + " (\"";
876                     for (auto i = reasons.begin(); i != reasons.end(); ++i) {
877                         reasonString += *i;
878                         if (i != --reasons.end()) {
879                             reasonString += "\", \"";
880                         }
881                     }
882                     reasonString += "\")";
883                     archManifest.results.exclude(mh, reasonString);
884                 }
885             } else if (mh->isBundle()) {
886                 if (archManifest.results.bundles.count(uuid) == 0) {
887                     archManifest.results.bundles[uuid].uuid = uuid;
888                 }
889             } else if (mh->isMainExecutable()) {
890                 //HACK exclude all launchd and installd variants until we can do something about xpcd_cache.dylib and friends
891                 if (runtimePath == "/sbin/launchd"
892                     || runtimePath == "/usr/local/sbin/launchd.debug"
893                     || runtimePath == "/usr/local/sbin/launchd.development"
894                     || runtimePath == "/usr/libexec/installd") {
895                     continue;
896                 }
897                 if (archManifest.results.executables.count(uuid) == 0) {
898                     archManifest.results.executables[uuid].uuid = uuid;
899                 }
900             }
901         }
902     }
903
904     __block std::set<UUID>         removedUUIDs;
905     __block bool                   doAgain = true;
906
907     //Trim out dylibs that are missing dependencies
908     while ( doAgain ) {
909         doAgain = false;
910         for (const auto& uuid : cachedUUIDs) {
911             __block std::set<std::string> badDependencies;
912             const dyld3::MachOAnalyzer* mh = machOForUUID(uuid);
913             mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
914                 if (isWeak)
915                     return;
916
917                 auto i = _installNameMap.find(std::make_pair(loadPath, architecture));
918                 if (i == _installNameMap.end() || removedUUIDs.count(i->second)) {
919                     removedUUIDs.insert(uuid);
920                     badDependencies.insert(loadPath);
921                     doAgain = true;
922                 }
923
924                 if (badDependencies.size()) {
925                     std::string reasonString = "Rejected from cached dylibs: " + std::string(mh->installName()) + " " + architecture + " (\"";
926                     for (auto i = badDependencies.begin(); i != badDependencies.end(); ++i) {
927                         reasonString += *i;
928                         if (i != --badDependencies.end()) {
929                             reasonString += "\", \"";
930                         }
931                     }
932                     reasonString += "\")";
933                     archManifest.results.exclude(mh, reasonString);
934                 }
935             });
936         }
937
938         for (const auto& removedUUID : removedUUIDs) {
939             cachedUUIDs.erase(removedUUID);
940         }
941     }
942
943     //Trim out excluded leaf dylibs
944     __block std::set<std::string> linkedDylibs;
945
946     for(const auto& uuid : cachedUUIDs) {
947         const dyld3::MachOAnalyzer* mh = machOForUUID(uuid);
948         mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
949             linkedDylibs.insert(loadPath);
950         });
951     }
952
953     for(const auto& uuid : cachedUUIDs) {
954         auto info = infoForUUID(uuid);
955         auto i = _metabomTagMap.find(info.runtimePath);
956         assert(i != _metabomTagMap.end());
957         auto exclusions = _metabomExcludeTagMap.find(configuration);
958         if (exclusions == _metabomExcludeTagMap.end() || is_disjoint(exclusions->second, i->second))
959             continue;
960
961         if (linkedDylibs.count(info.installName) != 0)
962             continue;
963
964         archManifest.results.exclude(*this, info.uuid, "Dylib '" + info.runtimePath + "' excluded leaf node");
965     }
966 }
967
968 void Manifest::writeJSON(const std::string& path) {
969     NSMutableDictionary* jsonDict = [[NSMutableDictionary alloc] init];
970     for (auto& configuration : _configurations) {
971         jsonDict[cppToObjStr(configuration.first)] = [[NSMutableDictionary alloc] init];
972
973         for (auto& arch : configuration.second.architectures) {
974             NSMutableOrderedSet* includedDylibsSet = [[NSMutableOrderedSet alloc] init];
975             NSMutableOrderedSet* executablesSet = [[NSMutableOrderedSet alloc] init];
976             NSMutableOrderedSet* otherSet = [[NSMutableOrderedSet alloc] init];
977             for (auto& dylib : arch.second.results.dylibs) {
978                 NSString *runtimePath = cppToObjStr(runtimePathForUUID(dylib.second.uuid));
979                 if (dylib.second.included) {
980                     [includedDylibsSet addObject:runtimePath];
981                 } else {
982                     [otherSet addObject:runtimePath];
983                 }
984             }
985
986             for (auto& executable : arch.second.results.executables) {
987                 NSString *runtimePath = cppToObjStr(runtimePathForUUID(executable.second.uuid));
988                 [executablesSet addObject:runtimePath];
989             }
990
991             for (auto& bundle : arch.second.results.bundles) {
992                 NSString *runtimePath = cppToObjStr(runtimePathForUUID(bundle.second.uuid));
993                 [otherSet addObject:runtimePath];
994             }
995
996             [includedDylibsSet sortUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
997                 return [obj1 compare:obj2];
998             }];
999
1000             [executablesSet sortUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
1001                 return [obj1 compare:obj2];
1002             }];
1003
1004             [otherSet sortUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
1005                 return [obj1 compare:obj2];
1006             }];
1007
1008             jsonDict[cppToObjStr(configuration.first)][cppToObjStr(arch.first)] = @{ @"cachedDylibs" : [includedDylibsSet array], @"mainExecutables" : [executablesSet array], @"other" : [otherSet array]};;
1009         }
1010     }
1011
1012     NSError* error = nil;
1013     NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDict options:0x0 error:&error];
1014     (void)[jsonData writeToFile:cppToObjStr(path) atomically:YES];
1015 }
1016
1017 void Manifest::write(const std::string& path)
1018 {
1019     if (path.empty())
1020         return;
1021
1022     NSMutableDictionary* cacheDict = [[NSMutableDictionary alloc] init];
1023     NSMutableDictionary* projectDict = [[NSMutableDictionary alloc] init];
1024     NSMutableDictionary* configurationsDict = [[NSMutableDictionary alloc] init];
1025     NSMutableDictionary* resultsDict = [[NSMutableDictionary alloc] init];
1026
1027     cacheDict[@"manifest-version"] = @(version());
1028     cacheDict[@"build"] = cppToObjStr(build());
1029     cacheDict[@"dylibOrderFile"] = cppToObjStr(dylibOrderFile());
1030     cacheDict[@"dirtyDataOrderFile"] = cppToObjStr(dirtyDataOrderFile());
1031     cacheDict[@"metabomFile"] = cppToObjStr(metabomFile());
1032
1033     cacheDict[@"projects"] = projectDict;
1034     cacheDict[@"results"] = resultsDict;
1035     cacheDict[@"configurations"] = configurationsDict;
1036
1037     for (const auto& project : projects()) {
1038         NSMutableArray* sources = [[NSMutableArray alloc] init];
1039
1040         for (const auto& source : project.second.sources) {
1041             [sources addObject:cppToObjStr(source)];
1042         }
1043
1044         projectDict[cppToObjStr(project.first)] = sources;
1045     }
1046
1047     for (auto& configuration : _configurations) {
1048         NSMutableArray* archArray = [[NSMutableArray alloc] init];
1049         for (auto& arch : configuration.second.architectures) {
1050             [archArray addObject:cppToObjStr(arch.first)];
1051         }
1052
1053         NSMutableArray* excludeTags = [[NSMutableArray alloc] init];
1054         for (const auto& excludeTag : configuration.second.metabomExcludeTags) {
1055             [excludeTags addObject:cppToObjStr(excludeTag)];
1056         }
1057
1058         configurationsDict[cppToObjStr(configuration.first)] = @{
1059             @"platformName" : cppToObjStr(configuration.second.platformName),
1060             @"metabomTag" : cppToObjStr(configuration.second.metabomTag),
1061             @"metabomExcludeTags" : excludeTags,
1062             @"architectures" : archArray
1063         };
1064     }
1065
1066     for (auto& configuration : _configurations) {
1067         NSMutableDictionary* archResultsDict = [[NSMutableDictionary alloc] init];
1068         for (auto& arch : configuration.second.architectures) {
1069             NSMutableDictionary* dylibsDict = [[NSMutableDictionary alloc] init];
1070             NSMutableArray* warningsArray = [[NSMutableArray alloc] init];
1071             NSMutableDictionary* devRegionsDict = [[NSMutableDictionary alloc] init];
1072             NSMutableDictionary* prodRegionsDict = [[NSMutableDictionary alloc] init];
1073             NSString* prodCDHash = cppToObjStr(arch.second.results.productionCache.cdHash);
1074             NSString* devCDHash = cppToObjStr(arch.second.results.developmentCache.cdHash);
1075
1076             for (auto& dylib : arch.second.results.dylibs) {
1077                 NSMutableDictionary* dylibDict = [[NSMutableDictionary alloc] init];
1078                 if (dylib.second.included) {
1079                     dylibDict[@"included"] = @YES;
1080                 } else {
1081                     dylibDict[@"included"] = @NO;
1082                     dylibDict[@"exclusionInfo"] = cppToObjStr(dylib.second.exclusionInfo);
1083                 }
1084                 dylibsDict[cppToObjStr(dylib.second.installname)] = dylibDict;
1085             }
1086
1087             for (auto& warning : arch.second.results.warnings) {
1088                 [warningsArray addObject:cppToObjStr(warning)];
1089             }
1090
1091             BOOL built = arch.second.results.failure.empty();
1092             archResultsDict[cppToObjStr(arch.first)] = @{
1093                 @"dylibs" : dylibsDict,
1094                 @"built" : @(built),
1095                 @"failure" : cppToObjStr(arch.second.results.failure),
1096                 @"productionCache" : @{ @"cdhash" : prodCDHash, @"regions" : prodRegionsDict },
1097                 @"developmentCache" : @{ @"cdhash" : devCDHash, @"regions" : devRegionsDict },
1098                 @"warnings" : warningsArray
1099             };
1100         }
1101         resultsDict[cppToObjStr(configuration.first)] = archResultsDict;
1102     }
1103
1104     switch (platform()) {
1105     case Platform::iOS:
1106         cacheDict[@"platform"] = @"ios";
1107         break;
1108     case Platform::tvOS:
1109         cacheDict[@"platform"] = @"tvos";
1110         break;
1111     case Platform::watchOS:
1112         cacheDict[@"platform"] = @"watchos";
1113         break;
1114     case Platform::bridgeOS:
1115         cacheDict[@"platform"] = @"bridgeos";
1116         break;
1117     case Platform::macOS:
1118         cacheDict[@"platform"] = @"macos";
1119         break;
1120     case Platform::unknown:
1121     case Platform::iOSMac:
1122     case Platform::iOS_simulator:
1123     case Platform::tvOS_simulator:
1124     case Platform::watchOS_simulator:
1125         cacheDict[@"platform"] = @"unknown";
1126         break;
1127     }
1128
1129     NSError* error = nil;
1130     NSData*  outData = [NSPropertyListSerialization dataWithPropertyList:cacheDict
1131                                                                  format:NSPropertyListBinaryFormat_v1_0
1132                                                                 options:0
1133                                                                   error:&error];
1134     (void)[outData writeToFile:cppToObjStr(path) atomically:YES];
1135 }
1136
1137
1138 void Manifest::forEachMachO(std::string configuration,
1139                             std::function<void(const std::string &buildPath, const std::string &runtimePath, const std::string &arch, bool shouldBeExcludedIfLeaf)> lambda) {
1140     for (auto& uuidInfo : _uuidMap) {
1141         auto i = _metabomTagMap.find(uuidInfo.second.runtimePath);
1142         assert(i != _metabomTagMap.end());
1143         auto restrictions = _metabomRestrictedTagMap.find(configuration);
1144         if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
1145             continue;
1146         }
1147         auto& configManifest = _configurations[configuration];
1148         auto exclusions = _metabomExcludeTagMap.find(configuration);
1149         bool isExcluded = (exclusions != _metabomExcludeTagMap.end()) && !is_disjoint(exclusions->second, i->second);
1150         bool isAnchor = !is_disjoint(i->second, configManifest.metabomTags);
1151         bool shouldBeExcludedIfLeaf = isExcluded || !isAnchor;
1152         lambda(uuidInfo.second.buildPath, uuidInfo.second.runtimePath, uuidInfo.second.arch, shouldBeExcludedIfLeaf);
1153     }
1154 }
1155
1156
1157 void Manifest::forEachSymlink(std::string configuration,
1158                             std::function<void(const std::string &fromPath, const std::string &toPath)> lambda) {
1159     for (const auto& symlink : _symlinks) {
1160         auto i = _metabomSymlinkTagMap.find(symlink.first);
1161         assert(i != _metabomSymlinkTagMap.end());
1162         auto restrictions = _metabomRestrictedTagMap.find(configuration);
1163         if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
1164             continue;
1165         }
1166         lambda(symlink.first, symlink.second);
1167     }
1168 }
1169
1170 } //namespace dyld3