]> git.saurik.com Git - apple/dyld.git/blob - dyld3/shared-cache/Manifest.mm
6d09811ed701dfee310113ccc74cd2cee61485d2
[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