1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
3 * Copyright (c) 2017 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
25 #include "mrm_shared_cache_builder.h"
26 #include "CacheBuilder.h"
27 #include "ClosureFileSystem.h"
28 #include "FileUtils.h"
35 static const uint64_t kMinBuildVersion
= 1; //The minimum version BuildOptions struct we can support
36 static const uint64_t kMaxBuildVersion
= 1; //The maximum version BuildOptions struct we can support
38 static const uint32_t MajorVersion
= 1;
39 static const uint32_t MinorVersion
= 0;
47 const uint64_t length
;
53 class FileSystemMRM
: public FileSystem
{
55 FileSystemMRM() : FileSystem() { }
57 bool getRealPath(const char possiblePath
[MAXPATHLEN
], char realPath
[MAXPATHLEN
]) const override
{
59 std::string resolvedPath
= symlinkResolver
.realPath(diag
, possiblePath
);
60 if (diag
.hasError()) {
61 diag
.verbose("MRM error: %s\n", diag
.errorMessage().c_str());
66 // FIXME: Should we only return real paths of files which point to macho's? For now that is what we are doing
67 auto it
= fileMap
.find(resolvedPath
);
68 if (it
== fileMap
.end())
71 memcpy(realPath
, resolvedPath
.c_str(), std::min((size_t)MAXPATHLEN
, resolvedPath
.size() + 1));
75 bool loadFile(const char* path
, LoadedFileInfo
& info
, char realerPath
[MAXPATHLEN
], void (^error
)(const char* format
, ...)) const override
{
77 std::string resolvedPath
= symlinkResolver
.realPath(diag
, path
);
78 if (diag
.hasError()) {
79 diag
.verbose("MRM error: %s\n", diag
.errorMessage().c_str());
84 auto it
= fileMap
.find(resolvedPath
);
85 if (it
== fileMap
.end())
88 if (resolvedPath
== path
)
91 memcpy(realerPath
, resolvedPath
.c_str(), std::min((size_t)MAXPATHLEN
, resolvedPath
.size() + 1));
93 // The file exists at this exact path. Lets use it!
94 const FileInfo
& fileInfo
= files
[it
->second
];
96 info
.fileContent
= fileInfo
.data
;
97 info
.fileContentLen
= fileInfo
.length
;
99 info
.sliceLen
= fileInfo
.length
;
100 info
.isSipProtected
= false;
101 info
.inode
= fileInfo
.inode
;
102 info
.mtime
= fileInfo
.mtime
;
103 info
.unload
= nullptr;
108 void unloadFile(const LoadedFileInfo
& info
) const override
{
113 void unloadPartialFile(LoadedFileInfo
& info
, uint64_t keepStartOffset
, uint64_t keepLength
) const override
{
114 // Note we don't actually unload the data here, but we do want to update the offsets for other data structures to track where we are
115 info
.fileContent
= (const void*)((char*)info
.fileContent
+ keepStartOffset
);
116 info
.fileContentLen
= keepLength
;
119 bool fileExists(const char* path
, uint64_t* inode
=nullptr, uint64_t* mtime
=nullptr,
120 bool* issetuid
=nullptr, bool* inodesMatchRuntime
= nullptr) const override
{
122 std::string resolvedPath
= symlinkResolver
.realPath(diag
, path
);
123 if (diag
.hasError()) {
124 diag
.verbose("MRM error: %s\n", diag
.errorMessage().c_str());
129 auto it
= fileMap
.find(resolvedPath
);
130 if (it
== fileMap
.end())
133 // The file exists at this exact path. Lets use it!
134 const FileInfo
& fileInfo
= files
[it
->second
];
136 *inode
= fileInfo
.inode
;
138 *mtime
= fileInfo
.mtime
;
141 if (inodesMatchRuntime
)
142 *inodesMatchRuntime
= false;
147 bool addFile(const char* path
, uint8_t* data
, uint64_t size
, Diagnostics
& diag
, FileFlags fileFlags
) {
148 auto iteratorAndInserted
= fileMap
.insert(std::make_pair(path
, files
.size()));
149 if (!iteratorAndInserted
.second
) {
150 diag
.error("Already have content for path: '%s'", path
);
154 symlinkResolver
.addFile(diag
, path
);
158 // on iOS, inode is just a placeholder
159 // Note its safe to just use the index here as we only compare it during closure building
160 // and never record it in the closures
161 uint64_t inode
= files
.size() + 1;
164 files
.push_back((FileInfo
){ path
, data
, size
, fileFlags
, mtime
, inode
});
168 bool addSymlink(const char* fromPath
, const char* toPath
, Diagnostics
& diag
) {
169 symlinkResolver
.addSymlink(diag
, fromPath
, toPath
);
170 return !diag
.hasError();
173 void forEachFileInfo(std::function
<void(const char* path
, FileFlags fileFlags
)> lambda
) {
174 for (const FileInfo
& fileInfo
: files
)
175 lambda(fileInfo
.path
, fileInfo
.flags
);
178 size_t fileCount() const {
182 std::vector
<DyldSharedCache::FileAlias
> getResolvedSymlinks(Diagnostics
& diag
) {
183 return symlinkResolver
.getResolvedSymlinks(diag
);
187 std::vector
<FileInfo
> files
;
188 std::map
<std::string
, uint64_t> fileMap
;
189 SymlinkResolver symlinkResolver
;
192 } // namespace closure
195 struct BuildInstance
{
196 std::unique_ptr
<DyldSharedCache::CreateOptions
> options
;
197 std::unique_ptr
<CacheBuilder
> builder
;
198 std::vector
<CacheBuilder::InputFile
> inputFiles
;
199 std::vector
<const char*> errors
;
200 std::vector
<const char*> warnings
;
201 std::vector
<std::string
> errorStrings
; // Owns the data for the errors
202 std::vector
<std::string
> warningStrings
; // Owns the data for the warnings
203 uint8_t* cacheData
= nullptr;
204 uint64_t cacheSize
= 0;
206 std::string cdHash
; // Owns the data for the cdHash
207 std::string cdHashType
; // Owns the data for the cdHashType
208 std::string uuid
; // Owns the data for the uuid
211 struct BuildFileResult
{
217 struct SharedCacheBuilder
{
218 SharedCacheBuilder(const BuildOptions_v1
* options
);
219 const BuildOptions_v1
* options
;
220 dyld3::closure::FileSystemMRM fileSystem
;
222 std::string dylibOrderFileData
;
223 std::string dirtyDataOrderFileData
;
225 // An array of builders and their options as we may have more than one builder for a given device variant.
226 std::vector
<BuildInstance
> builders
;
228 // The paths in all of the caches
229 // We keep this here to own the std::string path data
230 std::map
<std::string
, uint32_t> dylibsInCaches
;
232 // The results from all of the builders
233 // We keep this in a vector to own the data.
234 std::vector
<FileResult
*> fileResults
;
235 std::vector
<FileResult
> fileResultStorage
;
237 // The results from all of the builders
238 // We keep this in a vector to own the data.
239 std::vector
<CacheResult
*> cacheResults
;
240 std::vector
<CacheResult
> cacheResultStorage
;
242 // The files to remove. These are in every copy of the caches we built
243 std::vector
<const char*> filesToRemove
;
245 std::vector
<const char*> errors
;
246 std::vector
<std::string
> errorStorage
;
247 pthread_mutex_t lock
;
255 State state
= AcceptingFiles
;
257 void runSync(void (^block
)()) {
258 pthread_mutex_lock(&lock
);
260 pthread_mutex_unlock(&lock
);
263 __attribute__((format(printf
, 2, 3)))
264 void error(const char* format
, ...) {
266 va_start(list
, format
);
268 diag
.error(format
, list
);
271 errorStorage
.push_back(diag
.errorMessage());
272 errors
.push_back(errorStorage
.back().data());
276 SharedCacheBuilder::SharedCacheBuilder(const BuildOptions_v1
* options
) : options(options
), lock(PTHREAD_MUTEX_INITIALIZER
) {
280 void validiateBuildOptions(const BuildOptions_v1
* options
, SharedCacheBuilder
& builder
) {
281 if (options
->version
< kMinBuildVersion
) {
282 builder
.error("Builder version %llu is less than minimum supported version of %llu", options
->version
, kMinBuildVersion
);
284 if (options
->version
> kMaxBuildVersion
) {
285 builder
.error("Builder version %llu is greater than maximum supported version of %llu", options
->version
, kMaxBuildVersion
);
287 if (!options
->updateName
) {
288 builder
.error("updateName must not be null");
290 if (!options
->deviceName
) {
291 builder
.error("deviceName must not be null");
293 switch (options
->disposition
) {
294 case Disposition::Unknown
:
295 case Disposition::InternalDevelopment
:
296 case Disposition::Customer
:
299 builder
.error("unknown disposition value");
302 switch (options
->platform
) {
303 case Platform::unknown
:
304 builder
.error("platform must not be unknown");
306 case Platform::macOS
:
309 case Platform::watchOS
:
310 case Platform::bridgeOS
:
311 case Platform::iOSMac
:
312 case Platform::iOS_simulator
:
313 case Platform::tvOS_simulator
:
314 case Platform::watchOS_simulator
:
317 builder
.error("unknown platform value");
320 if (!options
->archs
) {
321 builder
.error("archs must not be null");
323 if (!options
->numArchs
) {
324 builder
.error("numArchs must not be 0");
328 void getVersion(uint32_t *major
, uint32_t *minor
) {
329 *major
= MajorVersion
;
330 *minor
= MinorVersion
;
333 struct SharedCacheBuilder
* createSharedCacheBuilder(const BuildOptions_v1
* options
) {
334 SharedCacheBuilder
* builder
= new SharedCacheBuilder(options
);
336 // Check the option struct values are valid
337 validiateBuildOptions(options
, *builder
);
342 bool addFile(struct SharedCacheBuilder
* builder
, const char* path
, uint8_t* data
, uint64_t size
, FileFlags fileFlags
) {
343 __block
bool success
= false;
344 builder
->runSync(^() {
345 if (builder
->state
!= SharedCacheBuilder::AcceptingFiles
) {
346 builder
->error("Cannot add file: '%s' as we have already started building", path
);
349 size_t pathLength
= strlen(path
);
350 if (pathLength
== 0) {
351 builder
->error("Empty path");
354 if (pathLength
>= MAXPATHLEN
) {
355 builder
->error("Path is too long: '%s'", path
);
358 if (data
== nullptr) {
359 builder
->error("Data cannot be null for file: '%s'", path
);
365 case ShouldBeExcludedFromCacheIfUnusedLeaf
:
366 case RequiredClosure
:
369 builder
->dylibOrderFileData
= std::string((char*)data
, size
);
372 case DirtyDataOrderFile
:
373 builder
->dirtyDataOrderFileData
= std::string((char*)data
, size
);
377 builder
->error("unknown file flags value");
381 if (!builder
->fileSystem
.addFile(path
, data
, size
, diag
, fileFlags
)) {
382 builder
->errorStorage
.push_back(diag
.errorMessage());
383 builder
->errors
.push_back(builder
->errorStorage
.back().data());
391 bool addSymlink(struct SharedCacheBuilder
* builder
, const char* fromPath
, const char* toPath
) {
392 __block
bool success
= false;
393 builder
->runSync(^() {
394 if (builder
->state
!= SharedCacheBuilder::AcceptingFiles
) {
395 builder
->error("Cannot add file: '%s' as we have already started building", fromPath
);
398 size_t pathLength
= strlen(fromPath
);
399 if (pathLength
== 0) {
400 builder
->error("Empty path");
403 if (pathLength
>= MAXPATHLEN
) {
404 builder
->error("Path is too long: '%s'", fromPath
);
408 if (!builder
->fileSystem
.addSymlink(fromPath
, toPath
, diag
)) {
409 builder
->errorStorage
.push_back(diag
.errorMessage());
410 builder
->errors
.push_back(builder
->errorStorage
.back().data());
418 static bool platformExcludeLocalSymbols(Platform platform
) {
420 case Platform::unknown
:
421 case Platform::macOS
:
425 case Platform::watchOS
:
426 case Platform::bridgeOS
:
428 case Platform::iOSMac
:
429 case Platform::iOS_simulator
:
430 case Platform::tvOS_simulator
:
431 case Platform::watchOS_simulator
:
436 static DyldSharedCache::CodeSigningDigestMode
platformCodeSigningDigestMode(Platform platform
) {
438 case Platform::unknown
:
439 case Platform::macOS
:
442 return DyldSharedCache::SHA256only
;
443 case Platform::watchOS
:
444 return DyldSharedCache::Agile
;
445 case Platform::bridgeOS
:
446 case Platform::iOSMac
:
447 case Platform::iOS_simulator
:
448 case Platform::tvOS_simulator
:
449 case Platform::watchOS_simulator
:
450 return DyldSharedCache::SHA256only
;
454 static bool platformIsForSimulator(Platform platform
) {
456 case Platform::unknown
:
457 case Platform::macOS
:
460 case Platform::watchOS
:
461 case Platform::bridgeOS
:
462 case Platform::iOSMac
:
464 case Platform::iOS_simulator
:
465 case Platform::tvOS_simulator
:
466 case Platform::watchOS_simulator
:
471 static const char* dispositionName(Disposition disposition
) {
472 switch (disposition
) {
473 case Disposition::Unknown
:
475 case Disposition::InternalDevelopment
:
477 case Disposition::Customer
:
479 case Disposition::InternalMinDevelopment
:
480 return "InternalMinDevelopment";
484 bool runSharedCacheBuilder(struct SharedCacheBuilder
* builder
) {
485 __block
bool success
= false;
486 builder
->runSync(^() {
487 if (builder
->state
!= SharedCacheBuilder::AcceptingFiles
) {
488 builder
->error("Builder has already been run");
491 builder
->state
= SharedCacheBuilder::Building
;
492 if (builder
->fileSystem
.fileCount() == 0) {
493 builder
->error("Cannot run builder with no files");
497 std::vector
<DyldSharedCache::FileAlias
> aliases
= builder
->fileSystem
.getResolvedSymlinks(diag
);
498 if (diag
.hasError()) {
499 diag
.verbose("Symlink resolver error: %s\n", diag
.errorMessage().c_str());
502 if (!builder
->errors
.empty()) {
503 builder
->error("Skipping running shared cache builder due to previous errors");
507 __block
std::vector
<CacheBuilder::InputFile
> inputFiles
;
508 builder
->fileSystem
.forEachFileInfo(^(const char* path
, FileFlags fileFlags
) {
509 CacheBuilder::InputFile::State state
= CacheBuilder::InputFile::Unset
;
511 case FileFlags::NoFlags
:
512 state
= CacheBuilder::InputFile::Unset
;
514 case FileFlags::MustBeInCache
:
515 state
= CacheBuilder::InputFile::MustBeIncluded
;
517 case FileFlags::ShouldBeExcludedFromCacheIfUnusedLeaf
:
518 state
= CacheBuilder::InputFile::MustBeExcludedIfUnused
;
520 case FileFlags::RequiredClosure
:
521 state
= CacheBuilder::InputFile::MustBeIncluded
;
523 case FileFlags::DylibOrderFile
:
524 case FileFlags::DirtyDataOrderFile
:
525 builder
->error("Order files should not be in the file system");
528 inputFiles
.emplace_back((CacheBuilder::InputFile
){ path
, state
});
531 auto addCacheConfiguration
= ^(bool isOptimized
) {
532 for (uint64_t i
= 0; i
!= builder
->options
->numArchs
; ++i
) {
533 auto options
= std::make_unique
<DyldSharedCache::CreateOptions
>((DyldSharedCache::CreateOptions
){});
534 const char *cacheSuffix
= (isOptimized
? "" : ".development");
535 std::string runtimePath
= (builder
->options
->platform
== Platform::macOS
) ? "/private/var/db/dyld/" : "/System/Library/Caches/com.apple.dyld/";
536 options
->outputFilePath
= runtimePath
+ "dyld_shared_cache_" + builder
->options
->archs
[i
] + cacheSuffix
;
537 options
->outputMapFilePath
= options
->outputFilePath
+ ".json";
538 options
->archs
= &dyld3::GradedArchs::forName(builder
->options
->archs
[i
]);
539 options
->platform
= (dyld3::Platform
)builder
->options
->platform
;
540 options
->excludeLocalSymbols
= platformExcludeLocalSymbols(builder
->options
->platform
);
541 options
->optimizeStubs
= isOptimized
;
542 options
->optimizeObjC
= true;
543 options
->codeSigningDigestMode
= platformCodeSigningDigestMode(builder
->options
->platform
);
544 options
->dylibsRemovedDuringMastering
= true;
545 options
->inodesAreSameAsRuntime
= false;
546 options
->cacheSupportsASLR
= true;
547 options
->forSimulator
= platformIsForSimulator(builder
->options
->platform
);
548 options
->isLocallyBuiltCache
= builder
->options
->isLocallyBuiltCache
;
549 options
->verbose
= builder
->options
->verboseDiagnostics
;
550 options
->evictLeafDylibsOnOverflow
= true;
551 options
->loggingPrefix
= std::string(builder
->options
->deviceName
) + dispositionName(builder
->options
->disposition
) + "." + builder
->options
->archs
[i
] + cacheSuffix
;
552 options
->dylibOrdering
= parseOrderFile(builder
->dylibOrderFileData
);
553 options
->dirtyDataSegmentOrdering
= parseOrderFile(builder
->dirtyDataOrderFileData
);
555 auto cacheBuilder
= std::make_unique
<CacheBuilder
>(*options
.get(), builder
->fileSystem
);
556 builder
->builders
.emplace_back((BuildInstance
) { std::move(options
), std::move(cacheBuilder
), inputFiles
});
560 // Enqueue a cache for each configuration
561 switch (builder
->options
->disposition
) {
562 case Disposition::Unknown
:
563 case Disposition::InternalDevelopment
:
564 addCacheConfiguration(false);
565 addCacheConfiguration(true);
567 case Disposition::Customer
:
568 addCacheConfiguration(true);
570 case Disposition::InternalMinDevelopment
:
571 addCacheConfiguration(false);
575 // FIXME: This step can run in parallel.
576 for (auto& buildInstance
: builder
->builders
) {
577 CacheBuilder
* cacheBuilder
= buildInstance
.builder
.get();
578 cacheBuilder
->build(buildInstance
.inputFiles
, aliases
);
580 // First put the warnings in to a vector to own them.
581 buildInstance
.warningStrings
.reserve(cacheBuilder
->warnings().size());
582 for (const std::string
& warning
: cacheBuilder
->warnings())
583 buildInstance
.warningStrings
.push_back(warning
);
585 // Then copy to a vector to reference the owner
586 buildInstance
.warnings
.reserve(buildInstance
.warningStrings
.size());
587 for (const std::string
& warning
: buildInstance
.warningStrings
)
588 buildInstance
.warnings
.push_back(warning
.c_str());
590 if (!cacheBuilder
->errorMessage().empty()) {
591 // First put the errors in to a vector to own them.
592 buildInstance
.errorStrings
.push_back(cacheBuilder
->errorMessage());
594 // Then copy to a vector to reference the owner
595 buildInstance
.errors
.reserve(buildInstance
.errorStrings
.size());
596 for (const std::string
& error
: buildInstance
.errorStrings
)
597 buildInstance
.errors
.push_back(error
.c_str());
600 if (cacheBuilder
->errorMessage().empty()) {
601 cacheBuilder
->writeBuffer(buildInstance
.cacheData
, buildInstance
.cacheSize
);
602 buildInstance
.jsonMap
= cacheBuilder
->getMapFileBuffer(builder
->options
->deviceName
);
603 buildInstance
.cdHash
= cacheBuilder
->cdHashFirst();
604 buildInstance
.uuid
= cacheBuilder
->uuid();
605 switch (buildInstance
.options
->codeSigningDigestMode
) {
606 case DyldSharedCache::SHA256only
:
607 buildInstance
.cdHashType
= "sha256";
609 case DyldSharedCache::SHA1only
:
610 buildInstance
.cdHashType
= "sha1";
612 case DyldSharedCache::Agile
:
613 buildInstance
.cdHashType
= "sha1";
619 // Now that we have run all of the builds, collect the results
620 // First push file results for each of the shared caches we built
621 for (auto& buildInstance
: builder
->builders
) {
622 CacheBuilder
* cacheBuilder
= buildInstance
.builder
.get();
624 CacheResult cacheBuildResult
;
625 cacheBuildResult
.version
= 1;
626 cacheBuildResult
.loggingPrefix
= buildInstance
.options
->loggingPrefix
.c_str();
627 cacheBuildResult
.deviceConfiguration
= buildInstance
.options
->loggingPrefix
.c_str();
628 cacheBuildResult
.warnings
= buildInstance
.warnings
.empty() ? nullptr : buildInstance
.warnings
.data();
629 cacheBuildResult
.numWarnings
= buildInstance
.warnings
.size();
630 cacheBuildResult
.errors
= buildInstance
.errors
.empty() ? nullptr : buildInstance
.errors
.data();
631 cacheBuildResult
.numErrors
= buildInstance
.errors
.size();
632 cacheBuildResult
.uuidString
= buildInstance
.uuid
.c_str();
633 cacheBuildResult
.mapJSON
= buildInstance
.jsonMap
.c_str();
635 builder
->cacheResultStorage
.emplace_back(cacheBuildResult
);
637 if (!cacheBuilder
->errorMessage().empty())
640 FileResult cacheFileResult
;
641 cacheFileResult
.version
= 1;
642 cacheFileResult
.path
= buildInstance
.options
->outputFilePath
.c_str();
643 cacheFileResult
.behavior
= AddFile
;
644 cacheFileResult
.data
= buildInstance
.cacheData
;
645 cacheFileResult
.size
= buildInstance
.cacheSize
;
646 cacheFileResult
.hashArch
= buildInstance
.options
->archs
->name();
647 cacheFileResult
.hashType
= buildInstance
.cdHashType
.c_str();
648 cacheFileResult
.hash
= buildInstance
.cdHash
.c_str();
650 builder
->fileResultStorage
.emplace_back(cacheFileResult
);
652 cacheBuilder
->forEachCacheDylib(^(const std::string
&path
) {
653 ++builder
->dylibsInCaches
[path
.c_str()];
657 // Copy from the storage to the vector we can return to the API.
658 for (auto &fileResult
: builder
->fileResultStorage
)
659 builder
->fileResults
.push_back(&fileResult
);
660 for (auto &cacheResult
: builder
->cacheResultStorage
)
661 builder
->cacheResults
.push_back(&cacheResult
);
664 // Add entries to tell us to remove all of the dylibs from disk which are in every cache.
665 const size_t numCaches
= builder
->builders
.size();
666 for (const auto& dylibAndCount
: builder
->dylibsInCaches
) {
667 if (dylibAndCount
.second
== numCaches
) {
668 builder
->filesToRemove
.push_back(dylibAndCount
.first
.c_str());
672 // Quit if we had any errors.
673 for (auto& buildInstance
: builder
->builders
) {
674 CacheBuilder
* cacheBuilder
= buildInstance
.builder
.get();
675 if (!cacheBuilder
->errorMessage().empty())
679 builder
->state
= SharedCacheBuilder::FinishedBuilding
;
685 const char* const* getErrors(const struct SharedCacheBuilder
* builder
, uint64_t* errorCount
) {
686 if (builder
->errors
.empty())
688 *errorCount
= builder
->errors
.size();
689 return builder
->errors
.data();
692 const struct FileResult
* const* getFileResults(struct SharedCacheBuilder
* builder
, uint64_t* resultCount
) {
693 if (builder
->fileResults
.empty())
695 *resultCount
= builder
->fileResults
.size();
696 return builder
->fileResults
.data();
699 const struct CacheResult
* const* getCacheResults(struct SharedCacheBuilder
* builder
, uint64_t* resultCount
) {
700 if (builder
->cacheResults
.empty())
702 *resultCount
= builder
->cacheResults
.size();
703 return builder
->cacheResults
.data();
706 const char* const* getFilesToRemove(const struct SharedCacheBuilder
* builder
, uint64_t* fileCount
) {
707 if (builder
->filesToRemove
.empty())
709 *fileCount
= builder
->filesToRemove
.size();
710 return builder
->filesToRemove
.data();
713 void destroySharedCacheBuilder(struct SharedCacheBuilder
* builder
) {
714 for (auto& buildInstance
: builder
->builders
) {
715 CacheBuilder
* cacheBuilder
= buildInstance
.builder
.get();
716 cacheBuilder
->deleteBuffer();
718 for (auto &fileResult
: builder
->fileResultStorage
) {
719 free((void*)fileResult
.data
);
720 fileResult
.data
= nullptr;