Move Weak Framework Loading to FBControlCore
Summary: This will allow Framework loading by XCTBootstrap & Others. Reviewed By: marekcirkos Differential Revision: D3092590 fb-gh-sync-id: c69645f21aa0d602e267fb76fc2d1e0cfe0aada7 shipit-source-id: c69645f21aa0d602e267fb76fc2d1e0cfe0aada7
This commit is contained in:
Родитель
4839d2f346
Коммит
8887a1ed05
|
@ -30,5 +30,6 @@
|
|||
#import <FBControlCore/FBTaskExecutor+Private.h>
|
||||
#import <FBControlCore/FBTaskExecutor.h>
|
||||
#import <FBControlCore/FBTerminationHandle.h>
|
||||
#import <FBControlCore/FBWeakFrameworkLoader.h>
|
||||
#import <FBControlCore/NSPredicate+FBControlCore.h>
|
||||
#import <FBControlCore/NSRunLoop+FBControlCore.h>
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol FBControlCoreLogger;
|
||||
|
||||
/**
|
||||
A Utility Class for loading weak-linked Frameworks at runtime.
|
||||
*/
|
||||
@interface FBWeakFrameworkLoader : NSObject
|
||||
|
||||
/**
|
||||
Loads a Mapping of Private Frameworks.
|
||||
Will avoid re-loading allready loaded Frameworks.
|
||||
|
||||
@param classMapping a mapping of Class Name to Framework Path. The Framework path is resolved relative to the current Developer Directory.
|
||||
@param logger a logger for logging framework loading activities.
|
||||
@param error an error out for any error that occurs.
|
||||
@return YES if successful, NO otherwise.
|
||||
*/
|
||||
+ (BOOL)loadPrivateFrameworks:(NSDictionary<NSString *, NSString *> *)classMapping logger:(id<FBControlCoreLogger>)logger error:(NSError **)error;
|
||||
|
||||
@end
|
|
@ -0,0 +1,115 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "FBWeakFrameworkLoader.h"
|
||||
|
||||
#import "FBCollectionInformation.h"
|
||||
#import "FBControlCoreError.h"
|
||||
#import "FBControlCoreGlobalConfiguration.h"
|
||||
#import "FBControlCoreLogger.h"
|
||||
|
||||
@implementation FBWeakFrameworkLoader
|
||||
|
||||
// A Mapping of Class Names to the Frameworks that they belong to. This serves to:
|
||||
// 1) Represent the Frameworks that FBControlCore is dependent on via their classes
|
||||
// 2) Provide a path to the relevant Framework.
|
||||
// 3) Provide a class for sanity checking the Framework load.
|
||||
// 4) Provide a class that can be checked before the Framework load to avoid re-loading the same
|
||||
// Framework if others have done so before.
|
||||
// 5) Provide a sanity check that any preloaded Private Frameworks match the current xcode-select version
|
||||
+ (BOOL)loadPrivateFrameworks:(NSDictionary<NSString *, NSString *> *)classMapping logger:(id<FBControlCoreLogger>)logger error:(NSError **)error
|
||||
{
|
||||
static BOOL hasLoaded = NO;
|
||||
if (hasLoaded) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
// This will assert if the directory could not be found.
|
||||
NSString *developerDirectory = FBControlCoreGlobalConfiguration.developerDirectory;
|
||||
[logger logFormat:@"Using Developer Directory %@", developerDirectory];
|
||||
|
||||
for (NSString *className in classMapping) {
|
||||
NSString *relativePath = classMapping[className];
|
||||
NSString *path = [[developerDirectory stringByAppendingPathComponent:relativePath] stringByStandardizingPath];
|
||||
|
||||
// The Class exists, therefore has been loaded
|
||||
if (NSClassFromString(className)) {
|
||||
[logger logFormat:@"%@ is already loaded, skipping load of framework %@", className, path];
|
||||
NSError *innerError = nil;
|
||||
if (![self verifyDeveloperDirectoryForPrivateClass:className developerDirectory:developerDirectory logger:logger error:&innerError]) {
|
||||
return [FBControlCoreError failBoolWithError:innerError errorOut:error];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise load the Framework.
|
||||
[logger logFormat:@"%@ is not loaded. Loading %@ at path %@", className, path.lastPathComponent, path];
|
||||
NSError *innerError = nil;
|
||||
if (![self loadFrameworkAtPath:path logger:logger error:&innerError]) {
|
||||
return [FBControlCoreError failBoolWithError:innerError errorOut:error];
|
||||
}
|
||||
[logger logFormat:@"Loaded %@ from %@", className, path];
|
||||
}
|
||||
|
||||
// We're done with loading Frameworks.
|
||||
hasLoaded = YES;
|
||||
[logger logFormat:@"Loaded All Private Frameworks %@", [FBCollectionInformation oneLineDescriptionFromArray:classMapping.allValues atKeyPath:@"lastPathComponent"]];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)loadFrameworkAtPath:(NSString *)path logger:(id<FBControlCoreLogger>)logger error:(NSError **)error
|
||||
{
|
||||
NSBundle *bundle = [NSBundle bundleWithPath:path];
|
||||
if (!bundle) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Failed to load the bundle for path %@", path]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
NSError *innerError = nil;
|
||||
if (![bundle loadAndReturnError:&innerError]) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Failed to load the the Framework Bundle %@", bundle]
|
||||
failBool:error];
|
||||
}
|
||||
[logger logFormat:@"Successfully loaded %@", path.lastPathComponent];
|
||||
return YES;
|
||||
}
|
||||
|
||||
/**
|
||||
Given that it is possible for FBControlCore.framework to be loaded after any of the
|
||||
Private Frameworks upon which it depends, it's possible that these Frameworks may have
|
||||
been loaded from a different Developer Directory.
|
||||
|
||||
In order to prevent crazy behaviour from arising, FBControlCore will check the
|
||||
directories of these Frameworks match the one that is currently set.
|
||||
*/
|
||||
+ (BOOL)verifyDeveloperDirectoryForPrivateClass:(NSString *)className developerDirectory:(NSString *)developerDirectory logger:(id<FBControlCoreLogger>)logger error:(NSError **)error
|
||||
{
|
||||
NSBundle *bundle = [NSBundle bundleForClass:NSClassFromString(className)];
|
||||
if (!bundle) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Could not obtain Framework bundle for class named %@", className]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
// Developer Directory is: /Applications/Xcode.app/Contents/Developer
|
||||
// The common base path is: is: /Applications/Xcode.app
|
||||
NSString *basePath = [[developerDirectory stringByDeletingLastPathComponent] stringByDeletingLastPathComponent];
|
||||
if (![bundle.bundlePath hasPrefix:basePath]) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Expected Framework %@ to be loaded for Developer Directory at path %@, but was loaded from %@", bundle.bundlePath.lastPathComponent, bundle.bundlePath, developerDirectory]
|
||||
failBool:error];
|
||||
}
|
||||
[logger logFormat:@"%@ has correct path of %@", className, basePath];
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
|
@ -9,6 +9,8 @@
|
|||
/* Begin PBXBuildFile section */
|
||||
877123F31BDA797800530B1E /* video0.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 877123F21BDA797800530B1E /* video0.mp4 */; };
|
||||
AA017F581BD7787300F45E9D /* libShimulator.dylib in Resources */ = {isa = PBXBuildFile; fileRef = AA017F4C1BD7784700F45E9D /* libShimulator.dylib */; };
|
||||
AA0F6F2A1CA3DCF700926518 /* FBWeakFrameworkLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = AA0F6F281CA3DCF700926518 /* FBWeakFrameworkLoader.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AA0F6F2B1CA3DCF700926518 /* FBWeakFrameworkLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = AA0F6F291CA3DCF700926518 /* FBWeakFrameworkLoader.m */; };
|
||||
AA111CCE1BBE7C5A0054AFDD /* CoreSimulatorDoubles.m in Sources */ = {isa = PBXBuildFile; fileRef = AA111CCD1BBE7C5A0054AFDD /* CoreSimulatorDoubles.m */; };
|
||||
AA19DA861C7740BB009BB89B /* FBSimulatorPoolTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = AA19DA851C7740BB009BB89B /* FBSimulatorPoolTestCase.m */; };
|
||||
AA19DA881C77450A009BB89B /* FBSimulatorPool+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = AA19DA871C77450A009BB89B /* FBSimulatorPool+Private.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
|
@ -347,6 +349,8 @@
|
|||
1DD70E29B6970A5500000000 /* CoreSimulator.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; name = CoreSimulator.framework; path = Library/PrivateFrameworks/CoreSimulator.framework; sourceTree = DEVELOPER_DIR; };
|
||||
877123F21BDA797800530B1E /* video0.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = video0.mp4; sourceTree = "<group>"; };
|
||||
AA017F4C1BD7784700F45E9D /* libShimulator.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libShimulator.dylib; sourceTree = "<group>"; };
|
||||
AA0F6F281CA3DCF700926518 /* FBWeakFrameworkLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBWeakFrameworkLoader.h; sourceTree = "<group>"; };
|
||||
AA0F6F291CA3DCF700926518 /* FBWeakFrameworkLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBWeakFrameworkLoader.m; sourceTree = "<group>"; };
|
||||
AA111CCC1BBE7C5A0054AFDD /* CoreSimulatorDoubles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreSimulatorDoubles.h; sourceTree = "<group>"; };
|
||||
AA111CCD1BBE7C5A0054AFDD /* CoreSimulatorDoubles.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CoreSimulatorDoubles.m; sourceTree = "<group>"; };
|
||||
AA19DA841C7740BB009BB89B /* FBSimulatorPoolTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSimulatorPoolTestCase.h; sourceTree = "<group>"; };
|
||||
|
@ -1531,6 +1535,8 @@
|
|||
EEBD60581C9062E900298A07 /* FBControlCoreLogger.m */,
|
||||
AACA33561C96F8D100DC9704 /* FBFileFinder.h */,
|
||||
AACA33571C96F8D100DC9704 /* FBFileFinder.m */,
|
||||
AA0F6F281CA3DCF700926518 /* FBWeakFrameworkLoader.h */,
|
||||
AA0F6F291CA3DCF700926518 /* FBWeakFrameworkLoader.m */,
|
||||
AA84EFFC1C9FE162000CDA41 /* NSPredicate+FBControlCore.h */,
|
||||
AA84EFFD1C9FE162000CDA41 /* NSPredicate+FBControlCore.m */,
|
||||
EEBD60591C9062E900298A07 /* NSRunLoop+FBControlCore.h */,
|
||||
|
@ -1666,6 +1672,7 @@
|
|||
AAEA3AA61C90BB62004F8409 /* FBLogSearch.h in Headers */,
|
||||
EEBD607C1C9062E900298A07 /* FBConcurrentCollectionOperations.h in Headers */,
|
||||
EEBD60781C9062E900298A07 /* FBCapacityQueue.h in Headers */,
|
||||
AA0F6F2A1CA3DCF700926518 /* FBWeakFrameworkLoader.h in Headers */,
|
||||
EEBD60821C9062E900298A07 /* FBControlCoreLogger.h in Headers */,
|
||||
EEBD60971C908FA200298A07 /* FBJSONConversion.h in Headers */,
|
||||
EEBD606F1C9062E900298A07 /* FBTaskExecutor+Convenience.h in Headers */,
|
||||
|
@ -2065,6 +2072,7 @@
|
|||
AA84EFFF1C9FE162000CDA41 /* NSPredicate+FBControlCore.m in Sources */,
|
||||
EEBD60951C908F8500298A07 /* FBCollectionInformation.m in Sources */,
|
||||
EEBD60771C9062E900298A07 /* FBBinaryParser.m in Sources */,
|
||||
AA0F6F2B1CA3DCF700926518 /* FBWeakFrameworkLoader.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -63,64 +63,6 @@
|
|||
|
||||
#pragma mark Framework Loading
|
||||
|
||||
+ (BOOL)loadPrivateFrameworks:(id<FBControlCoreLogger>)logger error:(NSError **)error
|
||||
{
|
||||
static BOOL hasLoaded = NO;
|
||||
if (hasLoaded) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
// This will assert if the directory could not be found.
|
||||
NSString *developerDirectory = FBControlCoreGlobalConfiguration.developerDirectory;
|
||||
|
||||
// A Mapping of Class Names to the Frameworks that they belong to. This serves to:
|
||||
// 1) Represent the Frameworks that FBSimulatorControl is dependent on via their classes
|
||||
// 2) Provide a path to the relevant Framework.
|
||||
// 3) Provide a class for sanity checking the Framework load.
|
||||
// 4) Provide a class that can be checked before the Framework load to avoid re-loading the same
|
||||
// Framework if others have done so before.
|
||||
// 5) Provide a sanity check that any preloaded Private Frameworks match the current xcode-select version
|
||||
NSDictionary *classMapping = @{
|
||||
@"SimDevice" : @"Library/PrivateFrameworks/CoreSimulator.framework",
|
||||
@"SimDeviceFramebufferService" : @"Library/PrivateFrameworks/SimulatorKit.framework",
|
||||
@"DVTDevice" : @"../SharedFrameworks/DVTFoundation.framework",
|
||||
@"DTiPhoneSimulatorApplicationSpecifier" : @"../SharedFrameworks/DVTiPhoneSimulatorRemoteClient.framework"
|
||||
};
|
||||
[logger logFormat:@"Using Developer Directory %@", developerDirectory];
|
||||
|
||||
for (NSString *className in classMapping) {
|
||||
NSString *relativePath = classMapping[className];
|
||||
NSString *path = [[developerDirectory stringByAppendingPathComponent:relativePath] stringByStandardizingPath];
|
||||
|
||||
// The Class exists, therefore has been loaded
|
||||
if (NSClassFromString(className)) {
|
||||
[logger logFormat:@"%@ is already loaded, skipping load of framework %@", className, path];
|
||||
NSError *innerError = nil;
|
||||
if (![self verifyDeveloperDirectoryForPrivateClass:className developerDirectory:developerDirectory logger:logger error:&innerError]) {
|
||||
return [FBSimulatorError failBoolWithError:innerError errorOut:error];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise load the Framework.
|
||||
[logger logFormat:@"%@ is not loaded. Loading %@ at path %@", className, path.lastPathComponent, path];
|
||||
NSError *innerError = nil;
|
||||
if (![self loadFrameworkAtPath:path logger:logger error:&innerError]) {
|
||||
return [FBSimulatorError failBoolWithError:innerError errorOut:error];
|
||||
}
|
||||
[logger logFormat:@"Loaded %@ from %@", className, path];
|
||||
}
|
||||
|
||||
// We're done with loading Frameworks.
|
||||
hasLoaded = YES;
|
||||
[logger logFormat:@"Loaded All Private Frameworks %@", [FBCollectionInformation oneLineDescriptionFromArray:classMapping.allValues atKeyPath:@"lastPathComponent"]];
|
||||
|
||||
// Set CoreSimulator Logging since it is now loaded.
|
||||
[self setCoreSimulatorLoggingEnabled:FBControlCoreGlobalConfiguration.debugLoggingEnabled];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (void)loadPrivateFrameworksOrAbort
|
||||
{
|
||||
id<FBControlCoreLogger> logger = FBControlCoreGlobalConfiguration.defaultLogger;
|
||||
|
@ -133,6 +75,20 @@
|
|||
abort();
|
||||
}
|
||||
|
||||
+ (BOOL)loadPrivateFrameworks:(id<FBControlCoreLogger>)logger error:(NSError **)error
|
||||
{
|
||||
NSDictionary *classMapping = @{
|
||||
@"SimDevice" : @"Library/PrivateFrameworks/CoreSimulator.framework",
|
||||
@"SimDeviceFramebufferService" : @"Library/PrivateFrameworks/SimulatorKit.framework",
|
||||
@"DVTDevice" : @"../SharedFrameworks/DVTFoundation.framework",
|
||||
@"DTiPhoneSimulatorApplicationSpecifier" : @"../SharedFrameworks/DVTiPhoneSimulatorRemoteClient.framework"
|
||||
};
|
||||
BOOL result = [FBWeakFrameworkLoader loadPrivateFrameworks:classMapping logger:logger error:error];
|
||||
// Set CoreSimulator Logging since it is now loaded.
|
||||
[self setCoreSimulatorLoggingEnabled:FBControlCoreGlobalConfiguration.debugLoggingEnabled];
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma mark Private Methods
|
||||
|
||||
+ (void)setCoreSimulatorLoggingEnabled:(BOOL)enabled
|
||||
|
@ -141,52 +97,4 @@
|
|||
[simulatorDefaults setBool:enabled forKey:@"DebugLogging"];
|
||||
}
|
||||
|
||||
+ (BOOL)loadFrameworkAtPath:(NSString *)path logger:(id<FBControlCoreLogger>)logger error:(NSError **)error
|
||||
{
|
||||
NSBundle *bundle = [NSBundle bundleWithPath:path];
|
||||
if (!bundle) {
|
||||
return [[FBSimulatorError
|
||||
describeFormat:@"Failed to load the bundle for path %@", path]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
NSError *innerError = nil;
|
||||
if (![bundle loadAndReturnError:&innerError]) {
|
||||
return [[FBSimulatorError
|
||||
describeFormat:@"Failed to load the the Framework Bundle %@", bundle]
|
||||
failBool:error];
|
||||
}
|
||||
[logger logFormat:@"Successfully loaded %@", path.lastPathComponent];
|
||||
return YES;
|
||||
}
|
||||
|
||||
/**
|
||||
Given that it is possible for FBSimulatorControl.framework to be loaded after any of the
|
||||
Private Frameworks upon which it depends, it's possible that these Frameworks may have
|
||||
been loaded from a different Developer Directory.
|
||||
|
||||
In order to prevent crazy behaviour from arising, FBSimulatorControl will check the
|
||||
directories of these Frameworks match the one that is currently set.
|
||||
*/
|
||||
+ (BOOL)verifyDeveloperDirectoryForPrivateClass:(NSString *)className developerDirectory:(NSString *)developerDirectory logger:(id<FBControlCoreLogger>)logger error:(NSError **)error
|
||||
{
|
||||
NSBundle *bundle = [NSBundle bundleForClass:NSClassFromString(className)];
|
||||
if (!bundle) {
|
||||
return [[FBSimulatorError
|
||||
describeFormat:@"Could not obtain Framework bundle for class named %@", className]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
// Developer Directory is: /Applications/Xcode.app/Contents/Developer
|
||||
// The common base path is: is: /Applications/Xcode.app
|
||||
NSString *basePath = [[developerDirectory stringByDeletingLastPathComponent] stringByDeletingLastPathComponent];
|
||||
if (![bundle.bundlePath hasPrefix:basePath]) {
|
||||
return [[FBSimulatorError
|
||||
describeFormat:@"Expected Framework %@ to be loaded for Developer Directory at path %@, but was loaded from %@", bundle.bundlePath.lastPathComponent, bundle.bundlePath, developerDirectory]
|
||||
failBool:error];
|
||||
}
|
||||
[logger logFormat:@"%@ has correct path of %@", className, basePath];
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
Загрузка…
Ссылка в новой задаче