Merge pull request #7 from Unity-Technologies/graphics/ugk/update-to-latest
Update to latest idb to enable compatibility with more recent devices
This commit is contained in:
Коммит
aeca9660bb
|
@ -11,7 +11,7 @@ version: 2
|
|||
jobs:
|
||||
deploy-website:
|
||||
docker:
|
||||
- image: circleci/node:14.11.0
|
||||
- image: cimg/node:16.14.0
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
name: facebook/idb/build_and_deploy
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
deploy-website:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16.14.0
|
||||
- name: Deploying to GitHub Pages
|
||||
run: |
|
||||
git config --global user.email "docusaurus-bot@users.noreply.github.com"
|
||||
git config --global user.name "Website Deployment Script"
|
||||
echo "machine github.com login docusaurus-bot password ${{ secrets.GITHUB_TOKEN }}" > ~/.netrc
|
||||
echo "Deploying website..."
|
||||
cd website && yarn install && GIT_USER=docusaurus-bot USE_SSH=false yarn deploy
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import FBControlCore
|
||||
import Foundation
|
||||
import IDBCompanionUtilities
|
||||
|
||||
enum FBFutureError: Error {
|
||||
|
||||
/// This indicates an error in objc code where result callback was called but no error or result provided. In case of this, debug `FBFuture` implementation.
|
||||
case continuationFullfilledWithoutValues
|
||||
|
||||
/// This indicates an error in `BridgeFuture.values` implementation. In case of this, debug `BridgeFuture.values` implementation.
|
||||
case taskGroupReceivedNilResultInternalError
|
||||
}
|
||||
|
||||
/// Swift compiler does not allow usage of generic parameters of objc classes in extension
|
||||
/// so we need to create a bridge for convenience.
|
||||
public enum BridgeFuture {
|
||||
|
||||
/// Use this to receive results from multiple futures. The results are **ordered in the same order as passed futures**, so you can safely access them from
|
||||
/// array by indexes.
|
||||
/// - Note: We should *not* use @discardableResult, results should be dropped explicitly by the callee.
|
||||
public static func values<T: AnyObject>(_ futures: FBFuture<T>...) async throws -> [T] {
|
||||
let futuresArr: [FBFuture<T>] = futures
|
||||
return try await values(futuresArr)
|
||||
}
|
||||
|
||||
/// Use this to receive results from multiple futures. The results are **ordered in the same order as passed futures**, so you can safely access them from
|
||||
/// array by indexes.
|
||||
/// - Note: We should *not* use @discardableResult, results should be dropped explicitly by the callee.
|
||||
public static func values<T: AnyObject>(_ futures: [FBFuture<T>]) async throws -> [T] {
|
||||
return try await withThrowingTaskGroup(of: (Int, T).self, returning: [T].self) { group in
|
||||
var results = [T?].init(repeating: nil, count: futures.count)
|
||||
|
||||
for (index, future) in futures.enumerated() {
|
||||
group.addTask {
|
||||
return try await (index, BridgeFuture.value(future))
|
||||
}
|
||||
}
|
||||
|
||||
for try await (index, value) in group {
|
||||
results[index] = value
|
||||
}
|
||||
|
||||
return try results.map { value -> T in
|
||||
guard let shouldDefinitelyExist = value else {
|
||||
assertionFailure("This should never happen. We should fullfill all values at that moment")
|
||||
throw FBFutureError.taskGroupReceivedNilResultInternalError
|
||||
}
|
||||
return shouldDefinitelyExist
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Awaitable value that waits for publishing from the wrapped future
|
||||
/// - Note: We should *not* use @discardableResult, results should be dropped explicitly by the callee.
|
||||
public static func value<T: AnyObject>(_ future: FBFuture<T>) async throws -> T {
|
||||
try await withTaskCancellationHandler {
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
future.onQueue(BridgeQueues.futureSerialFullfillmentQueue, notifyOfCompletion: { resultFuture in
|
||||
if let error = resultFuture.error {
|
||||
continuation.resume(throwing: error)
|
||||
} else if let value = resultFuture.result {
|
||||
// swiftlint:disable force_cast
|
||||
continuation.resume(returning: value as! T)
|
||||
} else {
|
||||
continuation.resume(throwing: FBFutureError.continuationFullfilledWithoutValues)
|
||||
}
|
||||
})
|
||||
}
|
||||
} onCancel: {
|
||||
future.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
/// Awaitable value that waits for publishing from the wrapped future.
|
||||
/// This is convenient bridgeable overload for dealing with objc `NSArray`.
|
||||
/// - Warning: This operation not safe (as most of objc bridge). That means you should be sure that type bridging will succeed.
|
||||
/// Consider this method as
|
||||
///
|
||||
/// ```
|
||||
/// // ------- command_executor.m
|
||||
/// - (FBFuture<NSArray<NSNumer> *> *)doTheThing;
|
||||
///
|
||||
/// // ------- swiftfile.swift
|
||||
/// let futureFromObjc: FBFuture<NSArray> = command_executor.doTheThing() // Note: NSNumber is lost
|
||||
/// let withoutBridge = BridgeFuture.value(futureFromObjc) // withoutBridge: NSArray
|
||||
/// let withBridge: [NSNumer] = BridgeFuture.value(futureFromObjc) // withBridge: [NSNumber]
|
||||
///
|
||||
/// // But this starts to shine more when you have to pass results to methods/return results, e.g.
|
||||
/// func operation() -> [Int] {
|
||||
/// return BridgeFuture.value(futureFromObjc)
|
||||
/// }
|
||||
///
|
||||
/// // Or pass value to some oter method
|
||||
/// func someMethod(accepts: [NSNumber]) { ... }
|
||||
///
|
||||
/// self.someMethod(accepts: BridgeFuture.value(futureFromObjc)
|
||||
/// ```
|
||||
public static func value<T>(_ future: FBFuture<NSArray>) async throws -> [T] {
|
||||
let objcValue = try await value(future)
|
||||
// swiftlint:disable force_cast
|
||||
return objcValue as! [T]
|
||||
}
|
||||
|
||||
/// Awaitable value that waits for publishing from the wrapped future.
|
||||
/// This is convenient bridgeable overload for dealing with objc `NSDictionary`.
|
||||
/// - Warning: This operation not safe (as most of objc bridge). That means you should be sure that type bridging will succeed.
|
||||
/// Consider this method as
|
||||
///
|
||||
/// ```
|
||||
/// // ------- command_executor.m
|
||||
/// - (FBFuture<NSDictionary<FBInstalledApplication *, id> *> *)doTheThing;
|
||||
///
|
||||
/// // ------- swiftfile.swift
|
||||
/// let futureFromObjc: FBFuture<NSDictionary> = command_executor.doTheThing() // Note: types is lost
|
||||
/// let withoutBridge = BridgeFuture.value(futureFromObjc) // withoutBridge: NSDictionary
|
||||
/// let withBridge: [FBInstalledApplication: Any] = BridgeFuture.value(futureFromObjc) // withBridge: [FBInstalledApplication: Any]
|
||||
///
|
||||
/// // But this starts to shine more when you have to pass results to methods/return results, e.g.
|
||||
/// func operation() -> [FBInstalledApplication: Any] {
|
||||
/// return BridgeFuture.value(futureFromObjc)
|
||||
/// }
|
||||
///
|
||||
/// // Or pass value to some oter method
|
||||
/// func someMethod(accepts: [FBInstalledApplication: Any]) { ... }
|
||||
///
|
||||
/// self.someMethod(accepts: BridgeFuture.value(futureFromObjc)
|
||||
/// ```
|
||||
public static func value<T: Hashable, U>(_ future: FBFuture<NSDictionary>) async throws -> [T: U] {
|
||||
let objcValue = try await value(future)
|
||||
// swiftlint:disable force_cast
|
||||
return objcValue as! [T: U]
|
||||
}
|
||||
|
||||
/// NSNull is Void equivalent in objc reference world. So is is safe to ignore the result.
|
||||
public static func await(_ future: FBFuture<NSNull>) async throws {
|
||||
_ = try await Self.value(future)
|
||||
}
|
||||
|
||||
/// This overload exists because of `FBMutableFuture` does not convert its exact generic type automatically but it can be automatically converted to `FBFuture<AnyObject>`
|
||||
/// without any problems. This decision may be revisited in future.
|
||||
public static func await(_ future: FBFuture<AnyObject>) async throws {
|
||||
_ = try await Self.value(future)
|
||||
}
|
||||
|
||||
/// Interop between swift and objc generics are quite bad, so we have to write wrappers like this.
|
||||
/// By default swift bridge compiler could not convert generic type of `FBMutableFuture`. But this force cast is 100% valid and works in runtime
|
||||
/// so we just use this little helper.
|
||||
public static func convertToFuture<T: AnyObject>(_ mutableFuture: FBMutableFuture<T>) -> FBFuture<T> {
|
||||
let future: FBFuture<AnyObject> = mutableFuture
|
||||
// swiftlint:disable force_cast
|
||||
return future as! FBFuture<T>
|
||||
}
|
||||
|
||||
/// Split FBFutureContext to two pieces: result and later cleanup closure
|
||||
/// - Parameter futureContext: source future context
|
||||
/// - Returns: Tuple of extracted result and cleanup closure that **should** be called later to perform all required cleanups
|
||||
public static func value<T: AnyObject>(_ futureContext: FBFutureContext<T>) async throws -> T {
|
||||
try FBTeardownContext.current.addCleanup {
|
||||
let cleanupFuture = futureContext.onQueue(BridgeQueues.futureSerialFullfillmentQueue) { (result: Any, teardown: FBMutableFuture<NSNull>) -> NSNull in
|
||||
teardown.resolve(withResult: NSNull())
|
||||
return NSNull()
|
||||
}
|
||||
try await BridgeFuture.await(cleanupFuture)
|
||||
}
|
||||
|
||||
return try await value(futureContext.future)
|
||||
}
|
||||
|
||||
/// Awaitable value that waits for publishing from the wrapped futureContext.
|
||||
/// This is convenient bridgeable overload for dealing with objc `NSArray`.
|
||||
/// - Warning: This operation not safe (as most of objc bridge). That means you should be sure that type bridging will succeed.
|
||||
/// Consider this method as
|
||||
///
|
||||
/// ```
|
||||
/// // ------- command_executor.m
|
||||
/// - (FBFuture<NSArray<NSNumer> *> *)doTheThing;
|
||||
///
|
||||
/// // ------- swiftfile.swift
|
||||
/// let futureFromObjc: FBFuture<NSArray> = command_executor.doTheThing() // Note: NSNumber is lost
|
||||
/// let withoutBridge = BridgeFuture.value(futureFromObjc) // withoutBridge: NSArray
|
||||
/// let withBridge: [NSNumer] = BridgeFuture.value(futureFromObjc) // withBridge: [NSNumber]
|
||||
///
|
||||
/// // But this starts to shine more when you have to pass results to methods/return results, e.g.
|
||||
/// func operation() -> [Int] {
|
||||
/// return BridgeFuture.value(futureFromObjc)
|
||||
/// }
|
||||
///
|
||||
/// // Or pass value to some oter method
|
||||
/// func someMethod(accepts: [NSNumber]) { ... }
|
||||
///
|
||||
/// self.someMethod(accepts: BridgeFuture.value(futureFromObjc)
|
||||
/// ```
|
||||
public static func values<T: AnyObject, U>(_ futureContext: FBFutureContext<T>) async throws -> [U] {
|
||||
let objcValue = try await value(futureContext)
|
||||
// swiftlint:disable force_cast
|
||||
return objcValue as! [U]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Queues collection to bring objc and async-swift worlds together
|
||||
public enum BridgeQueues {
|
||||
|
||||
/// Plain serial queue that is primarily used to convert *all* FBFuture calls to swift awaitable values
|
||||
public static let futureSerialFullfillmentQueue = DispatchQueue(label: "com.facebook.fbfuture.fullfilment")
|
||||
|
||||
/// Some of *commandExecutor* operations requires DispatchQueue to send response.
|
||||
/// The only purpose of everything handled inside this queue is to passthrough call to swift async world via calling swift `Task` api
|
||||
/// ```
|
||||
/// commandExecutor.doSomething(onQueue: BridgeQueues.miscEventReaderQueue) { jobResult in
|
||||
/// Task {
|
||||
/// try? await responseStream.send(jobResult)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
public static let miscEventReaderQueue = DispatchQueue(label: "com.facebook.miscellaneous.reader", qos: .userInitiated, attributes: .concurrent)
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <CompanionLib/FBIDBCommandExecutor.h>
|
||||
#import <CompanionLib/FBXCTestDescriptor.h>
|
||||
#import <CompanionLib/FBIDBLogger.h>
|
||||
#import <CompanionLib/FBCodeCoverageRequest.h>
|
||||
#import <CompanionLib/FBIDBLogger.h>
|
||||
#import <CompanionLib/FBDsymInstallLinkToBundle.h>
|
||||
#import <CompanionLib/FBXCTestRunRequest.h>
|
||||
#import <CompanionLib/FBDataDownloadInput.h>
|
||||
#import <CompanionLib/FBIDBLogger.h>
|
||||
#import <CompanionLib/FBIDBStorageManager.h>
|
||||
#import <CompanionLib/FBIDBTestOperation.h>
|
||||
#import <CompanionLib/FBiOSTargetProvider.h>
|
||||
#import <CompanionLib/FBTestApplicationsPair.h>
|
||||
#import <CompanionLib/FBXCTestDescriptor.h>
|
||||
#import <CompanionLib/FBXCTestReporterConfiguration.h>
|
||||
#import <CompanionLib/FBXCTestRunFileReader.h>
|
||||
#import <CompanionLib/FBIDBError.h>
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -9,11 +9,12 @@
|
|||
|
||||
#import <FBControlCore/FBControlCore.h>
|
||||
|
||||
#import "FBXCTestDescriptor.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol FBXCTestDescriptor;
|
||||
@protocol FBControlCoreLogger;
|
||||
@class FBBundleStorageManager;
|
||||
@class FBDsymInstallLinkToBundle;
|
||||
@class FBIDBLogger;
|
||||
@class FBIDBPortsConfiguration;
|
||||
@class FBIDBStorageManager;
|
||||
|
@ -21,9 +22,15 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@class FBInstalledArtifact;
|
||||
@class FBSimulatorHIDEvent;
|
||||
@class FBTemporaryDirectory;
|
||||
@class FBXCTestRunRequest;
|
||||
|
||||
@protocol FBXCTestReporter;
|
||||
|
||||
extern FBFileContainerKind const FBFileContainerKindXctest;
|
||||
extern FBFileContainerKind const FBFileContainerKindDylib;
|
||||
extern FBFileContainerKind const FBFileContainerKindDsym;
|
||||
extern FBFileContainerKind const FBFileContainerKindFramework;
|
||||
|
||||
@interface FBIDBCommandExecutor : NSObject
|
||||
|
||||
#pragma mark Initializers
|
||||
|
@ -34,11 +41,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@param target the target to run against.
|
||||
@param storageManager storage for all bundles
|
||||
@param temporaryDirectory the temporary directory to use.
|
||||
@param ports the ports to use.
|
||||
@param debugserverPort will launch debug server at that port.
|
||||
@param logger a logger to log to.
|
||||
@return a new FBIDBCommandExecutor instance
|
||||
*/
|
||||
+ (instancetype)commandExecutorForTarget:(id<FBiOSTarget>)target storageManager:(FBIDBStorageManager *)storageManager temporaryDirectory:(FBTemporaryDirectory *)temporaryDirectory ports:(FBIDBPortsConfiguration *)ports logger:(FBIDBLogger *)logger;
|
||||
+ (instancetype)commandExecutorForTarget:(id<FBiOSTarget>)target storageManager:(FBIDBStorageManager *)storageManager temporaryDirectory:(FBTemporaryDirectory *)temporaryDirectory debugserverPort:(in_port_t)debugserverPort logger:(FBIDBLogger *)logger;
|
||||
|
||||
#pragma mark Properties
|
||||
|
||||
|
@ -72,9 +79,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@param filePath the path to a file on disk with the file.
|
||||
@param makeDebuggable whether the app should be installed in a debuggable state or not.
|
||||
@param overrideModificationTime if YES the archive contests' `mtime` will be ignored. Current timestamp will be used as mtime of extracted files/directories.
|
||||
@return A future that resolves with the App Bundle Id
|
||||
*/
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_app_file_path:(NSString *)filePath make_debuggable:(BOOL)makeDebuggable;
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_app_file_path:(NSString *)filePath make_debuggable:(BOOL)makeDebuggable override_modification_time:(BOOL)overrideModificationTime;
|
||||
|
||||
/**
|
||||
Install an App via a Data stream.
|
||||
|
@ -82,25 +90,28 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@param input the input to pipe.
|
||||
@param compression the compression type to use
|
||||
@param makeDebuggable whether the app should be installed in a debuggable state or not.
|
||||
@param overrideModificationTime if YES the archive contests' `mtime` will be ignored. Current timestamp will be used as mtime of extracted files/directories.
|
||||
@return A future that resolves with the App Bundle Id
|
||||
*/
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_app_stream:(FBProcessInput *)input compression:(FBCompressionFormat)compression make_debuggable:(BOOL)makeDebuggable;
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_app_stream:(FBProcessInput *)input compression:(FBCompressionFormat)compression make_debuggable:(BOOL)makeDebuggable override_modification_time:(BOOL)overrideModificationTime;
|
||||
|
||||
/**
|
||||
Installs an xctest bundle by file path.
|
||||
|
||||
@param filePath the local file path of the xctest bundle
|
||||
@param skipSigningBundles pass true to skip signing xctest bundles.
|
||||
@return a Future that resolves with the xctest identifier.
|
||||
*/
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_xctest_app_file_path:(NSString *)filePath;
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_xctest_app_file_path:(NSString *)filePath skipSigningBundles:(BOOL)skipSigningBundles;
|
||||
|
||||
/**
|
||||
Installs an xctest bundle by a stream of tar data
|
||||
|
||||
@param input a tar stream of the xctest data.
|
||||
@param skipSigningBundles pass true to skip signing xctest bundles.
|
||||
@return a Future that resolves with the xctest identifier.
|
||||
*/
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_xctest_app_stream:(FBProcessInput *)input;
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_xctest_app_stream:(FBProcessInput *)input skipSigningBundles:(BOOL)skipSigningBundles;
|
||||
|
||||
/**
|
||||
Installs a dylib from a file path.
|
||||
|
@ -139,19 +150,20 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
Installs a dSYM from a file path.
|
||||
|
||||
@param filePath the input to pipe.
|
||||
@param bundleID if specified installed dsym will be linked into the app bundle container.
|
||||
@param linkTo if specified installed dsym will be linked into bundle container.
|
||||
@return A future that resolves with the dSYM Name
|
||||
*/
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_dsym_file_path:(NSString *)filePath linkToApp:(nullable NSString *)bundleID;
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_dsym_file_path:(NSString *)filePath linkTo:(nullable FBDsymInstallLinkToBundle *)linkTo;
|
||||
|
||||
/**
|
||||
Installs dSYM(s) from a zip stream.
|
||||
|
||||
@param input the input to pipe.
|
||||
@param bundleID if specified installed dsym will be linked into the app bundle container.
|
||||
@param compression the compression type to use
|
||||
@param linkTo if specified installed dsym will be linked into bundle container.
|
||||
@return A future that resolves with the directory containing the dSYM(s)
|
||||
*/
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_dsym_stream:(FBProcessInput *)input linkToApp:(nullable NSString *)bundleID;
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_dsym_stream:(FBProcessInput *)input compression:(FBCompressionFormat)compression linkTo:(nullable FBDsymInstallLinkToBundle *)linkTo;
|
||||
|
||||
/**
|
||||
Takes a Screenshot
|
||||
|
@ -168,7 +180,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@param nestedFormat YES if the legacy format should be used, NO otherwise.
|
||||
@return A Future that resolves with the accessibility info
|
||||
*/
|
||||
- (FBFuture<NSArray<NSDictionary<NSString *, id> *> *> *)accessibility_info_at_point:(nullable NSValue *)point nestedFormat:(BOOL)nestedFormat;
|
||||
- (FBFuture<id> *)accessibility_info_at_point:(nullable NSValue *)point nestedFormat:(BOOL)nestedFormat;
|
||||
|
||||
/**
|
||||
Adds media files (photos, videos, ...) to the target
|
||||
|
@ -211,7 +223,16 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@param bundleID app to approve services for
|
||||
@return a Future that resolves when complete.
|
||||
*/
|
||||
- (FBFuture<NSNull *> *)approve:(NSSet<FBSettingsApprovalService> *)services for_application:(NSString *)bundleID;
|
||||
- (FBFuture<NSNull *> *)approve:(NSSet<FBTargetSettingsService> *)services for_application:(NSString *)bundleID;
|
||||
|
||||
/**
|
||||
Revokes the given services for an app
|
||||
|
||||
@param services services to revoke
|
||||
@param bundleID app to revoke services for
|
||||
@return a Future that resolves when complete.
|
||||
*/
|
||||
- (FBFuture<NSNull *> *)revoke:(NSSet<FBTargetSettingsService> *)services for_application:(NSString *)bundleID;
|
||||
|
||||
/**
|
||||
Approves the deeplink given a schema and app.
|
||||
|
@ -223,6 +244,16 @@ This allows to avoid the permission popup the first time we open a deeplink
|
|||
*/
|
||||
- (FBFuture<NSNull *> *)approve_deeplink:(NSString *)scheme for_application:(NSString *)bundleID;
|
||||
|
||||
/**
|
||||
Revokes the deeplink given a schema and app.
|
||||
This enables the permission popup the first time we open a deeplink
|
||||
|
||||
@param scheme scheme of the deeplink url (the part before ":")
|
||||
@param bundleID app to revoke services for
|
||||
@return a Future that resolves when complete.
|
||||
*/
|
||||
- (FBFuture<NSNull *> *)revoke_deeplink:(NSString *)scheme for_application:(NSString *)bundleID;
|
||||
|
||||
/**
|
||||
Open a url on the target
|
||||
|
||||
|
@ -249,9 +280,9 @@ This allows to avoid the permission popup the first time we open a deeplink
|
|||
/**
|
||||
List the xctests installed
|
||||
|
||||
@return a Future that resolves with a set of tests.
|
||||
@return a Future that resolves with an array of tests.
|
||||
*/
|
||||
- (FBFuture<NSSet<id<FBXCTestDescriptor>> *> *)list_test_bundles;
|
||||
- (FBFuture<NSArray<id<FBXCTestDescriptor>> *> *)list_test_bundles;
|
||||
|
||||
/**
|
||||
List the tests in an installed bundle
|
||||
|
@ -368,10 +399,11 @@ This allows to avoid the permission popup the first time we open a deeplink
|
|||
|
||||
@param name preference name
|
||||
@param value preference value
|
||||
@param type preference value type
|
||||
@param domain preference domain - optional
|
||||
@return a Future that resolves when successful.
|
||||
*/
|
||||
- (FBFuture<NSNull *> *)set_preference:(NSString *)name value:(NSString *)value domain:(nullable NSString *)domain;
|
||||
- (FBFuture<NSNull *> *)set_preference:(NSString *)name value:(NSString *)value type:(nullable NSString *)type domain:(nullable NSString *)domain;
|
||||
|
||||
/**
|
||||
Gets a preference value by its name and domain. If domain not specified assumed to be Apple Global Domain
|
||||
|
@ -512,7 +544,7 @@ This allows to avoid the permission popup the first time we open a deeplink
|
|||
|
||||
/**
|
||||
Spawn a dap protocol server from dapPath
|
||||
|
||||
|
||||
@param dapPath relative path to the root container where dap is installed
|
||||
@param stdIn where the dap process reads
|
||||
@param stdOut where the dap process writes
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -10,17 +10,24 @@
|
|||
#import <FBSimulatorControl/FBSimulatorControl.h>
|
||||
#import <FBDeviceControl/FBDeviceControl.h>
|
||||
|
||||
#import "FBXCTestDescriptor.h"
|
||||
#import "FBXCTestRunRequest.h"
|
||||
#import "FBXCTestRunRequest.h"
|
||||
#import "FBIDBStorageManager.h"
|
||||
#import "FBIDBError.h"
|
||||
#import "FBIDBLogger.h"
|
||||
#import "FBIDBPortsConfiguration.h"
|
||||
#import "FBDsymInstallLinkToBundle.h"
|
||||
|
||||
FBFileContainerKind const FBFileContainerKindXctest = @"xctest";
|
||||
FBFileContainerKind const FBFileContainerKindDylib = @"dylib";
|
||||
FBFileContainerKind const FBFileContainerKindDsym = @"dsym";
|
||||
FBFileContainerKind const FBFileContainerKindFramework = @"framework";
|
||||
|
||||
@interface FBIDBCommandExecutor ()
|
||||
|
||||
@property (nonatomic, strong, readonly) id<FBiOSTarget> target;
|
||||
@property (nonatomic, strong, readonly) FBIDBLogger *logger;
|
||||
@property (nonatomic, strong, readonly) FBIDBPortsConfiguration *ports;
|
||||
@property (nonatomic, readonly) in_port_t debugserverPort;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -28,12 +35,12 @@
|
|||
|
||||
#pragma mark Initializers
|
||||
|
||||
+ (instancetype)commandExecutorForTarget:(id<FBiOSTarget>)target storageManager:(FBIDBStorageManager *)storageManager temporaryDirectory:(FBTemporaryDirectory *)temporaryDirectory ports:(FBIDBPortsConfiguration *)ports logger:(FBIDBLogger *)logger
|
||||
+ (instancetype)commandExecutorForTarget:(id<FBiOSTarget>)target storageManager:(FBIDBStorageManager *)storageManager temporaryDirectory:(FBTemporaryDirectory *)temporaryDirectory debugserverPort:(in_port_t)debugserverPort logger:(FBIDBLogger *)logger
|
||||
{
|
||||
return [[self alloc] initWithTarget:target storageManager:storageManager temporaryDirectory:temporaryDirectory ports:ports logger:[logger withName:@"grpc_handler"]];
|
||||
return [[self alloc] initWithTarget:target storageManager:storageManager temporaryDirectory:temporaryDirectory debugserverPort:debugserverPort logger:[logger withName:@"grpc_handler"]];
|
||||
}
|
||||
|
||||
- (instancetype)initWithTarget:(id<FBiOSTarget>)target storageManager:(FBIDBStorageManager *)storageManager temporaryDirectory:(FBTemporaryDirectory *)temporaryDirectory ports:(FBIDBPortsConfiguration *)ports logger:(FBIDBLogger *)logger
|
||||
- (instancetype)initWithTarget:(id<FBiOSTarget>)target storageManager:(FBIDBStorageManager *)storageManager temporaryDirectory:(FBTemporaryDirectory *)temporaryDirectory debugserverPort:(in_port_t)debugserverPort logger:(FBIDBLogger *)logger
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
|
@ -43,7 +50,7 @@
|
|||
_target = target;
|
||||
_storageManager = storageManager;
|
||||
_temporaryDirectory = temporaryDirectory;
|
||||
_ports = ports;
|
||||
_debugserverPort = debugserverPort;
|
||||
_logger = logger;
|
||||
|
||||
return self;
|
||||
|
@ -69,29 +76,29 @@
|
|||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_app_file_path:(NSString *)filePath make_debuggable:(BOOL)makeDebuggable
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_app_file_path:(NSString *)filePath make_debuggable:(BOOL)makeDebuggable override_modification_time:(BOOL)overrideModificationTime
|
||||
{
|
||||
// Use .app directly, or extract an .ipa
|
||||
if ([FBBundleDescriptor isApplicationAtPath:filePath]) {
|
||||
return [self installAppBundle:[FBFutureContext futureContextWithFuture:[FBBundleDescriptor extractedApplicationAtPath:filePath]] makeDebuggable:makeDebuggable];
|
||||
} else {
|
||||
return [self installExtractedApp:[self.temporaryDirectory withArchiveExtractedFromFile:filePath] makeDebuggable:makeDebuggable];
|
||||
return [self installExtractedApp:[self.temporaryDirectory withArchiveExtractedFromFile:filePath overrideModificationTime:overrideModificationTime] makeDebuggable:makeDebuggable];
|
||||
}
|
||||
}
|
||||
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_app_stream:(FBProcessInput *)input compression:(FBCompressionFormat)compression make_debuggable:(BOOL)makeDebuggable
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_app_stream:(FBProcessInput *)input compression:(FBCompressionFormat)compression make_debuggable:(BOOL)makeDebuggable override_modification_time:(BOOL)overrideModificationTime
|
||||
{
|
||||
return [self installExtractedApp:[self.temporaryDirectory withArchiveExtractedFromStream:input compression:compression] makeDebuggable:makeDebuggable];
|
||||
return [self installExtractedApp:[self.temporaryDirectory withArchiveExtractedFromStream:input compression:compression overrideModificationTime:overrideModificationTime] makeDebuggable:makeDebuggable];
|
||||
}
|
||||
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_xctest_app_file_path:(NSString *)filePath
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_xctest_app_file_path:(NSString *)filePath skipSigningBundles:(BOOL)skipSigningBundles
|
||||
{
|
||||
return [self installXctestFilePath:[FBFutureContext futureContextWithFuture:[FBFuture futureWithResult:[NSURL fileURLWithPath:filePath]]]];
|
||||
return [self installXctestFilePath:[FBFutureContext futureContextWithFuture:[FBFuture futureWithResult:[NSURL fileURLWithPath:filePath]]] skipSigningBundles:skipSigningBundles];
|
||||
}
|
||||
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_xctest_app_stream:(FBProcessInput *)stream
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_xctest_app_stream:(FBProcessInput *)stream skipSigningBundles:(BOOL)skipSigningBundles
|
||||
{
|
||||
return [self installXctest:[self.temporaryDirectory withArchiveExtractedFromStream:stream compression:FBCompressionFormatGZIP]];
|
||||
return [self installXctest:[self.temporaryDirectory withArchiveExtractedFromStream:stream compression:FBCompressionFormatGZIP] skipSigningBundles:skipSigningBundles];
|
||||
}
|
||||
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_dylib_file_path:(NSString *)filePath
|
||||
|
@ -114,14 +121,14 @@
|
|||
return [self installBundle:[self.temporaryDirectory withArchiveExtractedFromStream:input compression:FBCompressionFormatGZIP] intoStorage:self.storageManager.framework];
|
||||
}
|
||||
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_dsym_file_path:(NSString *)filePath linkToApp:(nullable NSString *)bundleID
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_dsym_file_path:(NSString *)filePath linkTo:(nullable FBDsymInstallLinkToBundle *)linkTo
|
||||
{
|
||||
return [self installAndLinkDsym:[FBFutureContext futureContextWithFuture:[FBFuture futureWithResult:[NSURL fileURLWithPath:filePath]]] intoStorage:self.storageManager.dsym linkToApp:bundleID];
|
||||
return [self installAndLinkDsym:[FBFutureContext futureContextWithFuture:[FBFuture futureWithResult:[NSURL fileURLWithPath:filePath]]] intoStorage:self.storageManager.dsym linkTo:linkTo];
|
||||
}
|
||||
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_dsym_stream:(FBProcessInput *)input linkToApp:(nullable NSString *)bundleID
|
||||
- (FBFuture<FBInstalledArtifact *> *)install_dsym_stream:(FBProcessInput *)input compression:(FBCompressionFormat)compression linkTo:(nullable FBDsymInstallLinkToBundle *)linkTo
|
||||
{
|
||||
return [self installAndLinkDsym:[self dsymDirnameFromUnzipDir:[self.temporaryDirectory withArchiveExtractedFromStream:input compression:FBCompressionFormatGZIP]] intoStorage:self.storageManager.dsym linkToApp:bundleID];
|
||||
return [self installAndLinkDsym:[self dsymDirnameFromUnzipDir:[self.temporaryDirectory withArchiveExtractedFromStream:input compression:compression]] intoStorage:self.storageManager.dsym linkTo:linkTo];
|
||||
}
|
||||
|
||||
#pragma mark Public Methods
|
||||
|
@ -135,7 +142,7 @@
|
|||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSArray<NSDictionary<NSString *, id> *> *> *)accessibility_info_at_point:(nullable NSValue *)value nestedFormat:(BOOL)nestedFormat
|
||||
- (FBFuture<id> *)accessibility_info_at_point:(nullable NSValue *)value nestedFormat:(BOOL)nestedFormat
|
||||
{
|
||||
return [[self
|
||||
accessibilityCommands]
|
||||
|
@ -175,7 +182,7 @@
|
|||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSNull *> *)approve:(NSSet<FBSettingsApprovalService> *)services for_application:(NSString *)bundleID
|
||||
- (FBFuture<NSNull *> *)approve:(NSSet<FBTargetSettingsService> *)services for_application:(NSString *)bundleID
|
||||
{
|
||||
return [self.settingsCommands
|
||||
onQueue:self.target.workQueue fmap:^FBFuture *(id<FBSimulatorSettingsCommands> commands) {
|
||||
|
@ -183,6 +190,14 @@
|
|||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSNull *> *)revoke:(NSSet<FBTargetSettingsService> *)services for_application:(NSString *)bundleID
|
||||
{
|
||||
return [self.settingsCommands
|
||||
onQueue:self.target.workQueue fmap:^FBFuture *(id<FBSimulatorSettingsCommands> commands) {
|
||||
return [commands revokeAccess:[NSSet setWithObject:bundleID] toServices:services];
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSNull *> *)approve_deeplink:(NSString *)scheme for_application:(NSString *)bundleID
|
||||
{
|
||||
return [self.settingsCommands
|
||||
|
@ -191,6 +206,14 @@
|
|||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSNull *> *)revoke_deeplink:(NSString *)scheme for_application:(NSString *)bundleID
|
||||
{
|
||||
return [self.settingsCommands
|
||||
onQueue:self.target.workQueue fmap:^FBFuture *(id<FBSimulatorSettingsCommands> commands) {
|
||||
return [commands revokeAccess:[NSSet setWithObject:bundleID] toDeeplink:scheme];
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSNull *> *)open_url:(NSString *)url
|
||||
{
|
||||
return [self.lifecycleCommands
|
||||
|
@ -253,11 +276,11 @@
|
|||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSSet<id<FBXCTestDescriptor>> *> *)list_test_bundles
|
||||
- (FBFuture<NSArray<id<FBXCTestDescriptor>> *> *)list_test_bundles
|
||||
{
|
||||
return [FBFuture onQueue:self.target.workQueue resolve:^{
|
||||
NSError *error;
|
||||
NSSet<id<FBXCTestDescriptor>> *testDescriptors = [self.storageManager.xctest listTestDescriptorsWithError:&error];
|
||||
NSArray<id<FBXCTestDescriptor>> *testDescriptors = [self.storageManager.xctest listTestDescriptorsWithError:&error];
|
||||
if (testDescriptors == nil) {
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
|
@ -265,7 +288,8 @@
|
|||
}];
|
||||
}
|
||||
|
||||
static const NSTimeInterval ListTestBundleTimeout = 60.0;
|
||||
// Some Mac tests are big that dlopen might take long
|
||||
static const NSTimeInterval ListTestBundleTimeout = 180.0;
|
||||
|
||||
- (FBFuture<NSArray<NSString *> *> *)list_tests_in_bundle:(NSString *)bundleID with_app:(NSString *)appPath
|
||||
{
|
||||
|
@ -302,7 +326,7 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
|
|||
|
||||
- (FBFuture<NSNull *> *)kill_application:(NSString *)bundleID
|
||||
{
|
||||
return [self.target killApplicationWithBundleID:bundleID];
|
||||
return [[self.target killApplicationWithBundleID:bundleID] fallback:NSNull.null];
|
||||
}
|
||||
|
||||
- (FBFuture<id<FBLaunchedApplication>> *)launch_app:(FBApplicationLaunchConfiguration *)configuration
|
||||
|
@ -354,10 +378,13 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
|
|||
failFuture];
|
||||
}
|
||||
|
||||
return [[self
|
||||
return [[[self
|
||||
debugserver_prepare:bundleID]
|
||||
onQueue:self.target.workQueue fmap:^(FBBundleDescriptor *application) {
|
||||
return [commands launchDebugServerForHostApplication:application port:self.ports.debugserverPort];
|
||||
return [commands launchDebugServerForHostApplication:application port:self.debugserverPort];
|
||||
}]
|
||||
onQueue:self.target.workQueue doOnResolved:^(id<FBDebugServer> debugServer) {
|
||||
self.debugServer = debugServer;
|
||||
}];
|
||||
}
|
||||
|
||||
|
@ -482,12 +509,12 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
|
|||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSNull *> *)set_preference:(NSString *)name value:(NSString *)value domain:(nullable NSString *)domain
|
||||
- (FBFuture<NSNull *> *)set_preference:(NSString *)name value:(NSString *)value type:(nullable NSString *)type domain:(nullable NSString *)domain
|
||||
{
|
||||
return [[self
|
||||
settingsCommands]
|
||||
onQueue:self.target.workQueue fmap:^(id<FBSimulatorSettingsCommands> commands) {
|
||||
return [commands setPreference:name value:value domain:domain];
|
||||
return [commands setPreference:name value:value type:type domain:domain];
|
||||
}];
|
||||
}
|
||||
|
||||
|
@ -505,7 +532,7 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
|
|||
return [[self
|
||||
settingsCommands]
|
||||
onQueue:self.target.workQueue fmap:^(id<FBSimulatorSettingsCommands> commands) {
|
||||
return [commands setPreference:@"AppleLocale" value:identifier domain:nil];
|
||||
return [commands setPreference:@"AppleLocale" value:identifier type:nil domain:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
|
@ -561,7 +588,7 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
|
|||
onQueue:self.target.workQueue pop:^FBFuture *(id<FBFileContainer> container) {
|
||||
NSMutableArray<FBFuture<NSNull *> *> *futures = NSMutableArray.array;
|
||||
for (NSURL *originPath in paths) {
|
||||
[futures addObject:[container copyFromHost:originPath toContainer:destinationPath]];
|
||||
[futures addObject:[container copyFromHost:originPath.path toContainer:destinationPath]];
|
||||
}
|
||||
return [[FBFuture futureWithFutures:futures] mapReplace:NSNull.null];
|
||||
}];
|
||||
|
@ -661,7 +688,7 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
|
|||
describeFormat:@"Target doesn't conform to FBDapServerCommand protocol %@", commands]
|
||||
failFuture];
|
||||
}
|
||||
|
||||
|
||||
return [commands launchDapServer:dapPath stdIn:stdIn stdOut:stdOut];
|
||||
}
|
||||
|
||||
|
@ -706,9 +733,24 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
|
|||
if ([containerType isEqualToString:FBFileContainerKindDiskImages]) {
|
||||
return [commands fileCommandsForDiskImages];
|
||||
}
|
||||
if ([containerType isEqualToString:FBFileContainerKindSymbols]) {
|
||||
return [commands fileCommandsForSymbols];
|
||||
}
|
||||
if ([containerType isEqualToString:FBFileContainerKindAuxillary]) {
|
||||
return [commands fileCommandsForAuxillary];
|
||||
}
|
||||
if ([containerType isEqualToString:FBFileContainerKindXctest]) {
|
||||
return [FBFutureContext futureContextWithResult:self.storageManager.xctest.asFileContainer];
|
||||
}
|
||||
if ([containerType isEqualToString:FBFileContainerKindDylib]) {
|
||||
return [FBFutureContext futureContextWithResult:self.storageManager.dylib.asFileContainer];
|
||||
}
|
||||
if ([containerType isEqualToString:FBFileContainerKindDsym]) {
|
||||
return [FBFutureContext futureContextWithResult:self.storageManager.dsym.asFileContainer];
|
||||
}
|
||||
if ([containerType isEqualToString:FBFileContainerKindFramework]) {
|
||||
return [FBFutureContext futureContextWithResult:self.storageManager.framework.asFileContainer];
|
||||
}
|
||||
if (containerType == nil || containerType.length == 0) {
|
||||
// The Default for no, or null container for back-compat.
|
||||
return [self.target isKindOfClass:FBDevice.class] ? [commands fileCommandsForMediaDirectory] : [commands fileCommandsForRootFilesystem];
|
||||
|
@ -841,19 +883,19 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
|
|||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<FBInstalledArtifact *> *)installXctest:(FBFutureContext<NSURL *> *)extractedXctest
|
||||
- (FBFuture<FBInstalledArtifact *> *)installXctest:(FBFutureContext<NSURL *> *)extractedXctest skipSigningBundles:(BOOL)skipSigningBundles
|
||||
{
|
||||
return [extractedXctest
|
||||
onQueue:self.target.workQueue pop:^(NSURL *extractionDirectory) {
|
||||
return [self.storageManager.xctest saveBundleOrTestRunFromBaseDirectory:extractionDirectory];
|
||||
return [self.storageManager.xctest saveBundleOrTestRunFromBaseDirectory:extractionDirectory skipSigningBundles:skipSigningBundles];
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<FBInstalledArtifact *> *)installXctestFilePath:(FBFutureContext<NSURL *> *)bundle
|
||||
- (FBFuture<FBInstalledArtifact *> *)installXctestFilePath:(FBFutureContext<NSURL *> *)bundle skipSigningBundles:(BOOL)skipSigningBundles
|
||||
{
|
||||
return [bundle
|
||||
onQueue:self.target.workQueue pop:^(NSURL *xctestURL) {
|
||||
return [self.storageManager.xctest saveBundleOrTestRun:xctestURL];
|
||||
return [self.storageManager.xctest saveBundleOrTestRun:xctestURL skipSigningBundles:skipSigningBundles];
|
||||
}];
|
||||
}
|
||||
|
||||
|
@ -880,18 +922,19 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
|
|||
if (!subDirs) {
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
// TODO: support cases when more than one dSYM is included in the archive
|
||||
if ([subDirs count] != 1) {
|
||||
return [FBFuture futureWithError:[FBControlCoreError errorForDescription:[NSString stringWithFormat:@"Expected only one dSYM directory: found: %tu", [subDirs count]]]];
|
||||
// if more than one dSYM is found
|
||||
// then we treat the parent dir as the dSYM directory
|
||||
return [FBFuture futureWithResult:parentDir];
|
||||
}
|
||||
return [FBFuture futureWithResult:subDirs[0]];
|
||||
}];
|
||||
}
|
||||
|
||||
// Will install the dsym under standard dsym location
|
||||
// if linkToApp app is passed:
|
||||
// after installation it will create a symlink in the app bundle
|
||||
- (FBFuture<FBInstalledArtifact *> *)installAndLinkDsym:(FBFutureContext<NSURL *> *)extractedFileContext intoStorage:(FBFileStorage *)storage linkToApp:(nullable NSString *)bundleID
|
||||
// if linkTo is passed:
|
||||
// after installation it will create a symlink in the bundle container
|
||||
- (FBFuture<FBInstalledArtifact *> *)installAndLinkDsym:(FBFutureContext<NSURL *> *)extractedFileContext intoStorage:(FBFileStorage *)storage linkTo:(nullable FBDsymInstallLinkToBundle *)linkTo
|
||||
{
|
||||
return [extractedFileContext
|
||||
onQueue:self.target.workQueue pop:^(NSURL *extractionDir) {
|
||||
|
@ -900,26 +943,38 @@ static const NSTimeInterval ListTestBundleTimeout = 60.0;
|
|||
if (!artifact) {
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
if (!bundleID) {
|
||||
|
||||
if (!linkTo) {
|
||||
return [FBFuture futureWithResult:artifact];
|
||||
}
|
||||
|
||||
return [[self.target installedApplicationWithBundleID:bundleID]
|
||||
onQueue:self.target.workQueue fmap:^(FBInstalledApplication *linkToApp) {
|
||||
|
||||
FBFuture<NSURL *> *future = nil;
|
||||
if (linkTo.bundle_type == FBDsymBundleTypeApp) {
|
||||
future = [[self.target installedApplicationWithBundleID:linkTo.bundle_id] onQueue:self.target.workQueue fmap:^(FBInstalledApplication *linkToApp) {
|
||||
[self.logger logFormat:@"Going to create a symlink for app bundle: %@", linkToApp.bundle.name];
|
||||
NSURL *appPath = [[NSURL fileURLWithPath:linkToApp.bundle.path] URLByDeletingLastPathComponent];
|
||||
NSURL *appDsymURL = [appPath URLByAppendingPathComponent:artifact.path.lastPathComponent];
|
||||
// delete a simlink if already exists
|
||||
// TODO: check if what we are deleting is a symlink
|
||||
[NSFileManager.defaultManager removeItemAtURL:appDsymURL error:nil];
|
||||
[self.logger logFormat:@"Deleted a symlink for dsym if it already exists: %@", appDsymURL];
|
||||
NSError *createLinkError = nil;
|
||||
if (![NSFileManager.defaultManager createSymbolicLinkAtURL:appDsymURL withDestinationURL:artifact.path error:&createLinkError]){
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
[self.logger logFormat:@"Created a symlink for dsym from: %@ to %@", appDsymURL, artifact.path];
|
||||
return [FBFuture futureWithResult:artifact];
|
||||
}];
|
||||
return [FBFuture futureWithResult:[NSURL fileURLWithPath:linkToApp.bundle.path]];
|
||||
}];
|
||||
} else {
|
||||
id<FBXCTestDescriptor> testDescriptor = [self.storageManager.xctest testDescriptorWithID:linkTo.bundle_id error:&error];
|
||||
[self.logger logFormat:@"Going to create a symlink for test bundle: %@", testDescriptor.name];
|
||||
future = [FBFuture futureWithResult:testDescriptor.url];
|
||||
}
|
||||
|
||||
return [future onQueue:self.target.workQueue fmap:^(NSURL *bundlePath) {
|
||||
NSURL *bundleUrl = [bundlePath URLByDeletingLastPathComponent];
|
||||
NSURL *dsymURL = [bundleUrl URLByAppendingPathComponent:artifact.path.lastPathComponent];
|
||||
// delete a simlink if already exists
|
||||
// TODO: check if what we are deleting is a symlink
|
||||
[NSFileManager.defaultManager removeItemAtURL:dsymURL error:nil];
|
||||
[self.logger logFormat:@"Deleted a symlink for dsym if it already exists: %@", dsymURL];
|
||||
NSError *createLinkError = nil;
|
||||
if (![NSFileManager.defaultManager createSymbolicLinkAtURL:dsymURL withDestinationURL:artifact.path error:&createLinkError]){
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
[self.logger logFormat:@"Created a symlink for dsym from: %@ to %@", dsymURL, artifact.path];
|
||||
return [FBFuture futureWithResult:artifact];
|
||||
}];
|
||||
|
||||
}];
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -27,7 +27,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
*/
|
||||
@property (nonatomic, assign, readonly) FBCodeCoverageFormat format;
|
||||
|
||||
- (instancetype)initWithCollect:(BOOL)collect format:(FBCodeCoverageFormat)format;
|
||||
/**
|
||||
Determines whether should enable continuous coverage collection
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) BOOL shouldEnableContinuousCoverageCollection;
|
||||
|
||||
- (instancetype)initWithCollect:(BOOL)collect format:(FBCodeCoverageFormat)format enableContinuousCoverageCollection:(BOOL)enableContinuousCoverageCollection;
|
||||
|
||||
@end
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -9,16 +9,16 @@
|
|||
|
||||
@implementation FBCodeCoverageRequest
|
||||
|
||||
- (instancetype)initWithCollect:(BOOL)collect format:(FBCodeCoverageFormat)format
|
||||
- (instancetype)initWithCollect:(BOOL)collect format:(FBCodeCoverageFormat)format enableContinuousCoverageCollection:(BOOL)enableContinuousCoverageCollection
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
_collect = collect;
|
||||
_format = format;
|
||||
|
||||
_shouldEnableContinuousCoverageCollection = enableContinuousCoverageCollection;
|
||||
return self;
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <FBControlCore/FBControlCore.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, FBDsymBundleType) {
|
||||
FBDsymBundleTypeXCTest,
|
||||
FBDsymBundleTypeApp,
|
||||
};
|
||||
|
||||
/**
|
||||
Describes bundle needs to be linked with Dsym
|
||||
*/
|
||||
@interface FBDsymInstallLinkToBundle : NSObject
|
||||
|
||||
/**
|
||||
ID of the bundle the dsym needs to link
|
||||
*/
|
||||
@property (nonatomic, copy, readonly) NSString *bundle_id;
|
||||
|
||||
/**
|
||||
Type of bundle
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) FBDsymBundleType bundle_type;
|
||||
|
||||
- (instancetype)initWith:(NSString *)bundle_id bundle_type:(FBDsymBundleType)bundle_type;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "FBDsymInstallLinkToBundle.h"
|
||||
|
||||
@implementation FBDsymInstallLinkToBundle
|
||||
|
||||
- (instancetype)initWith:(NSString *)bundle_id bundle_type:(FBDsymBundleType)bundle_type
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_bundle_id = bundle_id;
|
||||
_bundle_type = bundle_type;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -37,13 +37,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@param waitForDebugger a boolean describing whether the tests should stop after Run and wait for a debugger to be attached.
|
||||
@return an FBXCTestRunRequest instance.
|
||||
*/
|
||||
+ (instancetype)logicTestWithTestBundleID:(NSString *)testBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger;
|
||||
+ (instancetype)logicTestWithTestBundleID:(NSString *)testBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(nullable NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger collectResultBundle:(BOOL)collectResultBundle;
|
||||
|
||||
/**
|
||||
The Initializer for App Tests.
|
||||
|
||||
@param testBundleID the bundle id of the test to run.
|
||||
@param appBundleID the bundle id of the application to inject the test bundle into.
|
||||
@param testHostAppBundleID the bundle id of the application to inject the test bundle into.
|
||||
@param environment environment for the application test process.
|
||||
@param arguments arguments for the application test process.
|
||||
@param testsToRun the tests to run.
|
||||
|
@ -53,13 +53,12 @@ The Initializer for App Tests.
|
|||
@param coverageRequest information about llvm code coverage collection
|
||||
@return an FBXCTestRunRequest instance.
|
||||
*/
|
||||
+ (instancetype)applicationTestWithTestBundleID:(NSString *)testBundleID appBundleID:(NSString *)appBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger;
|
||||
+ (instancetype)applicationTestWithTestBundleID:(NSString *)testBundleID testHostAppBundleID:(NSString *)testHostAppBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(nullable NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger collectResultBundle:(BOOL)collectResultBundle;
|
||||
|
||||
/**
|
||||
The Initializer for UI Tests.
|
||||
|
||||
@param testBundleID the bundle id of the test to run.
|
||||
@param appBundleID the bundle id of the application to automatie.
|
||||
@param testHostAppBundleID the bundle id of the application hosting the test bundle.
|
||||
@param environment environment for the logic test process.
|
||||
@param arguments arguments for the logic test process.
|
||||
|
@ -70,7 +69,7 @@ The Initializer for UI Tests.
|
|||
@param coverageRequest information about llvm code coverage collection
|
||||
@return an FBXCTestRunRequest instance.
|
||||
*/
|
||||
+ (instancetype)uiTestWithTestBundleID:(NSString *)testBundleID appBundleID:(NSString *)appBundleID testHostAppBundleID:(NSString *)testHostAppBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs;
|
||||
+ (instancetype)uiTestWithTestBundleID:(NSString *)testBundleID testHostAppBundleID:(NSString *)testHostAppBundleID testTargetAppBundleID:(NSString *)testTargetAppBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(nullable NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs collectResultBundle:(BOOL)collectResultBundle;
|
||||
|
||||
#pragma mark Properties
|
||||
|
||||
|
@ -89,16 +88,16 @@ The Initializer for UI Tests.
|
|||
*/
|
||||
@property (nonatomic, copy, readonly) NSString *testBundleID;
|
||||
|
||||
/**
|
||||
The Bundle ID of the Application to test in, if relevant.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable, readonly) NSString *appBundleID;
|
||||
|
||||
/**
|
||||
The Bundle ID of the Test Host, if relevant.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable, readonly) NSString *testHostAppBundleID;
|
||||
|
||||
/**
|
||||
The Bundle ID of the Test Target (a.k.a. App Under Test), if relevant.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable, readonly) NSString *testTargetAppBundleID;
|
||||
|
||||
/**
|
||||
The environment variables for the application, if relevant
|
||||
*/
|
||||
|
@ -149,6 +148,11 @@ The Initializer for UI Tests.
|
|||
*/
|
||||
@property (nonatomic, assign, readonly) BOOL waitForDebugger;
|
||||
|
||||
/**
|
||||
If set tests' result bundle will be collected
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) BOOL collectResultBundle;
|
||||
|
||||
/**
|
||||
Starts the test operation.
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -49,7 +49,10 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
|
|||
NSURL *dir = [temporaryDirectory ephemeralTemporaryDirectory];
|
||||
NSString *coverageDirName =[NSString stringWithFormat:@"coverage_%@", NSUUID.UUID.UUIDString];
|
||||
NSString *coverageDirPath = [dir.path stringByAppendingPathComponent:coverageDirName];
|
||||
coverageConfig = [[FBCodeCoverageConfiguration alloc] initWithDirectory:coverageDirPath format:self.coverageRequest.format];
|
||||
if (![NSFileManager.defaultManager createDirectoryAtPath:coverageDirPath withIntermediateDirectories:YES attributes:nil error:&error]) {
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
coverageConfig = [[FBCodeCoverageConfiguration alloc] initWithDirectory:coverageDirPath format:self.coverageRequest.format enableContinuousCoverageCollection:self.coverageRequest.shouldEnableContinuousCoverageCollection];
|
||||
}
|
||||
|
||||
NSString *testFilter = nil;
|
||||
|
@ -78,7 +81,8 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
|
|||
mirroring:FBLogicTestMirrorFileLogs
|
||||
coverageConfiguration:coverageConfig
|
||||
binaryPath:testDescriptor.testBundle.binary.path
|
||||
logDirectoryPath:logDirectoryPath];
|
||||
logDirectoryPath:logDirectoryPath
|
||||
architectures:testDescriptor.architectures];
|
||||
|
||||
return [self startTestExecution:configuration target:target reporter:reporter logger:logger];
|
||||
}
|
||||
|
@ -96,7 +100,8 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
|
|||
coverageConfiguration:configuration.coverageConfiguration
|
||||
logDirectoryPath:configuration.logDirectoryPath
|
||||
binariesPaths:@[configuration.binaryPath]
|
||||
reportAttachments:self.reportAttachments];
|
||||
reportAttachments:self.reportAttachments
|
||||
reportResultBundle:self.collectResultBundle];
|
||||
FBIDBTestOperation *operation = [[FBIDBTestOperation alloc]
|
||||
initWithConfiguration:configuration
|
||||
reporterConfiguration:reporterConfiguration
|
||||
|
@ -135,11 +140,11 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
|
|||
}]
|
||||
onQueue:target.workQueue fmap:^ FBFuture<FBIDBTestOperation *> * (FBIDBAppHostedTestConfiguration *appHostedTestConfig) {
|
||||
[logger logFormat:@"Obtained app-hosted test configuration %@", appHostedTestConfig];
|
||||
return [FBXCTestRunRequest_AppTest startTestExecution:appHostedTestConfig reportAttachments:self.reportAttachments target:target reporter:reporter logger:logger];
|
||||
return [FBXCTestRunRequest_AppTest startTestExecution:appHostedTestConfig reportAttachments:self.reportAttachments target:target reporter:reporter logger:logger reportResultBundle:self.collectResultBundle];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (FBFuture<FBIDBTestOperation *> *)startTestExecution:(FBIDBAppHostedTestConfiguration *)configuration reportAttachments:(BOOL)reportAttachments target:(id<FBiOSTarget>)target reporter:(id<FBXCTestReporter>)reporter logger:(id<FBControlCoreLogger>)logger
|
||||
+ (FBFuture<FBIDBTestOperation *> *)startTestExecution:(FBIDBAppHostedTestConfiguration *)configuration reportAttachments:(BOOL)reportAttachments target:(id<FBiOSTarget>)target reporter:(id<FBXCTestReporter>)reporter logger:(id<FBControlCoreLogger>)logger reportResultBundle:(BOOL)reportResultBundle
|
||||
{
|
||||
FBTestLaunchConfiguration *testLaunchConfiguration = configuration.testLaunchConfiguration;
|
||||
FBCodeCoverageConfiguration *coverageConfiguration = configuration.coverageConfiguration;
|
||||
|
@ -164,7 +169,8 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
|
|||
coverageConfiguration:coverageConfiguration
|
||||
logDirectoryPath:testLaunchConfiguration.logDirectoryPath
|
||||
binariesPaths:binariesPaths
|
||||
reportAttachments:reportAttachments];
|
||||
reportAttachments:reportAttachments
|
||||
reportResultBundle:reportResultBundle];
|
||||
return [FBFuture futureWithResult:[[FBIDBTestOperation alloc]
|
||||
initWithConfiguration:testLaunchConfiguration
|
||||
reporterConfiguration:reporterConfiguration
|
||||
|
@ -203,33 +209,33 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
|
|||
@implementation FBXCTestRunRequest
|
||||
|
||||
@synthesize testBundleID = _testBundleID;
|
||||
@synthesize appBundleID = _appBundleID;
|
||||
@synthesize testHostAppBundleID = _testHostAppBundleID;
|
||||
@synthesize environment = _environment;
|
||||
@synthesize arguments = _arguments;
|
||||
@synthesize testsToRun = _testsToRun;
|
||||
@synthesize testsToSkip = _testsToSkip;
|
||||
@synthesize testTimeout = _testTimeout;
|
||||
@synthesize collectResultBundle = _collectResultBundle;
|
||||
|
||||
|
||||
#pragma mark Initializers
|
||||
|
||||
+ (instancetype)logicTestWithTestBundleID:(NSString *)testBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger
|
||||
+ (instancetype)logicTestWithTestBundleID:(NSString *)testBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger collectResultBundle:(BOOL)collectResultBundle
|
||||
{
|
||||
return [[FBXCTestRunRequest_LogicTest alloc] initWithTestBundleID:testBundleID appBundleID:nil testHostAppBundleID:nil environment:environment arguments:arguments testsToRun:testsToRun testsToSkip:testsToSkip testTimeout:testTimeout reportActivities:reportActivities reportAttachments:reportAttachments coverageRequest:coverageRequest collectLogs:collectLogs waitForDebugger:waitForDebugger];
|
||||
return [[FBXCTestRunRequest_LogicTest alloc] initWithTestBundleID:testBundleID testHostAppBundleID:nil testTargetAppBundleID:nil environment:environment arguments:arguments testsToRun:testsToRun testsToSkip:testsToSkip testTimeout:testTimeout reportActivities:reportActivities reportAttachments:reportAttachments coverageRequest:coverageRequest collectLogs:collectLogs waitForDebugger:waitForDebugger collectResultBundle:collectResultBundle];
|
||||
}
|
||||
|
||||
+ (instancetype)applicationTestWithTestBundleID:(NSString *)testBundleID appBundleID:(NSString *)appBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger
|
||||
+ (instancetype)applicationTestWithTestBundleID:(NSString *)testBundleID testHostAppBundleID:(NSString *)testHostAppBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger collectResultBundle:(BOOL)collectResultBundle
|
||||
{
|
||||
return [[FBXCTestRunRequest_AppTest alloc] initWithTestBundleID:testBundleID appBundleID:appBundleID testHostAppBundleID:nil environment:environment arguments:arguments testsToRun:testsToRun testsToSkip:testsToSkip testTimeout:testTimeout reportActivities:reportActivities reportAttachments:reportAttachments coverageRequest:coverageRequest collectLogs:collectLogs waitForDebugger:waitForDebugger];
|
||||
return [[FBXCTestRunRequest_AppTest alloc] initWithTestBundleID:testBundleID testHostAppBundleID:testHostAppBundleID testTargetAppBundleID:nil environment:environment arguments:arguments testsToRun:testsToRun testsToSkip:testsToSkip testTimeout:testTimeout reportActivities:reportActivities reportAttachments:reportAttachments coverageRequest:coverageRequest collectLogs:collectLogs waitForDebugger:waitForDebugger collectResultBundle:collectResultBundle];
|
||||
}
|
||||
|
||||
+ (instancetype)uiTestWithTestBundleID:(NSString *)testBundleID appBundleID:(NSString *)appBundleID testHostAppBundleID:(NSString *)testHostAppBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs
|
||||
+ (instancetype)uiTestWithTestBundleID:(NSString *)testBundleID testHostAppBundleID:(NSString *)testHostAppBundleID testTargetAppBundleID:(NSString *)testTargetAppBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs collectResultBundle:(BOOL)collectResultBundle
|
||||
{
|
||||
return [[FBXCTestRunRequest_UITest alloc] initWithTestBundleID:testBundleID appBundleID:appBundleID testHostAppBundleID:testHostAppBundleID environment:environment arguments:arguments testsToRun:testsToRun testsToSkip:testsToSkip testTimeout:testTimeout reportActivities:reportActivities reportAttachments:reportAttachments coverageRequest:coverageRequest collectLogs:collectLogs waitForDebugger:NO];
|
||||
return [[FBXCTestRunRequest_UITest alloc] initWithTestBundleID:testBundleID testHostAppBundleID:testHostAppBundleID testTargetAppBundleID:testTargetAppBundleID environment:environment arguments:arguments testsToRun:testsToRun testsToSkip:testsToSkip testTimeout:testTimeout reportActivities:reportActivities reportAttachments:reportAttachments coverageRequest:coverageRequest collectLogs:collectLogs waitForDebugger:NO collectResultBundle:collectResultBundle];
|
||||
}
|
||||
|
||||
- (instancetype)initWithTestBundleID:(NSString *)testBundleID appBundleID:(NSString *)appBundleID testHostAppBundleID:(NSString *)testHostAppBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger
|
||||
- (instancetype)initWithTestBundleID:(NSString *)testBundleID testHostAppBundleID:(NSString *)testHostAppBundleID testTargetAppBundleID:(NSString *)testTargetAppBundleID environment:(NSDictionary<NSString *, NSString *> *)environment arguments:(NSArray<NSString *> *)arguments testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip testTimeout:(NSNumber *)testTimeout reportActivities:(BOOL)reportActivities reportAttachments:(BOOL)reportAttachments coverageRequest:(FBCodeCoverageRequest *)coverageRequest collectLogs:(BOOL)collectLogs waitForDebugger:(BOOL)waitForDebugger collectResultBundle:(BOOL)collectResultBundle
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
|
@ -237,8 +243,8 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
|
|||
}
|
||||
|
||||
_testBundleID = testBundleID;
|
||||
_appBundleID = appBundleID;
|
||||
_testHostAppBundleID = testHostAppBundleID;
|
||||
_testTargetAppBundleID = testTargetAppBundleID;
|
||||
_environment = environment;
|
||||
_arguments = arguments;
|
||||
_testsToRun = testsToRun;
|
||||
|
@ -249,6 +255,7 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
|
|||
_coverageRequest = coverageRequest;
|
||||
_collectLogs = collectLogs;
|
||||
_waitForDebugger = waitForDebugger;
|
||||
_collectResultBundle = collectResultBundle;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
@ -299,7 +306,3 @@ static const NSTimeInterval FBLogicTestTimeout = 60 * 60; //Aprox. an hour.
|
|||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -90,7 +90,7 @@ static void RemoveGlobalLogger(id<FBControlCoreLogger> logger)
|
|||
+ (instancetype)loggerWithUserDefaults:(NSUserDefaults *)userDefaults
|
||||
{
|
||||
BOOL debugLogging = [[userDefaults stringForKey:@"-log-level"].lowercaseString isEqualToString:@"info"] ? NO : YES;
|
||||
id<FBControlCoreLogger> systemLogger = [FBControlCoreLogger systemLoggerWritingToStderr:YES withDebugLogging:debugLogging];
|
||||
id<FBControlCoreLogger> systemLogger = [FBControlCoreLoggerFactory systemLoggerWritingToStderr:YES withDebugLogging:debugLogging];
|
||||
NSMutableArray<id<FBControlCoreLogger>> *loggers = [NSMutableArray arrayWithObject:systemLogger];
|
||||
|
||||
NSError *error;
|
||||
|
@ -108,7 +108,7 @@ static void RemoveGlobalLogger(id<FBControlCoreLogger> logger)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
[loggers addObject:[FBControlCoreLogger loggerToFileDescriptor:fileDescriptor closeOnEndOfFile:YES]];
|
||||
[loggers addObject:[FBControlCoreLoggerFactory loggerToFileDescriptor:fileDescriptor closeOnEndOfFile:YES]];
|
||||
}
|
||||
FBIDBLogger *logger = [[[FBIDBLogger alloc] initWithLoggers:loggers] withDateFormatEnabled:YES];
|
||||
FBControlCoreGlobalConfiguration.defaultLogger = logger;
|
||||
|
@ -145,7 +145,7 @@ static void RemoveGlobalLogger(id<FBControlCoreLogger> logger)
|
|||
{
|
||||
dispatch_queue_t queue = FBIDBLogger.loggerQueue;
|
||||
return [FBFuture onQueue:queue resolveValue:^(NSError **_) {
|
||||
id<FBControlCoreLogger> logger = [FBControlCoreLogger loggerToConsumer:consumer];
|
||||
id<FBControlCoreLogger> logger = [FBControlCoreLoggerFactory loggerToConsumer:consumer];
|
||||
id<FBLogOperation> operation = [[FBIDBLogger_Operation alloc] initWithConsumer:consumer logger:logger queue:queue];
|
||||
AddGlobalLogger(logger);
|
||||
return operation;
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -7,7 +7,6 @@
|
|||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <FBControlCore/FBControlCore.h>
|
||||
#import "FBXCTestDescriptor.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
@ -17,6 +16,9 @@ extern NSString *const IdbDylibsFolder;
|
|||
extern NSString *const IdbDsymsFolder;
|
||||
extern NSString *const IdbFrameworksFolder;
|
||||
|
||||
@protocol FBControlCoreLogger;
|
||||
@protocol FBXCTestDescriptor;
|
||||
|
||||
/**
|
||||
A wrapper around an installed artifact
|
||||
*/
|
||||
|
@ -80,6 +82,13 @@ extern NSString *const IdbFrameworksFolder;
|
|||
*/
|
||||
@property (nonatomic, copy, readonly) NSDictionary<NSString *, NSString *> *replacementMapping;
|
||||
|
||||
/**
|
||||
Exposes the receiver as an FBFileContainer instance.
|
||||
|
||||
@return a FBFileContainer instance.
|
||||
*/
|
||||
- (id<FBFileContainer>)asFileContainer;
|
||||
|
||||
/**
|
||||
Cleans all storage
|
||||
|
||||
|
@ -177,24 +186,25 @@ extern NSString *const IdbFrameworksFolder;
|
|||
@param baseDirectory the directory containing the test bundle.
|
||||
@return the bundle id of the installed test, or nil if failed
|
||||
*/
|
||||
- (FBFuture<FBInstalledArtifact *> *)saveBundleOrTestRunFromBaseDirectory:(NSURL *)baseDirectory;
|
||||
- (FBFuture<FBInstalledArtifact *> *)saveBundleOrTestRunFromBaseDirectory:(NSURL *)baseDirectory skipSigningBundles:(BOOL)skipSigningBundles;
|
||||
|
||||
/**
|
||||
Stores a test bundle, based on the file path of the actual test bundle.
|
||||
This is useful when the test bundle is from an existing and local file path, instead of passed in an archive.
|
||||
|
||||
@param filePath the file path of the bundle.
|
||||
@param skipSigningBundles pass true to skip signing xctest bundles.
|
||||
@return the bundle id of the installed test, or nil if failed
|
||||
*/
|
||||
- (FBFuture<FBInstalledArtifact *> *)saveBundleOrTestRun:(NSURL *)filePath;
|
||||
- (FBFuture<FBInstalledArtifact *> *)saveBundleOrTestRun:(NSURL *)filePath skipSigningBundles:(BOOL)skipSigningBundles;
|
||||
|
||||
/**
|
||||
Get descriptors for all installed test bundles and xctestrun files.
|
||||
|
||||
@param error Set if getting this bundle failed
|
||||
@return Set of FBXCTestDescriptors of all installed test bundles and xctestrun files
|
||||
@return List of FBXCTestDescriptors of all installed test bundles and xctestrun files
|
||||
*/
|
||||
- (nullable NSSet<id<FBXCTestDescriptor>> *)listTestDescriptorsWithError:(NSError **)error;
|
||||
- (nullable NSArray<id<FBXCTestDescriptor>> *)listTestDescriptorsWithError:(NSError **)error;
|
||||
|
||||
/**
|
||||
Get test descriptor by bundle id.
|
||||
|
@ -260,7 +270,7 @@ extern NSString *const IdbFrameworksFolder;
|
|||
|
||||
/**
|
||||
Interpolate path replacements
|
||||
|
||||
|
||||
@return a dictionary with the replacements defined
|
||||
*/
|
||||
- (NSDictionary<NSString *, NSString *> *)replacementMapping;
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -54,6 +54,23 @@ NSString *const IdbFrameworksFolder = @"idb-frameworks";
|
|||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Methods
|
||||
|
||||
- (BOOL)clean:(NSError **)error
|
||||
{
|
||||
for (NSURL *url in [NSFileManager.defaultManager contentsOfDirectoryAtURL:self.basePath includingPropertiesForKeys:nil options:0 error:nil]) {
|
||||
if (![NSFileManager.defaultManager removeItemAtPath:url.path error:error]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (id<FBFileContainer>)asFileContainer
|
||||
{
|
||||
return [FBFileContainer fileContainerForBasePath:self.basePath.path];
|
||||
}
|
||||
|
||||
#pragma mark Properties
|
||||
|
||||
- (NSDictionary<NSString *, NSString *> *)replacementMapping
|
||||
|
@ -65,16 +82,6 @@ NSString *const IdbFrameworksFolder = @"idb-frameworks";
|
|||
return replacementMapping;
|
||||
}
|
||||
|
||||
-(BOOL)clean:(NSError **)error
|
||||
{
|
||||
for (NSURL *url in [NSFileManager.defaultManager contentsOfDirectoryAtURL:self.basePath includingPropertiesForKeys:nil options:0 error:nil]) {
|
||||
if (![NSFileManager.defaultManager removeItemAtPath:url.path error:error]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBFileStorage
|
||||
|
@ -129,23 +136,29 @@ NSString *const IdbFrameworksFolder = @"idb-frameworks";
|
|||
|
||||
- (BOOL)checkArchitecture:(FBBundleDescriptor *)bundle error:(NSError **)error
|
||||
{
|
||||
NSSet<NSString *> *bundleArchs = bundle.binary.architectures;
|
||||
NSString *targetArch = self.target.architecture;
|
||||
|
||||
const BOOL containsExactArch = [bundleArchs containsObject:targetArch];
|
||||
NSSet<FBArchitecture> *binaryArchitectures = bundle.binary.architectures;
|
||||
NSArray<FBArchitecture> *targetArchs = self.target.architectures;
|
||||
NSSet<FBArchitecture> *supportedArchitectures = [FBiOSTargetConfiguration baseArchsToCompatibleArch:targetArchs];
|
||||
|
||||
const BOOL containsExactArch = [binaryArchitectures intersectsSet:supportedArchitectures];
|
||||
// arm64 binaries are acceptable on arm64e devices, but arm64e is not yet available
|
||||
const BOOL arm64eEquivalent = [targetArch isEqualToString:@"arm64e"] && [bundleArchs containsObject:@"arm64"];
|
||||
|
||||
const BOOL arm64eEquivalent = [targetArchs containsObject:@"arm64e"] && [binaryArchitectures containsObject:@"arm64"];
|
||||
|
||||
if (!(containsExactArch || arm64eEquivalent)) {
|
||||
return [[FBIDBError
|
||||
describeFormat:@"Targets architecture %@ not in the bundles supported architectures: %@", targetArch, bundleArchs.allObjects]
|
||||
failBool:error];
|
||||
describeFormat:@"The supported architectures of the target %@ do not intersect with any architectures in the bundle: %@", [FBCollectionInformation oneLineDescriptionFromArray:supportedArchitectures.allObjects], [FBCollectionInformation oneLineDescriptionFromArray:binaryArchitectures.allObjects]]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (FBFuture<FBInstalledArtifact *> *)saveBundle:(FBBundleDescriptor *)bundle
|
||||
{
|
||||
return [self saveBundle:bundle skipSigningBundles:NO];
|
||||
}
|
||||
|
||||
- (FBFuture<FBInstalledArtifact *> *)saveBundle:(FBBundleDescriptor *)bundle skipSigningBundles:(BOOL)skipSigningBundles
|
||||
{
|
||||
// Check that the bundle matches the architecture of the target.
|
||||
NSError *error = nil;
|
||||
|
@ -162,14 +175,13 @@ NSString *const IdbFrameworksFolder = @"idb-frameworks";
|
|||
// Copy over bundle
|
||||
NSURL *sourceBundlePath = [NSURL fileURLWithPath:bundle.path];
|
||||
NSURL *destinationBundlePath = [storageDirectory URLByAppendingPathComponent:sourceBundlePath.lastPathComponent];
|
||||
[self.logger logFormat:@"Persisting %@ to %@", bundle.identifier, destinationBundlePath];
|
||||
if (![NSFileManager.defaultManager copyItemAtURL:sourceBundlePath toURL:destinationBundlePath error:&error]) {
|
||||
[self.logger logFormat:@"Symlink %@ to %@", bundle.identifier, destinationBundlePath];
|
||||
if (![NSFileManager.defaultManager createSymbolicLinkAtURL:destinationBundlePath withDestinationURL:sourceBundlePath error:&error]) {
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
[self.logger logFormat:@"Persisted %@", bundle.identifier];
|
||||
|
||||
FBInstalledArtifact *artifact = [[FBInstalledArtifact alloc] initWithName:bundle.identifier uuid:bundle.binary.uuid path:destinationBundlePath];
|
||||
if (!self.relocateLibraries || ![self.target requiresBundlesToBeSigned]) {
|
||||
if (!self.relocateLibraries || ![self.target requiresBundlesToBeSigned] || skipSigningBundles) {
|
||||
return [FBFuture futureWithResult:artifact];
|
||||
}
|
||||
bundle = [FBBundleDescriptor bundleFromPath:destinationBundlePath.path error:&error];
|
||||
|
@ -250,7 +262,7 @@ static NSString *const XctestRunExtension = @"xctestrun";
|
|||
|
||||
#pragma mark Public
|
||||
|
||||
- (FBFuture<FBInstalledArtifact *> *)saveBundleOrTestRunFromBaseDirectory:(NSURL *)baseDirectory
|
||||
- (FBFuture<FBInstalledArtifact *> *)saveBundleOrTestRunFromBaseDirectory:(NSURL *)baseDirectory skipSigningBundles:(BOOL)skipSigningBundles
|
||||
{
|
||||
// Find .xctest or .xctestrun in directory.
|
||||
NSError *error = nil;
|
||||
|
@ -279,7 +291,7 @@ static NSString *const XctestRunExtension = @"xctestrun";
|
|||
}
|
||||
|
||||
if (xctestBundleURL) {
|
||||
return [self saveTestBundle:xctestBundleURL];
|
||||
return [self saveTestBundle:xctestBundleURL skipSigningBundles:skipSigningBundles];
|
||||
}
|
||||
if (xctestrunURL) {
|
||||
return [self saveTestRun:xctestrunURL];
|
||||
|
@ -289,11 +301,11 @@ static NSString *const XctestRunExtension = @"xctestrun";
|
|||
failFuture];
|
||||
}
|
||||
|
||||
- (FBFuture<FBInstalledArtifact *> *)saveBundleOrTestRun:(NSURL *)filePath
|
||||
- (FBFuture<FBInstalledArtifact *> *)saveBundleOrTestRun:(NSURL *)filePath skipSigningBundles:(BOOL)skipSigningBundles
|
||||
{
|
||||
// save .xctest or .xctestrun
|
||||
if ([filePath.pathExtension isEqualToString:XctestExtension]) {
|
||||
return [self saveTestBundle:filePath];
|
||||
return [self saveTestBundle:filePath skipSigningBundles:skipSigningBundles];
|
||||
}
|
||||
if ([filePath.pathExtension isEqualToString:XctestRunExtension]) {
|
||||
return [self saveTestRun:filePath];
|
||||
|
@ -303,9 +315,9 @@ static NSString *const XctestRunExtension = @"xctestrun";
|
|||
failFuture];
|
||||
}
|
||||
|
||||
- (NSSet<id<FBXCTestDescriptor>> *)listTestDescriptorsWithError:(NSError **)error
|
||||
- (NSArray<id<FBXCTestDescriptor>> *)listTestDescriptorsWithError:(NSError **)error
|
||||
{
|
||||
NSMutableSet<id<FBXCTestDescriptor>> *testDescriptors = [[NSMutableSet alloc] init];
|
||||
NSMutableArray<id<FBXCTestDescriptor>> *testDescriptors = [[NSMutableArray alloc] init];
|
||||
|
||||
// Get xctest bundles
|
||||
NSSet<NSURL *> *testURLS = [self listTestBundlesWithError:error];
|
||||
|
@ -346,12 +358,12 @@ static NSString *const XctestRunExtension = @"xctestrun";
|
|||
[testDescriptors addObjectsFromArray:descriptors];
|
||||
}
|
||||
|
||||
return testDescriptors;
|
||||
return [NSArray arrayWithArray:testDescriptors];
|
||||
}
|
||||
|
||||
- (id<FBXCTestDescriptor>)testDescriptorWithID:(NSString *)bundleId error:(NSError **)error
|
||||
{
|
||||
NSSet<id<FBXCTestDescriptor>> *testDescriptors = [self listTestDescriptorsWithError:error];
|
||||
NSArray<id<FBXCTestDescriptor>> *testDescriptors = [self listTestDescriptorsWithError:error];
|
||||
for (id<FBXCTestDescriptor> testDescriptor in testDescriptors) {
|
||||
if ([[testDescriptor testBundleID] isEqualToString: bundleId]) {
|
||||
return testDescriptor;
|
||||
|
@ -403,7 +415,7 @@ static NSString *const XctestRunExtension = @"xctestrun";
|
|||
|
||||
- (id<FBXCTestDescriptor>)testDescriptorWithURL:(NSURL *)url error:(NSError **)error
|
||||
{
|
||||
NSSet<id<FBXCTestDescriptor>> *testDescriptors = [self listTestDescriptorsWithError:error];
|
||||
NSArray<id<FBXCTestDescriptor>> *testDescriptors = [self listTestDescriptorsWithError:error];
|
||||
for (id<FBXCTestDescriptor> testDescriptor in testDescriptors) {
|
||||
if ([[[testDescriptor url] absoluteString] isEqualToString:[url absoluteString]]) {
|
||||
return testDescriptor;
|
||||
|
@ -497,7 +509,7 @@ static NSString *const XctestRunExtension = @"xctestrun";
|
|||
return [[FBXCodebuildTestRunDescriptor alloc] initWithURL:xctestrunURL name:testTarget testBundle:testBundle testHostBundle:testHostBundle];
|
||||
}
|
||||
|
||||
- (FBFuture<FBInstalledArtifact *> *)saveTestBundle:(NSURL *)testBundleURL
|
||||
- (FBFuture<FBInstalledArtifact *> *)saveTestBundle:(NSURL *)testBundleURL skipSigningBundles:(BOOL)skipSigningBundles
|
||||
{
|
||||
// Test Bundles don't always have a bundle id, so fallback to another name if it's not there.
|
||||
NSError *error = nil;
|
||||
|
@ -505,7 +517,7 @@ static NSString *const XctestRunExtension = @"xctestrun";
|
|||
if (!bundle) {
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
return [self saveBundle:bundle];
|
||||
return [self saveBundle:bundle skipSigningBundles:skipSigningBundles];
|
||||
}
|
||||
|
||||
- (FBFuture<FBInstalledArtifact *> *)saveTestRun:(NSURL *)XCTestRunURL
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -26,9 +26,8 @@ static FBFuture<FBApplicationLaunchConfiguration *> *BuildAppLaunchConfig(NSStri
|
|||
|
||||
if (processLogDirectory) {
|
||||
FBXCTestLogger *mirrorLogger = [FBXCTestLogger defaultLoggerInDirectory:processLogDirectory];
|
||||
NSUUID *udid = NSUUID.UUID;
|
||||
stdOutFuture = [mirrorLogger logConsumptionToFile:stdOutConsumer outputKind:@"out" udid:udid logger:logger];
|
||||
stdErrFuture = [mirrorLogger logConsumptionToFile:stdErrConsumer outputKind:@"err" udid:udid logger:logger];
|
||||
stdOutFuture = [mirrorLogger logConsumptionOf:stdOutConsumer toFileNamed:@"test_process_stdout.out" logger:logger];
|
||||
stdErrFuture = [mirrorLogger logConsumptionOf:stdErrConsumer toFileNamed:@"test_process_stderr.err" logger:logger];
|
||||
}
|
||||
|
||||
return [[FBFuture
|
||||
|
@ -141,7 +140,7 @@ static FBFuture<FBApplicationLaunchConfiguration *> *BuildAppLaunchConfig(NSStri
|
|||
return [FBFuture futureWithResult:[[FBTestApplicationsPair alloc] initWithApplicationUnderTest:nil testHostApp:nil]];
|
||||
}
|
||||
if (request.isUITest) {
|
||||
if (!request.appBundleID) {
|
||||
if (!request.testTargetAppBundleID) {
|
||||
return [[FBIDBError
|
||||
describe:@"Request for UI Test, but no app_bundle_id provided"]
|
||||
failFuture];
|
||||
|
@ -149,14 +148,15 @@ static FBFuture<FBApplicationLaunchConfiguration *> *BuildAppLaunchConfig(NSStri
|
|||
NSString *testHostBundleID = request.testHostAppBundleID ?: @"com.apple.Preferences";
|
||||
return [[FBFuture
|
||||
futureWithFutures:@[
|
||||
[target installedApplicationWithBundleID:request.appBundleID],
|
||||
[target installedApplicationWithBundleID:request.testTargetAppBundleID],
|
||||
[target installedApplicationWithBundleID:testHostBundleID],
|
||||
]]
|
||||
onQueue:target.asyncQueue map:^(NSArray<FBInstalledApplication *> *applications) {
|
||||
return [[FBTestApplicationsPair alloc] initWithApplicationUnderTest:applications[0] testHostApp:applications[1]];
|
||||
}];
|
||||
}
|
||||
NSString *bundleID = request.testHostAppBundleID ?: request.appBundleID;
|
||||
// it's an App Test then
|
||||
NSString *bundleID = request.testHostAppBundleID;
|
||||
if (!bundleID) {
|
||||
return [[FBIDBError
|
||||
describe:@"Request for Application Test, but no app_bundle_id or test_host_app_bundle_id provided"]
|
||||
|
@ -171,19 +171,13 @@ static FBFuture<FBApplicationLaunchConfiguration *> *BuildAppLaunchConfig(NSStri
|
|||
|
||||
- (FBFuture<FBIDBAppHostedTestConfiguration *> *)testConfigWithRunRequest:(FBXCTestRunRequest *)request testApps:(FBTestApplicationsPair *)testApps logDirectoryPath:(NSString *)logDirectoryPath logger:(id<FBControlCoreLogger>)logger queue:(dispatch_queue_t)queue
|
||||
{
|
||||
BOOL uiTesting = NO;
|
||||
FBFuture<FBApplicationLaunchConfiguration *> *appLaunchConfigFuture = nil;
|
||||
if (request.isUITest) {
|
||||
appLaunchConfigFuture = BuildAppLaunchConfig(testApps.testHostApp.bundle.identifier, request.environment, request.arguments, logger, logDirectoryPath, request.waitForDebugger, queue);
|
||||
uiTesting = YES;
|
||||
} else {
|
||||
appLaunchConfigFuture = BuildAppLaunchConfig(request.appBundleID, request.environment, request.arguments, logger, logDirectoryPath, request.waitForDebugger, queue);
|
||||
}
|
||||
appLaunchConfigFuture = BuildAppLaunchConfig(testApps.testHostApp.bundle.identifier, request.environment, request.arguments, logger, logDirectoryPath, request.waitForDebugger, queue);
|
||||
FBCodeCoverageConfiguration *coverageConfig = nil;
|
||||
if (request.coverageRequest.collect) {
|
||||
NSString *coverageDirName =[NSString stringWithFormat:@"coverage_%@", NSUUID.UUID.UUIDString];
|
||||
NSString *coverageDirPath = [self.targetAuxillaryDirectory stringByAppendingPathComponent:coverageDirName];
|
||||
coverageConfig = [[FBCodeCoverageConfiguration alloc] initWithDirectory:coverageDirPath format:request.coverageRequest.format];
|
||||
coverageConfig = [[FBCodeCoverageConfiguration alloc] initWithDirectory:coverageDirPath format:request.coverageRequest.format enableContinuousCoverageCollection:request.coverageRequest.shouldEnableContinuousCoverageCollection];
|
||||
}
|
||||
|
||||
return [appLaunchConfigFuture onQueue:queue map:^ FBIDBAppHostedTestConfiguration * (FBApplicationLaunchConfiguration *applicationLaunchConfiguration) {
|
||||
|
@ -192,7 +186,7 @@ static FBFuture<FBApplicationLaunchConfiguration *> *BuildAppLaunchConfig(NSStri
|
|||
applicationLaunchConfiguration:applicationLaunchConfiguration
|
||||
testHostBundle:testApps.testHostApp.bundle
|
||||
timeout:(request.testTimeout ? request.testTimeout.doubleValue : 0)
|
||||
initializeUITesting:uiTesting
|
||||
initializeUITesting:request.isUITest
|
||||
useXcodebuild:NO
|
||||
testsToRun:request.testsToRun
|
||||
testsToSkip:request.testsToSkip
|
||||
|
@ -201,7 +195,9 @@ static FBFuture<FBApplicationLaunchConfiguration *> *BuildAppLaunchConfig(NSStri
|
|||
resultBundlePath:nil
|
||||
reportActivities:request.reportActivities
|
||||
coverageDirectoryPath:coverageConfig.coverageDirectory
|
||||
logDirectoryPath:logDirectoryPath];
|
||||
enableContinuousCoverageCollection:coverageConfig.shouldEnableContinuousCoverageCollection
|
||||
logDirectoryPath:logDirectoryPath
|
||||
reportResultBundle:request.collectResultBundle];
|
||||
return [[FBIDBAppHostedTestConfiguration alloc] initWithTestLaunchConfiguration:testLaunchConfig coverageConfiguration:coverageConfig];
|
||||
}];
|
||||
}
|
||||
|
@ -278,26 +274,35 @@ static FBFuture<FBApplicationLaunchConfiguration *> *BuildAppLaunchConfig(NSStri
|
|||
if (!properties) {
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
|
||||
return [BuildAppLaunchConfig(request.appBundleID, request.environment, request.arguments, logger, nil, request.waitForDebugger, queue)
|
||||
onQueue:queue map:^ FBIDBAppHostedTestConfiguration * (FBApplicationLaunchConfiguration *launchConfig) {
|
||||
FBTestLaunchConfiguration *testLaunchConfiguration = [[FBTestLaunchConfiguration alloc]
|
||||
initWithTestBundle:self.testBundle
|
||||
applicationLaunchConfiguration:launchConfig
|
||||
testHostBundle:self.testHostBundle
|
||||
timeout:0
|
||||
initializeUITesting:request.isUITest
|
||||
useXcodebuild:YES
|
||||
testsToRun:request.testsToRun
|
||||
testsToSkip:request.testsToSkip
|
||||
targetApplicationBundle:nil
|
||||
xcTestRunProperties:properties
|
||||
resultBundlePath:resultBundlePath
|
||||
reportActivities:request.reportActivities
|
||||
coverageDirectoryPath:nil
|
||||
logDirectoryPath:logDirectoryPath];
|
||||
return [[FBIDBAppHostedTestConfiguration alloc] initWithTestLaunchConfiguration:testLaunchConfiguration coverageConfiguration:nil];
|
||||
}];
|
||||
|
||||
FBApplicationLaunchConfiguration *launchConfig = [[FBApplicationLaunchConfiguration alloc]
|
||||
initWithBundleID:@"not.used.bundleId"
|
||||
bundleName:nil
|
||||
arguments:request.arguments ?: @[]
|
||||
environment:request.environment ?: @{}
|
||||
waitForDebugger:request.waitForDebugger
|
||||
io:[[FBProcessIO alloc] initWithStdIn:nil stdOut:nil stdErr:nil]
|
||||
launchMode:FBApplicationLaunchModeFailIfRunning];
|
||||
|
||||
FBTestLaunchConfiguration *testLaunchConfiguration = [[FBTestLaunchConfiguration alloc]
|
||||
initWithTestBundle:self.testBundle
|
||||
applicationLaunchConfiguration:launchConfig
|
||||
testHostBundle:self.testHostBundle
|
||||
timeout:0
|
||||
initializeUITesting:request.isUITest
|
||||
useXcodebuild:YES
|
||||
testsToRun:request.testsToRun
|
||||
testsToSkip:request.testsToSkip
|
||||
targetApplicationBundle:nil
|
||||
xcTestRunProperties:properties
|
||||
resultBundlePath:resultBundlePath
|
||||
reportActivities:request.reportActivities
|
||||
coverageDirectoryPath:nil
|
||||
enableContinuousCoverageCollection:NO
|
||||
logDirectoryPath:logDirectoryPath
|
||||
reportResultBundle:request.collectResultBundle];
|
||||
|
||||
return [FBFuture futureWithResult:[[FBIDBAppHostedTestConfiguration alloc] initWithTestLaunchConfiguration:testLaunchConfiguration coverageConfiguration:nil]];
|
||||
}
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -41,7 +41,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
*/
|
||||
@property (nonatomic, assign, readonly) BOOL reportAttachments;
|
||||
|
||||
- (instancetype)initWithResultBundlePath:(nullable NSString *)resultBundlePath coverageConfiguration:(nullable FBCodeCoverageConfiguration *)coverageConfiguration logDirectoryPath:(nullable NSString *)logDirectoryPath binariesPaths:(nullable NSArray<NSString *> *)binariesPaths reportAttachments:(BOOL)reportAttachments;
|
||||
/**
|
||||
Whether to report return result bundle or not.
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) BOOL reportResultBundle;
|
||||
|
||||
- (instancetype)initWithResultBundlePath:(nullable NSString *)resultBundlePath coverageConfiguration:(nullable FBCodeCoverageConfiguration *)coverageConfiguration logDirectoryPath:(nullable NSString *)logDirectoryPath binariesPaths:(nullable NSArray<NSString *> *)binariesPaths reportAttachments:(BOOL)reportAttachments reportResultBundle:(BOOL)reportResultBundle;
|
||||
|
||||
@end
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -12,7 +12,7 @@
|
|||
|
||||
@implementation FBXCTestReporterConfiguration
|
||||
|
||||
- (instancetype)initWithResultBundlePath:(nullable NSString *)resultBundlePath coverageConfiguration:(nullable FBCodeCoverageConfiguration *)coverageConfiguration logDirectoryPath:(nullable NSString *)logDirectoryPath binariesPaths:(nullable NSArray<NSString *> *)binariesPaths reportAttachments:(BOOL)reportAttachments
|
||||
- (instancetype)initWithResultBundlePath:(nullable NSString *)resultBundlePath coverageConfiguration:(nullable FBCodeCoverageConfiguration *)coverageConfiguration logDirectoryPath:(nullable NSString *)logDirectoryPath binariesPaths:(nullable NSArray<NSString *> *)binariesPaths reportAttachments:(BOOL)reportAttachments reportResultBundle:(BOOL)reportResultBundle
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
|
@ -24,13 +24,14 @@
|
|||
_logDirectoryPath = logDirectoryPath;
|
||||
_binariesPaths = binariesPaths;
|
||||
_reportAttachments = reportAttachments;
|
||||
_reportResultBundle = reportResultBundle;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"Result Bundle %@ | Coverage %@ | Log Path %@ | Binaries Paths %@ | Report Attachments %d", self.resultBundlePath, self.coverageConfiguration, self.logDirectoryPath, [FBCollectionInformation oneLineDescriptionFromArray:self.binariesPaths], self.reportAttachments];
|
||||
return [NSString stringWithFormat:@"Result Bundle %@ | Coverage %@ | Log Path %@ | Binaries Paths %@ | Report Attachments %d | Report Restul Bundle %d", self.resultBundlePath, self.coverageConfiguration, self.logDirectoryPath, [FBCollectionInformation oneLineDescriptionFromArray:self.binariesPaths], self.reportAttachments, self.reportResultBundle];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -28,16 +28,17 @@
|
|||
describeFormat:@"IDB app storage folder does not exist at: %@", idbAppStoragePath]
|
||||
fail:error];
|
||||
}
|
||||
// dictionaryWithContentsOfURL:error: is only available in NSDictionary not in NSMutableDictionary
|
||||
NSMutableDictionary<NSString *, id> *xctestrunContents = [[NSDictionary dictionaryWithContentsOfURL:xctestrunURL error:error] mutableCopy];
|
||||
NSDictionary<NSString *, id> *xctestrunContents = [NSDictionary dictionaryWithContentsOfURL:xctestrunURL error:error];
|
||||
NSMutableDictionary<NSString *, id> *mutableContents = [NSMutableDictionary dictionaryWithCapacity:xctestrunContents.count];
|
||||
if (!xctestrunContents) {
|
||||
return nil;
|
||||
}
|
||||
for (NSString *testTarget in xctestrunContents) {
|
||||
if ([testTarget isEqualToString:@"__xctestrun_metadata__"] || [testTarget isEqualToString:@"CodeCoverageBuildableInfos"]) {
|
||||
for (NSString *contentKey in xctestrunContents) {
|
||||
if ([contentKey isEqualToString:@"__xctestrun_metadata__"] || [contentKey isEqualToString:@"CodeCoverageBuildableInfos"]) {
|
||||
[mutableContents setObject:xctestrunContents[contentKey] forKey:contentKey];
|
||||
continue;
|
||||
}
|
||||
NSMutableDictionary<NSString *, id> *testTargetProperties = [[xctestrunContents objectForKey:testTarget] mutableCopy];
|
||||
NSMutableDictionary<NSString *, id> *testTargetProperties = [[xctestrunContents objectForKey:contentKey] mutableCopy];
|
||||
// Expand __TESTROOT__ and __IDB_APPSTORAGE__ in TestHostPath
|
||||
NSString *testHostPath = [testTargetProperties objectForKey:@"TestHostPath"];
|
||||
if (testHostPath != nil) {
|
||||
|
@ -56,11 +57,22 @@
|
|||
NSString *targetAppPath = [testTargetProperties objectForKey:@"UITargetAppPath"];
|
||||
if (targetAppPath != nil) {
|
||||
targetAppPath = [targetAppPath stringByReplacingOccurrencesOfString:@"__IDB_APPSTORAGE__" withString:idbAppStoragePath];
|
||||
targetAppPath = [targetAppPath stringByReplacingOccurrencesOfString:@"__TESTROOT__" withString:testRoot];
|
||||
[testTargetProperties setObject:targetAppPath forKey:@"UITargetAppPath"];
|
||||
}
|
||||
[xctestrunContents setObject:testTargetProperties forKey:testTarget];
|
||||
NSArray<NSString *> *dependencies = [testTargetProperties objectForKey:@"DependentProductPaths"];
|
||||
if (dependencies && dependencies.count) {
|
||||
NSMutableArray<NSString *> *expandedDeps = [NSMutableArray arrayWithCapacity:dependencies.count];
|
||||
for (NSString *dep in dependencies) {
|
||||
NSString *absPath = [dep stringByReplacingOccurrencesOfString:@"__IDB_APPSTORAGE__" withString:idbAppStoragePath];
|
||||
absPath = [absPath stringByReplacingOccurrencesOfString:@"__TESTROOT__" withString:testRoot];
|
||||
[expandedDeps addObject:absPath];
|
||||
}
|
||||
[testTargetProperties setObject:[NSArray arrayWithArray:expandedDeps] forKey:@"DependentProductPaths"];
|
||||
}
|
||||
[mutableContents setObject:testTargetProperties forKey:contentKey];
|
||||
}
|
||||
return xctestrunContents;
|
||||
return [NSDictionary dictionaryWithDictionary:mutableContents];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
|
@ -10,3 +10,5 @@ DYLIB_INSTALL_NAME_BASE = @rpath
|
|||
FRAMEWORK_VERSION = A
|
||||
VERSIONING_SYSTEM = apple-generic
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path @loader_path/Frameworks
|
||||
// Only expose PrivateHeaders to Framework targets. Framework targets never expose PrivateHeader imports themselves.
|
||||
HEADER_SEARCH_PATHS = $(inherited) $(SRCROOT)/PrivateHeaders
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Warnings & Errors
|
||||
CLANG_CXX_LIBRARY = "libc++"
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES
|
||||
CLANG_WARN_BOOL_CONVERSION = YES
|
||||
CLANG_WARN_COMMA = YES
|
||||
|
@ -8,6 +8,7 @@ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES
|
|||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES
|
||||
CLANG_WARN_DUPLICATE_METHOD_MATCH = YES
|
||||
CLANG_WARN_DUPLICATE_METHOD_MATCH = YES
|
||||
CLANG_WARN_EMPTY_BODY = YES
|
||||
CLANG_WARN_ENUM_CONVERSION = YES
|
||||
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES
|
||||
|
@ -17,17 +18,12 @@ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES
|
|||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES
|
||||
CLANG_WARN_DUPLICATE_METHOD_MATCH = YES
|
||||
COMBINE_HIDPI_IMAGES = YES
|
||||
DEBUG_INFORMATION_FORMAT = dwarf
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES
|
||||
ENABLE_TESTABILITY = YES
|
||||
GCC_NO_COMMON_BLOCKS = YES
|
||||
GCC_OPTIMIZATION_LEVEL = 0
|
||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES
|
||||
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES
|
||||
|
@ -53,24 +49,12 @@ GCC_WARN_UNUSED_FUNCTION = YES
|
|||
GCC_WARN_UNUSED_LABEL = YES
|
||||
GCC_WARN_UNUSED_VALUE = YES
|
||||
GCC_WARN_UNUSED_VARIABLE = YES
|
||||
ONLY_ACTIVE_ARCH = YES
|
||||
|
||||
// Inferred Directories. Important for the linker to be able to find and weak-link private symbols.
|
||||
APPLE_FRAMEWORKS_DIR = $(DEVELOPER_DIR)/../Frameworks
|
||||
APPLE_OTHER_FRAMEWORKS_DIR = $(DEVELOPER_DIR)/../OtherFrameworks
|
||||
APPLE_PLUGINS_DIR = $(DEVELOPER_DIR)/../PlugIns
|
||||
APPLE_SHARED_FRAMEWORKS_DIR = $(DEVELOPER_DIR)/../SharedFrameworks
|
||||
|
||||
// Search Paths for Frameworks & Headers
|
||||
ALWAYS_SEARCH_USER_PATHS = NO
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) $(APPLE_FRAMEWORKS_DIR) $(APPLE_PLUGINS_DIR) $(APPLE_OTHER_FRAMEWORKS_DIR) $(APPLE_SHARED_FRAMEWORKS_DIR) $(DEVELOPER_LIBRARY_DIR)/PrivateFrameworks $(SDKROOT)/System/Library/PrivateFrameworks /Library/Developer/PrivateFrameworks
|
||||
HEADER_SEARCH_PATHS = $(inherited) $(SRCROOT)/PrivateHeaders
|
||||
|
||||
// SDK & OS Versions
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14
|
||||
SDKROOT = macosx
|
||||
|
||||
// Language Standards & Features
|
||||
// Compile-Time Settings
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"
|
||||
CLANG_CXX_LIBRARY = "libc++"
|
||||
CLANG_ENABLE_MODULES = YES
|
||||
|
@ -83,10 +67,8 @@ GCC_SYMBOLS_PRIVATE_EXTERN = NO
|
|||
// Preprocessor
|
||||
ENABLE_NS_ASSERTIONS = YES
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES
|
||||
ENABLE_TESTABILITY = YES
|
||||
|
||||
// Deployment
|
||||
COMBINE_HIDPI_IMAGES = YES
|
||||
COPY_PHASE_STRIP = NO
|
||||
INSTALL_PATH = @rpath
|
||||
SKIP_INSTALL = YES
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -23,17 +23,6 @@ typedef NS_ENUM(NSUInteger, FBApplicationInstallType) {
|
|||
FBApplicationInstallTypeUserDevelopment = 5, /** The Application has been installed by the user and signed with a development certificate */
|
||||
};
|
||||
|
||||
/**
|
||||
String Representations of the Installed Type.
|
||||
*/
|
||||
typedef NSString *FBApplicationInstallTypeString NS_STRING_ENUM;
|
||||
extern FBApplicationInstallTypeString const FBApplicationInstallTypeStringUnknown;
|
||||
extern FBApplicationInstallTypeString const FBApplicationInstallTypeStringSystem;
|
||||
extern FBApplicationInstallTypeString const FBApplicationInstallTypeStringMac;
|
||||
extern FBApplicationInstallTypeString const FBApplicationInstallTypeStringUser;
|
||||
extern FBApplicationInstallTypeString const FBApplicationInstallTypeStringUserEnterprise;
|
||||
extern FBApplicationInstallTypeString const FBApplicationInstallTypeStringUserDevelopment;
|
||||
|
||||
/**
|
||||
Keys from UserInfo about Applications
|
||||
*/
|
||||
|
@ -61,6 +50,16 @@ extern FBApplicationInstallInfoKey const FBApplicationInstallInfoKeySignerIdenti
|
|||
*/
|
||||
+ (instancetype)installedApplicationWithBundle:(FBBundleDescriptor *)bundle installType:(FBApplicationInstallType)installType dataContainer:(nullable NSString *)dataContainer;
|
||||
|
||||
/**
|
||||
The Designated Initializer.
|
||||
|
||||
@param bundle the Application Bundle. This represents the bundle as-installed on the target, rather than pre-install.
|
||||
@param installTypeString the string representation of the install type.
|
||||
@param dataContainer the Data Container Path, may be nil.
|
||||
@return a new Installed Application Instance.
|
||||
*/
|
||||
+ (instancetype)installedApplicationWithBundle:(FBBundleDescriptor *)bundle installTypeString:(nullable NSString *)installTypeString signerIdentity:(nullable NSString *)signerIdentity dataContainer:(nullable NSString *)dataContainer;
|
||||
|
||||
#pragma mark Properties
|
||||
|
||||
/**
|
||||
|
@ -73,30 +72,16 @@ extern FBApplicationInstallInfoKey const FBApplicationInstallInfoKeySignerIdenti
|
|||
*/
|
||||
@property (nonatomic, assign, readonly) FBApplicationInstallType installType;
|
||||
|
||||
/**
|
||||
The "Install Type" enum of the Application, represented as a string.
|
||||
*/
|
||||
@property (nonatomic, copy, readonly) NSString *installTypeString;
|
||||
|
||||
/**
|
||||
The data container path of the Application.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable, readonly) NSString *dataContainer;
|
||||
|
||||
#pragma mark Install Type
|
||||
|
||||
/**
|
||||
Returns a String Represnting the Application Install Type.
|
||||
|
||||
@param installType the install type enum.
|
||||
@return a string of the install type.
|
||||
*/
|
||||
+ (FBApplicationInstallTypeString)stringFromApplicationInstallType:(FBApplicationInstallType)installType;
|
||||
|
||||
/**
|
||||
Returns the FBApplicationInstallType from the string representation.
|
||||
|
||||
@param installTypeString install type as a string
|
||||
@param signerIdentity the signer identity.
|
||||
@return an FBApplicationInstallType
|
||||
*/
|
||||
+ (FBApplicationInstallType)installTypeFromString:(nullable FBApplicationInstallTypeString)installTypeString signerIdentity:(nullable NSString *)signerIdentity;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -9,12 +9,12 @@
|
|||
|
||||
#import "FBBundleDescriptor.h"
|
||||
|
||||
FBApplicationInstallTypeString const FBApplicationInstallTypeStringUnknown = @"unknown";
|
||||
FBApplicationInstallTypeString const FBApplicationInstallTypeStringSystem = @"system";
|
||||
FBApplicationInstallTypeString const FBApplicationInstallTypeStringMac = @"mac";
|
||||
FBApplicationInstallTypeString const FBApplicationInstallTypeStringUser = @"user";
|
||||
FBApplicationInstallTypeString const FBApplicationInstallTypeStringUserEnterprise = @"user_enterprise";
|
||||
FBApplicationInstallTypeString const FBApplicationInstallTypeStringUserDevelopment = @"user_development";
|
||||
static NSString *const FBApplicationInstallTypeStringUnknown = @"unknown";
|
||||
static NSString *const FBApplicationInstallTypeStringSystem = @"system";
|
||||
static NSString *const FBApplicationInstallTypeStringMac = @"mac";
|
||||
static NSString *const FBApplicationInstallTypeStringUser = @"user";
|
||||
static NSString *const FBApplicationInstallTypeStringUserEnterprise = @"user_enterprise";
|
||||
static NSString *const FBApplicationInstallTypeStringUserDevelopment = @"user_development";
|
||||
|
||||
FBApplicationInstallInfoKey const FBApplicationInstallInfoKeyApplicationType = @"ApplicationType";
|
||||
FBApplicationInstallInfoKey const FBApplicationInstallInfoKeyBundleIdentifier = @"CFBundleIdentifier";
|
||||
|
@ -31,6 +31,11 @@ FBApplicationInstallInfoKey const FBApplicationInstallInfoKeySignerIdentity = @"
|
|||
return [[self alloc] initWithBundle:bundle installType:installType dataContainer:dataContainer];
|
||||
}
|
||||
|
||||
+ (instancetype)installedApplicationWithBundle:(FBBundleDescriptor *)bundle installTypeString:(NSString *)installTypeString signerIdentity:(NSString *)signerIdentity dataContainer:(NSString *)dataContainer
|
||||
{
|
||||
return [self installedApplicationWithBundle:bundle installType:[self installTypeFromString:installTypeString signerIdentity:signerIdentity] dataContainer:dataContainer];
|
||||
}
|
||||
|
||||
- (instancetype)initWithBundle:(FBBundleDescriptor *)bundle installType:(FBApplicationInstallType)installType dataContainer:(NSString *)dataContainer
|
||||
{
|
||||
self = [super init];
|
||||
|
@ -67,7 +72,7 @@ FBApplicationInstallInfoKey const FBApplicationInstallInfoKeySignerIdentity = @"
|
|||
return [NSString stringWithFormat:
|
||||
@"Bundle %@ | Install Type %@ | Container %@",
|
||||
self.bundle.description,
|
||||
[FBInstalledApplication stringFromApplicationInstallType:self.installType],
|
||||
self.installTypeString,
|
||||
self.dataContainer
|
||||
];
|
||||
}
|
||||
|
@ -79,9 +84,16 @@ FBApplicationInstallInfoKey const FBApplicationInstallInfoKeySignerIdentity = @"
|
|||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Properties
|
||||
|
||||
- (NSString *)installTypeString
|
||||
{
|
||||
return [FBInstalledApplication stringFromApplicationInstallType:self.installType];
|
||||
}
|
||||
|
||||
#pragma mark Install Type
|
||||
|
||||
+ (FBApplicationInstallTypeString)stringFromApplicationInstallType:(FBApplicationInstallType)installType
|
||||
+ (NSString *)stringFromApplicationInstallType:(FBApplicationInstallType)installType
|
||||
{
|
||||
switch (installType) {
|
||||
case FBApplicationInstallTypeUser:
|
||||
|
@ -99,7 +111,7 @@ FBApplicationInstallInfoKey const FBApplicationInstallInfoKeySignerIdentity = @"
|
|||
}
|
||||
}
|
||||
|
||||
+ (FBApplicationInstallType)installTypeFromString:(nullable FBApplicationInstallTypeString)installTypeString signerIdentity:(nullable NSString *)signerIdentity
|
||||
+ (FBApplicationInstallType)installTypeFromString:(nullable NSString *)installTypeString signerIdentity:(nullable NSString *)signerIdentity
|
||||
{
|
||||
if (!installTypeString) {
|
||||
return FBApplicationInstallTypeUnknown;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -91,7 +91,7 @@ static NSString *const KeyIsAwaiting = @"FBCONTROLCORE_IS_AWAITING";
|
|||
|
||||
static NSTimeInterval const ForeverTimeout = DBL_MAX;
|
||||
|
||||
static dispatch_queue_t blockQueue()
|
||||
static dispatch_queue_t blockQueue(void)
|
||||
{
|
||||
return dispatch_queue_create("com.facebook.fbfuture.block", DISPATCH_QUEUE_SERIAL);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -135,7 +135,7 @@ extern dispatch_time_t FBCreateDispatchTimeFromDuration(NSTimeInterval inDuratio
|
|||
@param futures the futures to compose.
|
||||
@return a new future with the resolved results of all the composed futures.
|
||||
*/
|
||||
+ (FBFuture<NSArray<T> *> *)futureWithFutures:(NSArray<FBFuture<T> *> *)futures;
|
||||
+ (FBFuture<NSArray<T> *> *)futureWithFutures:(NSArray<FBFuture<T> *> *)futures NS_SWIFT_UNAVAILABLE("Use BridgeFuture.values instead");
|
||||
|
||||
/**
|
||||
Constructrs a Future from an Array of Futures.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@param nestedFormat if YES then data is returned in the nested format, NO for flat format
|
||||
@return the accessibility elements for the main screen, wrapped in a Future.
|
||||
*/
|
||||
- (FBFuture<NSArray<NSDictionary<NSString *, id> *> *> *)accessibilityElementsWithNestedFormat:(BOOL)nestedFormat;
|
||||
- (FBFuture<id> *)accessibilityElementsWithNestedFormat:(BOOL)nestedFormat;
|
||||
|
||||
/**
|
||||
Obtain the acessibility element for the main screen at the given point.
|
||||
|
@ -35,7 +35,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@param nestedFormat if YES then data is returned in the nested format, NO for flat format
|
||||
@return the accessibility element at the provided point, wrapped in a Future.
|
||||
*/
|
||||
- (FBFuture<NSDictionary<NSString *, id> *> *)accessibilityElementAtPoint:(CGPoint)point nestedFormat:(BOOL)nestedFormat;
|
||||
- (FBFuture<id> *)accessibilityElementAtPoint:(CGPoint)point nestedFormat:(BOOL)nestedFormat;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -98,6 +98,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
*/
|
||||
- (FBFutureContext<id<FBFileContainer>> *)fileCommandsForDiskImages;
|
||||
|
||||
/**
|
||||
Returns a file container for manipulating device symbols.
|
||||
|
||||
@return a Future context that resolves with an implementation of the file container.
|
||||
*/
|
||||
- (FBFutureContext<id<FBFileContainer>> *)fileCommandsForSymbols;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -26,6 +26,7 @@ extern FBFileContainerKind const FBFileContainerKindMedia;
|
|||
extern FBFileContainerKind const FBFileContainerKindProvisioningProfiles;
|
||||
extern FBFileContainerKind const FBFileContainerKindRoot;
|
||||
extern FBFileContainerKind const FBFileContainerKindSpringboardIcons;
|
||||
extern FBFileContainerKind const FBFileContainerKindSymbols;
|
||||
extern FBFileContainerKind const FBFileContainerKindWallpaper;
|
||||
|
||||
@protocol FBDataConsumer;
|
||||
|
@ -45,25 +46,25 @@ extern FBFileContainerKind const FBFileContainerKindWallpaper;
|
|||
@param destinationPath the destination path to copy to, relative to the root of the container.
|
||||
@return A future that resolves when successful.
|
||||
*/
|
||||
- (FBFuture<NSNull *> *)copyFromHost:(NSURL *)sourcePath toContainer:(NSString *)destinationPath;
|
||||
- (FBFuture<NSNull *> *)copyFromHost:(NSString *)sourcePath toContainer:(NSString *)destinationPath;
|
||||
|
||||
/**
|
||||
Copy a path from inside the container, to the host.
|
||||
|
||||
@param containerPath the source path, relative to the root of the container. May be Files and/or Directories.
|
||||
@param sourcePath the source path, relative to the root of the container. May be Files and/or Directories.
|
||||
@param destinationPath the destination path on the host.
|
||||
@return A future that resolves with the destination path when successful.
|
||||
*/
|
||||
- (FBFuture<NSString *> *)copyFromContainer:(NSString *)containerPath toHost:(NSString *)destinationPath;
|
||||
- (FBFuture<NSString *> *)copyFromContainer:(NSString *)sourcePath toHost:(NSString *)destinationPath;
|
||||
|
||||
/**
|
||||
Tails a path from inside the container, to a consumer.
|
||||
Tails the contents of a file path inside the container, to a data consumer.
|
||||
|
||||
@param containerPath the source path to tail, relative to the root of the container. Must be a file
|
||||
@param path the source path to tail, relative to the root of the container. Must be a file
|
||||
@param consumer the consumer to write to.
|
||||
@return a Future that resolves with a Future when the tailing has completed. The wrapped future can be cancelled to end the tailing operation.
|
||||
*/
|
||||
- (FBFuture<FBFuture<NSNull *> *> *)tail:(NSString *)containerPath toConsumer:(id<FBDataConsumer>)consumer;
|
||||
- (FBFuture<FBFuture<NSNull *> *> *)tail:(NSString *)path toConsumer:(id<FBDataConsumer>)consumer;
|
||||
|
||||
/**
|
||||
Create a directory inside the container.
|
||||
|
@ -100,6 +101,95 @@ extern FBFileContainerKind const FBFileContainerKindWallpaper;
|
|||
|
||||
@end
|
||||
|
||||
/**
|
||||
An abstraction over a file. The file can be local to the host, or remote.
|
||||
*/
|
||||
@protocol FBContainedFile <NSObject>
|
||||
|
||||
/**
|
||||
Removes a file path.
|
||||
If the path is a directory, the entire directory is removed and all contents, recursively.
|
||||
|
||||
@param error an error out for any error that occurs.
|
||||
@return YES on success, NO otherwise.
|
||||
*/
|
||||
- (BOOL)removeItemWithError:(NSError **)error;
|
||||
|
||||
/**
|
||||
List the contents of a path.
|
||||
|
||||
@param error an error out for any error that occurs.
|
||||
@return an array of strings representing the directory contents.
|
||||
*/
|
||||
- (nullable NSArray<NSString *> *)contentsOfDirectoryWithError:(NSError **)error;
|
||||
|
||||
/**
|
||||
Obtains the contents of the contained file.
|
||||
|
||||
@param error an error out for any error that occurs.
|
||||
@return Data for the file, or an nil on error.
|
||||
*/
|
||||
- (nullable NSData *)contentsOfFileWithError:(NSError **)error;
|
||||
|
||||
/**
|
||||
Creates a directory at the given path.
|
||||
|
||||
@param error an error out for any error that occurs.
|
||||
@return YES on success, NO otherwise.
|
||||
*/
|
||||
- (BOOL)createDirectoryWithError:(NSError **)error;
|
||||
|
||||
/**
|
||||
Checks whether the path exists, optionally providing information about whether the path is a regular file or directory.
|
||||
|
||||
@param isDirectoryOut an outparam for indicating if the path represents a directory.
|
||||
@return YES if exists, NO otherwise.
|
||||
*/
|
||||
- (BOOL)fileExistsIsDirectory:(BOOL *)isDirectoryOut;
|
||||
|
||||
/**
|
||||
Moves the receiver to the provided destination file.
|
||||
|
||||
@param destination the destination to move to.
|
||||
@param error an error out for any error that occurs.
|
||||
*/
|
||||
- (BOOL)moveTo:(id<FBContainedFile>)destination error:(NSError **)error;
|
||||
|
||||
/**
|
||||
Replaces the contents of the wrapped file with the provided path on the host filesystem.
|
||||
|
||||
@param path the path to pull contents from.
|
||||
@param error an error out for any error that occurs.
|
||||
@return YES if successful, NO otherwise.
|
||||
*/
|
||||
- (BOOL)populateWithContentsOfHostPath:(NSString *)path error:(NSError **)error;
|
||||
|
||||
/**
|
||||
Replaces provided path on the host filesystem with the contents of the wrapped file.
|
||||
|
||||
@param path the path to push contents to.
|
||||
@param error an error out for any error that occurs.
|
||||
@return YES if successful, NO otherwise.
|
||||
*/
|
||||
- (BOOL)populateHostPathWithContents:(NSString *)path error:(NSError **)error;
|
||||
|
||||
/**
|
||||
Constructs a new contained file by appending a path component.
|
||||
|
||||
@param component the component to add.
|
||||
@param error an error out if the path is invalid.
|
||||
@return the new contained file.
|
||||
*/
|
||||
- (id<FBContainedFile>)fileByAppendingPathComponent:(NSString *)component error:(NSError **)error;
|
||||
|
||||
/**
|
||||
The host path corresponding to this file, if any.
|
||||
If the file is remote, this will be nil
|
||||
*/
|
||||
@property (nonatomic, copy, nullable, readonly) NSString *pathOnHostFileSystem;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
Implementations of File Commands.
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -22,8 +22,439 @@ FBFileContainerKind const FBFileContainerKindMedia = @"media";
|
|||
FBFileContainerKind const FBFileContainerKindProvisioningProfiles = @"provisioning_profiles";
|
||||
FBFileContainerKind const FBFileContainerKindRoot = @"root";
|
||||
FBFileContainerKind const FBFileContainerKindSpringboardIcons = @"springboard_icons";
|
||||
FBFileContainerKind const FBFileContainerKindSymbols = @"symbols";
|
||||
FBFileContainerKind const FBFileContainerKindWallpaper = @"wallpaper";
|
||||
|
||||
@interface FBContainedFile_Host : NSObject <FBContainedFile>
|
||||
|
||||
@property (nonatomic, strong, readonly) NSFileManager *fileManager;
|
||||
@property (nonatomic, copy, readonly) NSString *path;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBContainedFile_Host
|
||||
|
||||
#pragma mark Initializers
|
||||
|
||||
- (instancetype)initWithFileManager:(NSFileManager *)fileManager path:(NSString *)path
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_fileManager = fileManager;
|
||||
_path = path;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark FBContainedFile
|
||||
|
||||
- (BOOL)removeItemWithError:(NSError **)error
|
||||
{
|
||||
return [self.fileManager removeItemAtPath:self.path error:error];
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)contentsOfDirectoryWithError:(NSError **)error
|
||||
{
|
||||
return [self.fileManager contentsOfDirectoryAtPath:self.path error:error];
|
||||
}
|
||||
|
||||
- (NSData *)contentsOfFileWithError:(NSError **)error
|
||||
{
|
||||
return [NSData dataWithContentsOfFile:self.path options:0 error:error];
|
||||
}
|
||||
|
||||
- (BOOL)moveTo:(id<FBContainedFile>)destination error:(NSError **)error
|
||||
{
|
||||
if (![destination isKindOfClass:FBContainedFile_Host.class]) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Cannot move to %@, it is not on the host filesystem", destination]
|
||||
failBool:error];
|
||||
}
|
||||
FBContainedFile_Host *hostDestination = (FBContainedFile_Host *) destination;
|
||||
return [self.fileManager moveItemAtPath:self.path toPath:hostDestination.path error:error];
|
||||
}
|
||||
|
||||
- (BOOL)createDirectoryWithError:(NSError **)error
|
||||
{
|
||||
return [self.fileManager createDirectoryAtPath:self.path withIntermediateDirectories:YES attributes:nil error:error];
|
||||
}
|
||||
|
||||
- (BOOL)fileExistsIsDirectory:(BOOL *)isDirectoryOut
|
||||
{
|
||||
return [self.fileManager fileExistsAtPath:self.path isDirectory:isDirectoryOut];
|
||||
}
|
||||
|
||||
- (BOOL)populateWithContentsOfHostPath:(NSString *)path error:(NSError **)error
|
||||
{
|
||||
return [self.fileManager copyItemAtPath:path toPath:self.path error:error];
|
||||
}
|
||||
|
||||
- (BOOL)populateHostPathWithContents:(NSString *)path error:(NSError **)error
|
||||
{
|
||||
return [self.fileManager copyItemAtPath:self.path toPath:path error:error];
|
||||
}
|
||||
|
||||
- (id<FBContainedFile>)fileByAppendingPathComponent:(NSString *)component error:(NSError **)error
|
||||
{
|
||||
return [[FBContainedFile_Host alloc] initWithFileManager:self.fileManager path:[self.path stringByAppendingPathComponent:component]];
|
||||
}
|
||||
|
||||
- (NSString *)pathOnHostFileSystem
|
||||
{
|
||||
return self.path;
|
||||
}
|
||||
|
||||
#pragma mark NSObject
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"Host File %@", self.path];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface FBContainedFile_Mapped_Host : NSObject <FBContainedFile>
|
||||
|
||||
@property (nonatomic, copy, readonly) NSDictionary<NSString *, NSString *> *mappingPaths;
|
||||
@property (nonatomic, strong, readonly) NSFileManager *fileManager;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBContainedFile_Mapped_Host
|
||||
|
||||
- (instancetype)initWithMappingPaths:(NSDictionary<NSString *, NSString *> *)mappingPaths fileManager:(NSFileManager *)fileManager;
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_mappingPaths = mappingPaths;
|
||||
_fileManager = fileManager;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark FBContainedFile
|
||||
|
||||
- (BOOL)removeItemWithError:(NSError **)error
|
||||
{
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"%@ does not operate on root virtual containers", NSStringFromSelector(_cmd)]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)contentsOfDirectoryWithError:(NSError **)error
|
||||
{
|
||||
return self.mappingPaths.allKeys;
|
||||
}
|
||||
|
||||
- (BOOL)createDirectoryWithError:(NSError **)error
|
||||
{
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"%@ does not operate on root virtual containers", NSStringFromSelector(_cmd)]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
- (NSData *)contentsOfFileWithError:(NSError **)error
|
||||
{
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"%@ does not operate on root virtual containers", NSStringFromSelector(_cmd)]
|
||||
fail:error];
|
||||
}
|
||||
|
||||
- (BOOL)fileExistsIsDirectory:(BOOL *)isDirectoryOut
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)moveTo:(id<FBContainedFile>)destination error:(NSError **)error
|
||||
{
|
||||
return [[FBControlCoreError
|
||||
describe:@"Moving files does not work on root virtual containers"]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
- (BOOL)populateWithContentsOfHostPath:(NSString *)path error:(NSError **)error
|
||||
{
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"%@ does not operate on root virtual containers", NSStringFromSelector(_cmd)]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
- (BOOL)populateHostPathWithContents:(NSString *)path error:(NSError **)error
|
||||
{
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"%@ does not operate on root virtual containers", NSStringFromSelector(_cmd)]
|
||||
failBool:error];
|
||||
}
|
||||
|
||||
- (id<FBContainedFile>)fileByAppendingPathComponent:(NSString *)component error:(NSError **)error
|
||||
{
|
||||
// If the provided path represents the root (the mapping itself), then there's nothing to map to.
|
||||
NSArray<NSString *> *pathComponents = component.pathComponents;
|
||||
if ([FBContainedFile_Mapped_Host isRootPathOfContainer:pathComponents]) {
|
||||
return self;
|
||||
}
|
||||
NSString *firstComponent = pathComponents.firstObject;
|
||||
NSString *nextPath = [FBContainedFile_Mapped_Host popFirstPathComponent:pathComponents];
|
||||
NSString *mappedPath = self.mappingPaths[firstComponent];
|
||||
if (!mappedPath) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"'%@' is not a valid root path out of %@", firstComponent, [FBCollectionInformation oneLineDescriptionFromArray:self.mappingPaths.allKeys]]
|
||||
fail:error];
|
||||
}
|
||||
id<FBContainedFile> mapped = [[FBContainedFile_Host alloc] initWithFileManager:self.fileManager path:mappedPath];
|
||||
return [mapped fileByAppendingPathComponent:nextPath error:error];
|
||||
}
|
||||
|
||||
- (NSString *)pathOnHostFileSystem
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark NSObject
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"Root mapping: %@", [FBCollectionInformation oneLineDescriptionFromArray:self.mappingPaths.allKeys]];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
+ (BOOL)isRootPathOfContainer:(NSArray<NSString *> *)pathComponents
|
||||
{
|
||||
// If no path components this must be the root
|
||||
if (pathComponents.count == 0) {
|
||||
return YES;
|
||||
}
|
||||
// The root is also signified by a query for the root of the container.
|
||||
NSString *firstPath = pathComponents.firstObject;
|
||||
if (pathComponents.count == 1 && ([firstPath isEqualToString:@"."] || [firstPath isEqualToString:@"/"])) {
|
||||
return YES;
|
||||
}
|
||||
// Otherwise we can't be the root path
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (NSString *)popFirstPathComponent:(NSArray<NSString *> *)pathComponents
|
||||
{
|
||||
// Re-assemble the mapped path, discarding the re-mapped first path component.
|
||||
BOOL isFirstPathComponent = YES;
|
||||
NSString *next = @"";
|
||||
for (NSString *pathComponent in pathComponents) {
|
||||
if (isFirstPathComponent) {
|
||||
isFirstPathComponent = NO;
|
||||
continue;
|
||||
}
|
||||
next = [next stringByAppendingPathComponent:pathComponent];
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface FBContainedFile_ContainedRoot : NSObject <FBFileContainer>
|
||||
|
||||
@property (nonatomic, strong, readonly) dispatch_queue_t queue;
|
||||
@property (nonatomic, strong, readonly) id<FBContainedFile> rootFile;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBContainedFile_ContainedRoot
|
||||
|
||||
- (instancetype)initWithRootFile:(id<FBContainedFile>)rootFile queue:(dispatch_queue_t)queue
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_rootFile = rootFile;
|
||||
_queue = queue;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark FBFileCommands
|
||||
|
||||
- (FBFuture<NSNull *> *)copyFromHost:(NSString *)sourcePath toContainer:(NSString *)destinationPath
|
||||
{
|
||||
return [[self
|
||||
mapToContainedFile:destinationPath]
|
||||
onQueue:self.queue fmap:^ FBFuture<NSNull *> * (id<FBContainedFile> destination) {
|
||||
// Attempt to delete first to overwrite
|
||||
NSError *error;
|
||||
destination = [destination fileByAppendingPathComponent:sourcePath.lastPathComponent error:&error];
|
||||
if (!destination) {
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
[destination removeItemWithError:nil];
|
||||
if (![destination populateWithContentsOfHostPath:sourcePath error:&error]) {
|
||||
return [[[FBControlCoreError
|
||||
describeFormat:@"Could not copy from %@ to %@: %@", sourcePath, destinationPath, error]
|
||||
causedBy:error]
|
||||
failFuture];
|
||||
}
|
||||
return FBFuture.empty;
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSString *> *)copyFromContainer:(NSString *)sourcePath toHost:(NSString *)destinationPath
|
||||
{
|
||||
return [[self
|
||||
mapToContainedFile:sourcePath]
|
||||
onQueue:self.queue fmap:^ FBFuture<NSString *> * (id<FBContainedFile> source) {
|
||||
BOOL sourceIsDirectory = NO;
|
||||
if (![source fileExistsIsDirectory:&sourceIsDirectory]) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Source path does not exist: %@", source]
|
||||
failFuture];
|
||||
}
|
||||
NSString *dstPath = destinationPath;
|
||||
if (!sourceIsDirectory) {
|
||||
NSError *createDirectoryError;
|
||||
if (![NSFileManager.defaultManager createDirectoryAtPath:dstPath withIntermediateDirectories:YES attributes:@{} error:&createDirectoryError]) {
|
||||
return [[[FBControlCoreError
|
||||
describeFormat:@"Could not create temporary directory: %@", createDirectoryError]
|
||||
causedBy:createDirectoryError]
|
||||
failFuture];
|
||||
}
|
||||
dstPath = [dstPath stringByAppendingPathComponent:[sourcePath lastPathComponent]];
|
||||
}
|
||||
// if it already exists at the destination path we should remove it before copying again
|
||||
BOOL destinationIsDirectory = NO;
|
||||
if ([NSFileManager.defaultManager fileExistsAtPath:dstPath isDirectory:&destinationIsDirectory]) {
|
||||
NSError *removeError;
|
||||
if (![NSFileManager.defaultManager removeItemAtPath:dstPath error:&removeError]) {
|
||||
return [[[FBControlCoreError
|
||||
describeFormat:@"Could not remove %@", dstPath]
|
||||
causedBy:removeError]
|
||||
failFuture];
|
||||
}
|
||||
}
|
||||
|
||||
NSError *copyError;
|
||||
if (![source populateHostPathWithContents:dstPath error:©Error]) {
|
||||
return [[[FBControlCoreError
|
||||
describeFormat:@"Could not copy from %@ to %@: %@", source, dstPath, copyError]
|
||||
causedBy:copyError]
|
||||
failFuture];
|
||||
}
|
||||
return [FBFuture futureWithResult:destinationPath];
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<FBFuture<NSNull *> *> *)tail:(NSString *)path toConsumer:(id<FBDataConsumer>)consumer
|
||||
{
|
||||
return [[[self
|
||||
mapToContainedFile:path]
|
||||
onQueue:self.queue fmap:^ FBFuture<FBProcess<NSNull *, id<FBDataConsumer>, NSData *> *> * (id<FBContainedFile> fileToTail) {
|
||||
NSString *pathOnHostFileSystem = fileToTail.pathOnHostFileSystem;
|
||||
if (!pathOnHostFileSystem) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Cannot tail %@, it is not on the local filesystem", fileToTail]
|
||||
failFuture];
|
||||
}
|
||||
return [[[[FBProcessBuilder
|
||||
withLaunchPath:@"/usr/bin/tail"]
|
||||
withArguments:@[@"-c+1", @"-f", pathOnHostFileSystem]]
|
||||
withStdOutConsumer:consumer]
|
||||
start];
|
||||
}]
|
||||
onQueue:self.queue map:^(FBProcess *process) {
|
||||
return [process.statLoc
|
||||
onQueue:self.queue respondToCancellation:^{
|
||||
return [process sendSignal:SIGTERM backingOffToKillWithTimeout:1 logger:nil];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSNull *> *)createDirectory:(NSString *)directoryPath
|
||||
{
|
||||
return [[self
|
||||
mapToContainedFile:directoryPath]
|
||||
onQueue:self.queue fmap:^ FBFuture<NSNull *> * (id<FBContainedFile> directory) {
|
||||
NSError *error;
|
||||
if (![directory createDirectoryWithError:&error]) {
|
||||
return [[[FBControlCoreError
|
||||
describeFormat:@"Could not create directory %@: %@", directory, error]
|
||||
causedBy:error]
|
||||
failFuture];
|
||||
}
|
||||
return FBFuture.empty;
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSNull *> *)moveFrom:(NSString *)sourcePath to:(NSString *)destinationPath
|
||||
{
|
||||
return [[FBFuture
|
||||
futureWithFutures:@[
|
||||
[self mapToContainedFile:sourcePath],
|
||||
[self mapToContainedFile:destinationPath],
|
||||
]]
|
||||
onQueue:self.queue fmap:^ FBFuture<NSNull *> * (NSArray<id<FBContainedFile>> *providedFiles) {
|
||||
// If the source and destination are on the same filesystem, they can be moved directly.
|
||||
id<FBContainedFile> source = providedFiles[0];
|
||||
id<FBContainedFile> destination = providedFiles[1];
|
||||
NSError *error = nil;
|
||||
if (![source moveTo:destination error:&error]) {
|
||||
return [[[FBControlCoreError
|
||||
describeFormat:@"Could not move item at %@ to %@: %@", source, destination, error]
|
||||
causedBy:error]
|
||||
failFuture];
|
||||
}
|
||||
return FBFuture.empty;
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSNull *> *)remove:(NSString *)path
|
||||
{
|
||||
return [[self
|
||||
mapToContainedFile:path]
|
||||
onQueue:self.queue fmap:^ FBFuture<NSNull *> * (id<FBContainedFile> file) {
|
||||
NSError *error;
|
||||
if (![file removeItemWithError:&error]) {
|
||||
return [[[FBControlCoreError
|
||||
describeFormat:@"Could not remove item at path %@: %@", file, error]
|
||||
causedBy:error]
|
||||
failFuture];
|
||||
}
|
||||
return FBFuture.empty;
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSArray<NSString *> *> *)contentsOfDirectory:(NSString *)path
|
||||
{
|
||||
return [[self
|
||||
mapToContainedFile:path]
|
||||
onQueue:self.queue fmap:^(id<FBContainedFile> directory) {
|
||||
NSError *error;
|
||||
NSArray<NSString *> *contents = [directory contentsOfDirectoryWithError:&error];
|
||||
if (!contents) {
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
return [FBFuture futureWithResult:contents];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
- (FBFuture<id<FBContainedFile>> *)mapToContainedFile:(NSString *)path
|
||||
{
|
||||
NSError *error = nil;
|
||||
id<FBContainedFile> file = [self.rootFile fileByAppendingPathComponent:path error:&error];
|
||||
if (!file) {
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
return [FBFuture futureWithResult:file];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface FBFileContainer_ProvisioningProfile : NSObject <FBFileContainer>
|
||||
|
||||
@property (nonatomic, strong, readonly) id<FBProvisioningProfileCommands> commands;
|
||||
|
@ -48,12 +479,12 @@ FBFileContainerKind const FBFileContainerKindWallpaper = @"wallpaper";
|
|||
|
||||
#pragma mark FBFileContainer Implementation
|
||||
|
||||
- (FBFuture<NSNull *> *)copyFromHost:(NSURL *)path toContainer:(NSString *)destinationPath
|
||||
- (FBFuture<NSNull *> *)copyFromHost:(NSString *)path toContainer:(NSString *)destinationPath
|
||||
{
|
||||
return [FBFuture
|
||||
onQueue:self.queue resolve:^ FBFuture<NSNull *> * {
|
||||
NSError *error = nil;
|
||||
NSData *data = [NSData dataWithContentsOfURL:path options:0 error:&error];
|
||||
NSData *data = [NSData dataWithContentsOfFile:path options:0 error:&error];
|
||||
if (!data) {
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
|
@ -109,411 +540,6 @@ FBFileContainerKind const FBFileContainerKindWallpaper = @"wallpaper";
|
|||
|
||||
@end
|
||||
|
||||
@interface FBFileContainerBase : NSObject <FBFileContainer>
|
||||
|
||||
@property (nonatomic, strong, readonly) dispatch_queue_t queue;
|
||||
@property (nonatomic, strong, readonly) NSFileManager *fileManager;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBFileContainerBase
|
||||
|
||||
- (instancetype)initWithQueue:(dispatch_queue_t)queue
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_queue = queue;
|
||||
_fileManager = NSFileManager.defaultManager;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark FBFileCommands
|
||||
|
||||
- (FBFuture<NSNull *> *)copyFromHost:(NSURL *)sourcePath toContainer:(NSString *)destinationPath
|
||||
{
|
||||
return [[self
|
||||
mappedPath:destinationPath]
|
||||
onQueue:self.queue fmap:^ FBFuture<NSNull *> * (NSString *mappedPath) {
|
||||
NSError *error;
|
||||
NSString *destPath = [mappedPath stringByAppendingPathComponent:sourcePath.lastPathComponent];
|
||||
// Attempt to delete first to overwrite
|
||||
[self removeItemAtPath:destPath error:nil];
|
||||
if (![self copyItemAtPath:sourcePath.path toPath:destPath error:&error]) {
|
||||
return [[[FBControlCoreError
|
||||
describeFormat:@"Could not copy from %@ to %@: %@", sourcePath, destPath, error]
|
||||
causedBy:error]
|
||||
failFuture];
|
||||
}
|
||||
return FBFuture.empty;
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSString *> *)copyFromContainer:(NSString *)containerPath toHost:(NSString *)destinationPath
|
||||
{
|
||||
return [[self
|
||||
mappedPath:containerPath]
|
||||
onQueue:self.queue fmap:^ FBFuture<NSString *> * (NSString *source) {
|
||||
BOOL srcIsDirecory = NO;
|
||||
if (![self.fileManager fileExistsAtPath:source isDirectory:&srcIsDirecory]) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Source path does not exist: %@", source]
|
||||
failFuture];
|
||||
}
|
||||
NSString *dstPath = destinationPath;
|
||||
if (!srcIsDirecory) {
|
||||
NSError *createDirectoryError;
|
||||
if (![self createDirectoryAtPath:dstPath withIntermediateDirectories:YES attributes:nil error:&createDirectoryError]) {
|
||||
return [[[FBControlCoreError
|
||||
describeFormat:@"Could not create temporary directory: %@", createDirectoryError]
|
||||
causedBy:createDirectoryError]
|
||||
failFuture];
|
||||
}
|
||||
dstPath = [dstPath stringByAppendingPathComponent:[source lastPathComponent]];
|
||||
}
|
||||
// if it already exists at the destination path we should remove it before copying again
|
||||
if ([self.fileManager fileExistsAtPath:dstPath]) {
|
||||
NSError *removeError;
|
||||
if (![self removeItemAtPath:dstPath error:&removeError]) {
|
||||
return [[[FBControlCoreError
|
||||
describeFormat:@"Could not remove %@", dstPath]
|
||||
causedBy:removeError]
|
||||
failFuture];
|
||||
}
|
||||
}
|
||||
|
||||
NSError *copyError;
|
||||
if (![self copyItemAtPath:source toPath:dstPath error:©Error]) {
|
||||
return [[[FBControlCoreError
|
||||
describeFormat:@"Could not copy from %@ to %@: %@", source, dstPath, copyError]
|
||||
causedBy:copyError]
|
||||
failFuture];
|
||||
}
|
||||
return [FBFuture futureWithResult:destinationPath];
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<FBFuture<NSNull *> *> *)tail:(NSString *)containerPath toConsumer:(id<FBDataConsumer>)consumer
|
||||
{
|
||||
return [[[self
|
||||
mappedPath:containerPath]
|
||||
onQueue:self.queue fmap:^(NSString *fullSourcePath) {
|
||||
return [[[[FBProcessBuilder
|
||||
withLaunchPath:@"/usr/bin/tail"]
|
||||
withArguments:@[@"-c+1", @"-f", fullSourcePath]]
|
||||
withStdOutConsumer:consumer]
|
||||
start];
|
||||
}]
|
||||
onQueue:self.queue map:^(FBProcess *task) {
|
||||
return [task.statLoc
|
||||
onQueue:self.queue respondToCancellation:^{
|
||||
return [task sendSignal:SIGTERM backingOffToKillWithTimeout:1 logger:nil];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSNull *> *)createDirectory:(NSString *)directoryPath
|
||||
{
|
||||
return [[self
|
||||
mappedPath:directoryPath]
|
||||
onQueue:self.queue fmap:^ FBFuture<NSNull *> * (NSString *fullPath) {
|
||||
NSError *error;
|
||||
if (![self createDirectoryAtPath:fullPath withIntermediateDirectories:YES attributes:nil error:&error]) {
|
||||
return [[[FBControlCoreError
|
||||
describeFormat:@"Could not create directory %@ at container %@: %@", directoryPath, fullPath, error]
|
||||
causedBy:error]
|
||||
failFuture];
|
||||
}
|
||||
return FBFuture.empty;
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSNull *> *)moveFrom:(NSString *)sourcePath to:(NSString *)destinationPath
|
||||
{
|
||||
return [[FBFuture
|
||||
futureWithFutures:@[
|
||||
[self mappedPath:sourcePath],
|
||||
[self mappedPath:destinationPath],
|
||||
]]
|
||||
onQueue:self.queue fmap:^ FBFuture<NSNull *> * (NSArray<NSString *> *mappedPaths) {
|
||||
NSString *fullSourcePath = mappedPaths[0];
|
||||
NSString *fullDestinationPath = mappedPaths[1];
|
||||
NSError *error = nil;
|
||||
if (![self moveItemAtPath:fullSourcePath toPath:fullDestinationPath error:&error]) {
|
||||
return [[[FBControlCoreError
|
||||
describeFormat:@"Could not move item at %@ to %@: %@", fullSourcePath, fullDestinationPath, error]
|
||||
causedBy:error]
|
||||
failFuture];
|
||||
}
|
||||
return FBFuture.empty;
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSNull *> *)remove:(NSString *)path
|
||||
{
|
||||
return [[self
|
||||
mappedPath:path]
|
||||
onQueue:self.queue fmap:^ FBFuture<NSNull *> * (NSString *fullPath) {
|
||||
NSError *error;
|
||||
if (![self removeItemAtPath:fullPath error:&error]) {
|
||||
return [[[FBControlCoreError
|
||||
describeFormat:@"Could not remove item at path %@: %@", fullPath, error]
|
||||
causedBy:error]
|
||||
failFuture];
|
||||
}
|
||||
return FBFuture.empty;
|
||||
}];
|
||||
}
|
||||
|
||||
- (FBFuture<NSArray<NSString *> *> *)contentsOfDirectory:(NSString *)path
|
||||
{
|
||||
return [[self
|
||||
mappedPath:path]
|
||||
onQueue:self.queue fmap:^(NSString *fullPath) {
|
||||
NSError *error;
|
||||
NSArray<NSString *> *contents = [self contentsOfDirectoryAtPath:fullPath error:&error];
|
||||
if (!contents) {
|
||||
return [FBFuture futureWithError:error];
|
||||
}
|
||||
return [FBFuture futureWithResult:contents];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
- (FBFuture<NSString *> *)mappedPath:(NSString *)path
|
||||
{
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"-[%@ %@] must be implemented by subclasses", NSStringFromClass(self.class), NSStringFromSelector(_cmd)]
|
||||
failFuture];
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)contentsOfDirectoryAtPath:(NSString *)fullPath error:(NSError **)error
|
||||
{
|
||||
return [self.fileManager contentsOfDirectoryAtPath:fullPath error:error];
|
||||
}
|
||||
|
||||
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error
|
||||
{
|
||||
return [self.fileManager removeItemAtPath:path error:error];
|
||||
}
|
||||
|
||||
- (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error
|
||||
{
|
||||
return [self.fileManager moveItemAtPath:srcPath toPath:dstPath error:error];
|
||||
}
|
||||
|
||||
- (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error
|
||||
{
|
||||
return [self.fileManager copyItemAtPath:srcPath toPath:dstPath error:error];
|
||||
}
|
||||
|
||||
- (BOOL)createDirectoryAtPath:(NSString *)path withIntermediateDirectories:(BOOL)createIntermediates attributes:(NSDictionary<NSFileAttributeKey, id> *)attributes error:(NSError **)error
|
||||
{
|
||||
return [self.fileManager createDirectoryAtPath:path withIntermediateDirectories:createIntermediates attributes:attributes error:error];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface FBBasePathFileContainer : FBFileContainerBase
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *containerPath;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBBasePathFileContainer
|
||||
|
||||
- (instancetype)initWithContainerPath:(NSString *)containerPath queue:(dispatch_queue_t)queue
|
||||
{
|
||||
self = [super initWithQueue:queue];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_containerPath = containerPath;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (FBFuture<NSString *> *)mappedPath:(NSString *)path
|
||||
{
|
||||
return [FBFuture futureWithResult:[self.containerPath stringByAppendingPathComponent:path]];
|
||||
}
|
||||
@end
|
||||
|
||||
@interface FBMappedFileContainer : FBFileContainerBase
|
||||
|
||||
@property (nonatomic, copy, readonly) NSDictionary<NSString *, NSString *> *pathMapping;
|
||||
@property (nonatomic, copy, readonly) NSSet<NSString *> *mappedPaths;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBMappedFileContainer
|
||||
|
||||
- (instancetype)initWithPathMapping:(NSDictionary<NSString *, NSString *> *)pathMapping queue:(dispatch_queue_t)queue
|
||||
{
|
||||
self = [super initWithQueue:queue];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_pathMapping = pathMapping;
|
||||
_mappedPaths = [NSSet setWithArray:pathMapping.allValues];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (FBFuture<NSString *> *)mappedPath:(NSString *)path
|
||||
{
|
||||
NSArray<NSString *> *pathComponents = path.pathComponents;
|
||||
// If we're the root, there's nothing to map to.
|
||||
if ([self isRootPathOfContainer:pathComponents]) {
|
||||
return [FBFuture futureWithResult:path];
|
||||
}
|
||||
// Otherwise, take the first path component, which must be name of the container, so it must have a mapping.
|
||||
NSString *firstPath = pathComponents.firstObject;
|
||||
NSString *mappedPath = self.pathMapping[firstPath];
|
||||
if (!mappedPath) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"%@ is not a valid container id in %@", firstPath, [FBCollectionInformation oneLineDescriptionFromArray:self.pathMapping.allKeys]]
|
||||
failFuture];
|
||||
}
|
||||
// Re-assemble the mapped path, discarding the re-mapped first path component.
|
||||
BOOL isFirstPathComponent = YES;
|
||||
for (NSString *pathComponent in pathComponents) {
|
||||
if (isFirstPathComponent) {
|
||||
isFirstPathComponent = NO;
|
||||
continue;
|
||||
}
|
||||
mappedPath = [mappedPath stringByAppendingPathComponent:pathComponent];
|
||||
}
|
||||
return [FBFuture futureWithResult:mappedPath];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
- (NSArray<NSString *> *)contentsOfDirectoryAtPath:(NSString *)fullPath error:(NSError **)error
|
||||
{
|
||||
NSArray<NSString *> *pathComponents = fullPath.pathComponents;
|
||||
// Request for the root container, list all mapped names.
|
||||
if ([self isRootPathOfContainer:pathComponents]) {
|
||||
return self.pathMapping.allKeys;
|
||||
}
|
||||
return [super contentsOfDirectoryAtPath:fullPath error:error];
|
||||
}
|
||||
|
||||
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error
|
||||
{
|
||||
NSArray<NSString *> *pathComponents = path.pathComponents;
|
||||
if ([self isRootPathOfContainer:pathComponents]) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Cannot remove mapped container root at path %@", path]
|
||||
failBool:error];
|
||||
}
|
||||
if ([self isGroupContainerRoot:pathComponents]) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Cannot remove mapped container at path %@", path]
|
||||
failBool:error];
|
||||
}
|
||||
return [super removeItemAtPath:path error:error];
|
||||
}
|
||||
|
||||
- (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error
|
||||
{
|
||||
NSArray<NSString *> *srcPathComponents = srcPath.pathComponents;
|
||||
NSArray<NSString *> *dstPathComponents = dstPath.pathComponents;
|
||||
if ([self isRootPathOfContainer:srcPathComponents]) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Cannot move mapped container root at path %@", srcPath]
|
||||
failBool:error];
|
||||
}
|
||||
if ([self isGroupContainerRoot:srcPathComponents]) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Cannot move mapped container at path %@", srcPath]
|
||||
failBool:error];
|
||||
}
|
||||
if ([self isRootPathOfContainer:dstPathComponents]) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Cannot move to mapped container root at path %@", dstPath]
|
||||
failBool:error];
|
||||
}
|
||||
if ([self isGroupContainerRoot:dstPathComponents]) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Cannot move to mapped container at path %@", dstPath]
|
||||
failBool:error];
|
||||
}
|
||||
return [super moveItemAtPath:srcPath toPath:dstPath error:error];
|
||||
}
|
||||
|
||||
- (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error
|
||||
{
|
||||
NSArray<NSString *> *srcPathComponents = srcPath.pathComponents;
|
||||
NSArray<NSString *> *dstPathComponents = dstPath.pathComponents;
|
||||
if ([self isRootPathOfContainer:srcPathComponents]) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Cannot copy mapped container root at path %@", srcPath]
|
||||
failBool:error];
|
||||
}
|
||||
if ([self isRootPathOfContainer:dstPathComponents]) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Cannot copy to mapped container root at path %@", dstPath]
|
||||
failBool:error];
|
||||
}
|
||||
if ([self isGroupContainerRoot:dstPathComponents]) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Cannot copy to mapped container at path %@", dstPath]
|
||||
failBool:error];
|
||||
}
|
||||
return [super copyItemAtPath:srcPath toPath:dstPath error:error];
|
||||
}
|
||||
|
||||
- (BOOL)createDirectoryAtPath:(NSString *)path withIntermediateDirectories:(BOOL)createIntermediates attributes:(NSDictionary<NSFileAttributeKey, id> *)attributes error:(NSError **)error
|
||||
{
|
||||
NSArray<NSString *> *pathComponents = path.pathComponents;
|
||||
if ([self isRootPathOfContainer:pathComponents]) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Cannot create mapped container root at path %@", path]
|
||||
failBool:error];
|
||||
}
|
||||
if ([self isGroupContainerRoot:pathComponents]) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Cannot create mapped container at path %@", path]
|
||||
failBool:error];
|
||||
}
|
||||
return [super createDirectoryAtPath:path withIntermediateDirectories:createIntermediates attributes:attributes error:error];
|
||||
}
|
||||
|
||||
- (BOOL)isRootPathOfContainer:(NSArray<NSString *> *)pathComponents
|
||||
{
|
||||
// If no path components this must be the root
|
||||
if (pathComponents.count == 0) {
|
||||
return YES;
|
||||
}
|
||||
// The root is also signified by a query for the root of the container.
|
||||
NSString *firstPath = pathComponents.firstObject;
|
||||
if (pathComponents.count == 1 && ([firstPath isEqualToString:@"."] || [firstPath isEqualToString:@"/"])) {
|
||||
return YES;
|
||||
}
|
||||
// Otherwise we can't be the root path.
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isGroupContainerRoot:(NSArray<NSString *> *)pathComponents
|
||||
{
|
||||
// Re-assemble the path, confirming whether it matches with one of the mapped paths
|
||||
NSString *reassembled = [NSURL fileURLWithPathComponents:pathComponents].path;
|
||||
if ([self.mappedPaths containsObject:reassembled]) {
|
||||
return YES;
|
||||
}
|
||||
// If the canonical path does not match the known paths this can't be the group container root.
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation FBFileContainer
|
||||
|
||||
+ (id<FBFileContainer>)fileContainerForProvisioningProfileCommands:(id<FBProvisioningProfileCommands>)commands queue:(dispatch_queue_t)queue
|
||||
|
@ -523,14 +549,22 @@ FBFileContainerKind const FBFileContainerKindWallpaper = @"wallpaper";
|
|||
|
||||
+ (id<FBFileContainer>)fileContainerForBasePath:(NSString *)basePath
|
||||
{
|
||||
dispatch_queue_t queue = dispatch_queue_create("com.facebook.fbcontrolcore.file_container", DISPATCH_QUEUE_SERIAL);
|
||||
return [[FBBasePathFileContainer alloc] initWithContainerPath:basePath queue:queue];
|
||||
id<FBContainedFile> rootFile = [[FBContainedFile_Host alloc] initWithFileManager:NSFileManager.defaultManager path:basePath];
|
||||
return [self fileContainerForRootFile:rootFile];
|
||||
}
|
||||
|
||||
+ (id<FBFileContainer>)fileContainerForPathMapping:(NSDictionary<NSString *, NSString *> *)pathMapping
|
||||
{
|
||||
id<FBContainedFile> rootFile = [[FBContainedFile_Mapped_Host alloc] initWithMappingPaths:pathMapping fileManager:NSFileManager.defaultManager];
|
||||
return [self fileContainerForRootFile:rootFile];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
+ (id<FBFileContainer>)fileContainerForRootFile:(id<FBContainedFile>)root
|
||||
{
|
||||
dispatch_queue_t queue = dispatch_queue_create("com.facebook.fbcontrolcore.file_container", DISPATCH_QUEUE_SERIAL);
|
||||
return [[FBMappedFileContainer alloc] initWithPathMapping:pathMapping queue:queue];
|
||||
return [[FBContainedFile_ContainedRoot alloc] initWithRootFile:root queue:queue];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <FBControlCore/FBFuture.h>
|
||||
#import <FBControlCore/FBiOSTargetConstants.h>
|
||||
#import <FBControlCore/FBiOSTargetCommandForwarder.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Defines an interface for power related commands.
|
||||
*/
|
||||
@protocol FBLifecycleCommands <NSObject, FBiOSTargetCommand>
|
||||
|
||||
#pragma mark States
|
||||
|
||||
/**
|
||||
Asynchronously waits on the provided state.
|
||||
|
||||
@param state the state to wait on
|
||||
@return A future that resolves when it has transitioned to the given state.
|
||||
*/
|
||||
- (FBFuture<NSNull *> *)resolveState:(FBiOSTargetState)state;
|
||||
|
||||
/**
|
||||
Asynchronously waits to leave the provided state.
|
||||
|
||||
@param state the state to wait to leave
|
||||
@return A future that resolves when it has transitioned away from the given state.
|
||||
*/
|
||||
- (FBFuture<NSNull *> *)resolveLeavesState:(FBiOSTargetState)state;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -60,14 +60,14 @@
|
|||
[statLocFuture resolveWithResult:@(statLoc)];
|
||||
if (WIFSIGNALED(statLoc)) {
|
||||
int signalCode = WTERMSIG(statLoc);
|
||||
NSString *message = [NSString stringWithFormat:@"Process %d (%@) died with signal %d", processIdentifier, configuration.processName, signalCode];
|
||||
NSString *message = [NSString stringWithFormat:@"Process %d (%@) exited with signal %d", processIdentifier, configuration.processName, signalCode];
|
||||
[logger log:message];
|
||||
NSError *error = [[FBControlCoreError describe:message] build];
|
||||
[exitCodeFuture resolveWithError:error];
|
||||
[signalFuture resolveWithResult:@(signalCode)];
|
||||
} else {
|
||||
int exitCode = WEXITSTATUS(statLoc);
|
||||
NSString *message = [NSString stringWithFormat:@"Process %d (%@) died with exit code %d", processIdentifier, configuration.processName, exitCode];
|
||||
NSString *message = [NSString stringWithFormat:@"Process %d (%@) exited with code %d", processIdentifier, configuration.processName, exitCode];
|
||||
[logger log:message];
|
||||
NSError *error = [[FBControlCoreError describe:message] build];
|
||||
[signalFuture resolveWithError:error];
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -10,16 +10,16 @@
|
|||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
An Enumeration Representing Approval of Services.
|
||||
An Enumeration Representing Services.
|
||||
*/
|
||||
typedef NSString *FBSettingsApprovalService NS_STRING_ENUM;
|
||||
typedef NSString *FBTargetSettingsService NS_STRING_ENUM;
|
||||
|
||||
extern FBSettingsApprovalService const FBSettingsApprovalServiceContacts;
|
||||
extern FBSettingsApprovalService const FBSettingsApprovalServicePhotos;
|
||||
extern FBSettingsApprovalService const FBSettingsApprovalServiceCamera;
|
||||
extern FBSettingsApprovalService const FBSettingsApprovalServiceLocation;
|
||||
extern FBSettingsApprovalService const FBSettingsApprovalServiceMicrophone;
|
||||
extern FBSettingsApprovalService const FBSettingsApprovalServiceUrl;
|
||||
extern FBSettingsApprovalService const FBSettingsApprovalServiceNotification;
|
||||
extern FBTargetSettingsService const FBTargetSettingsServiceContacts;
|
||||
extern FBTargetSettingsService const FBTargetSettingsServicePhotos;
|
||||
extern FBTargetSettingsService const FBTargetSettingsServiceCamera;
|
||||
extern FBTargetSettingsService const FBTargetSettingsServiceLocation;
|
||||
extern FBTargetSettingsService const FBTargetSettingsServiceMicrophone;
|
||||
extern FBTargetSettingsService const FBTargetSettingsServiceUrl;
|
||||
extern FBTargetSettingsService const FBTargetSettingsServiceNotification;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -7,10 +7,10 @@
|
|||
|
||||
#import "FBSettingsCommands.h"
|
||||
|
||||
FBSettingsApprovalService const FBSettingsApprovalServiceContacts = @"contacts";
|
||||
FBSettingsApprovalService const FBSettingsApprovalServicePhotos = @"photos";
|
||||
FBSettingsApprovalService const FBSettingsApprovalServiceCamera = @"camera";
|
||||
FBSettingsApprovalService const FBSettingsApprovalServiceLocation = @"location";
|
||||
FBSettingsApprovalService const FBSettingsApprovalServiceMicrophone = @"microphone";
|
||||
FBSettingsApprovalService const FBSettingsApprovalServiceUrl = @"url";
|
||||
FBSettingsApprovalService const FBSettingsApprovalServiceNotification = @"notification";
|
||||
FBTargetSettingsService const FBTargetSettingsServiceContacts = @"contacts";
|
||||
FBTargetSettingsService const FBTargetSettingsServicePhotos = @"photos";
|
||||
FBTargetSettingsService const FBTargetSettingsServiceCamera = @"camera";
|
||||
FBTargetSettingsService const FBTargetSettingsServiceLocation = @"location";
|
||||
FBTargetSettingsService const FBTargetSettingsServiceMicrophone = @"microphone";
|
||||
FBTargetSettingsService const FBTargetSettingsServiceUrl = @"url";
|
||||
FBTargetSettingsService const FBTargetSettingsServiceNotification = @"notification";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <FBControlCore/FBArchitecture.h>
|
||||
#import <FBControlCore/FBFuture.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class FBProcessSpawnConfiguration;
|
||||
|
||||
@interface FBArchitectureProcessAdapter : NSObject
|
||||
|
||||
/// Force binaries to be launched in desired architectures.
|
||||
///
|
||||
/// Convenience method for `-[FBArchitectureProcessAdapter adaptProcessConfiguration:toAnyArchitectureIn:hostArchitectures:queue:temporaryDirectory:]`
|
||||
-(FBFuture<FBProcessSpawnConfiguration *> *)adaptProcessConfiguration:(FBProcessSpawnConfiguration *)processConfiguration toAnyArchitectureIn:(NSSet<FBArchitecture> *)requestedArchitectures queue:(dispatch_queue_t)queue temporaryDirectory:(NSURL *)temporaryDirectory;
|
||||
|
||||
/// Force binaries to be launched in desired architectures.
|
||||
///
|
||||
/// Up to Xcode 14.2, subprocesses were spawned in the same architecture as parent process by default.
|
||||
/// But from Xcode 14.3, subprocesses are spawned in arm64 when running on a arm64, regardless of parent process architecture.
|
||||
/// To force subprocess being spawned in other architecture, there is `arch` utility that does not work in simulator context.
|
||||
///
|
||||
/// As a workaround, to bring predictability into which architecture spawned process will be spawned,
|
||||
/// we lipo-thin the executable to an architecture supported by the host machine.
|
||||
///
|
||||
/// The selection of the final architecture is done by comparing consiliating the architectures idb companion needs
|
||||
/// the process to run with (often dictactated by the architecture of the binary code we want to inject into
|
||||
/// the spawned process) and the architectures supported by the processor of the host machine.
|
||||
///
|
||||
/// As an example, on an arm64 machine, when idb companion needs to inject an x86_64 lib into a process that could
|
||||
/// run in either x86_64 or arm64, the target process needs to be thinned down to `x86_64` to ensure it runs in the
|
||||
/// same of the lib that needs to be injected.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - processConfiguration: Initial process configuration
|
||||
/// - toAnyArchitectureIn: Set of architectures the process needs to be spawned with. `arm64` will take precedence over `x86_64`
|
||||
/// - queue: Target Queue
|
||||
/// - hostArchitectures: Set of architectures supported by the host machine
|
||||
/// - temporaryDirectory: Target directory where we put lipoed binary
|
||||
-(FBFuture<FBProcessSpawnConfiguration *> *)adaptProcessConfiguration:(FBProcessSpawnConfiguration *)processConfiguration toAnyArchitectureIn:(NSSet<FBArchitecture> *)architectures hostArchitectures:(NSSet<FBArchitecture> *)hostArchitectures queue:(dispatch_queue_t)queue temporaryDirectory:(NSURL *)temporaryDirectory;
|
||||
|
||||
|
||||
/// Returns supported architectures based on companion launch architecture and launch under rosetta determination.
|
||||
+(NSSet<FBArchitecture> *)hostMachineSupportedArchitectures;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "FBArchitectureProcessAdapter.h"
|
||||
|
||||
#import "FBProcessSpawnConfiguration.h"
|
||||
#import "FBCollectionInformation.h"
|
||||
#import "FBArchitecture.h"
|
||||
#import "FBProcessBuilder.h"
|
||||
#import "FBFuture.h"
|
||||
#import "FBControlCoreError.h"
|
||||
#import <mach-o/arch.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
|
||||
// https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment#Determine-Whether-Your-App-Is-Running-as-a-Translated-Binary
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
static int processIsTranslated(void)
|
||||
{
|
||||
int ret = 0;
|
||||
size_t size = sizeof(ret);
|
||||
// patternlint-disable-next-line prefer-metasystemcontrol-byname
|
||||
if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
@implementation FBArchitectureProcessAdapter
|
||||
|
||||
-(FBFuture<FBProcessSpawnConfiguration *> *)adaptProcessConfiguration:(FBProcessSpawnConfiguration *)processConfiguration toAnyArchitectureIn:(NSSet<FBArchitecture> *)requestedArchitectures queue:(dispatch_queue_t)queue temporaryDirectory:(NSURL *)temporaryDirectory {
|
||||
return [self adaptProcessConfiguration:processConfiguration toAnyArchitectureIn:requestedArchitectures hostArchitectures:[FBArchitectureProcessAdapter hostMachineSupportedArchitectures] queue:queue temporaryDirectory:temporaryDirectory];
|
||||
}
|
||||
|
||||
- (nullable FBArchitecture)selectArchitectureFrom:(NSSet<FBArchitecture> *)requestedArchitectures supportedArchitectures:(NSSet<FBArchitecture> *)supportedArchitectures
|
||||
{
|
||||
|
||||
if([requestedArchitectures containsObject:FBArchitectureArm64] && [supportedArchitectures containsObject:FBArchitectureArm64]) {
|
||||
return FBArchitectureArm64;
|
||||
}
|
||||
|
||||
if([requestedArchitectures containsObject:FBArchitectureX86_64] && [supportedArchitectures containsObject:FBArchitectureX86_64]) {
|
||||
return FBArchitectureX86_64;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
-(FBFuture<FBProcessSpawnConfiguration *> *)adaptProcessConfiguration:(FBProcessSpawnConfiguration *)processConfiguration toAnyArchitectureIn:(NSSet<FBArchitecture> *)requestedArchitectures hostArchitectures:(NSSet<FBArchitecture> *)hostArchitectures queue:(dispatch_queue_t)queue temporaryDirectory:(NSURL *)temporaryDirectory {
|
||||
FBArchitecture architecture = [self selectArchitectureFrom:requestedArchitectures supportedArchitectures:hostArchitectures];
|
||||
if (!architecture) {
|
||||
return [[FBControlCoreError
|
||||
describeFormat:@"Could not select an architecture from %@ compatible with %@", [FBCollectionInformation oneLineDescriptionFromArray:requestedArchitectures.allObjects], [FBCollectionInformation oneLineDescriptionFromArray:hostArchitectures.allObjects]
|
||||
] failFuture];
|
||||
}
|
||||
|
||||
return [[[self verifyArchitectureAvailable:processConfiguration.launchPath architecture:architecture queue:queue]
|
||||
onQueue:queue fmap:^FBFuture *(NSNull * _) {
|
||||
NSString *fileName = [[[[processConfiguration.launchPath lastPathComponent] stringByAppendingString:[[NSUUID new] UUIDString]] stringByAppendingString:@"."] stringByAppendingString:architecture];
|
||||
NSURL *filePath = [temporaryDirectory URLByAppendingPathComponent:fileName isDirectory:NO];
|
||||
return [[self extractArchitecture:architecture processConfiguration:processConfiguration queue:queue outputPath:filePath] mapReplace:[filePath path]];
|
||||
}]
|
||||
onQueue:queue fmap:^FBFuture *(NSString *extractedBinary) {
|
||||
return [[self getFixedupDyldFrameworkPathFromOriginalBinary:processConfiguration.launchPath queue:queue]
|
||||
onQueue:queue map:^FBProcessSpawnConfiguration *(NSString *dyldFrameworkPath) {
|
||||
NSMutableDictionary<NSString *, NSString *> *updatedEnvironment = [processConfiguration.environment mutableCopy];
|
||||
// DYLD_FRAMEWORK_PATH adds additional search paths for required "*.framewosk"s in binary
|
||||
// DYLD_LIBRARY_PATH adds additional search paths for required "*.dyld"s in binary
|
||||
[updatedEnvironment setValue:dyldFrameworkPath forKey:@"DYLD_FRAMEWORK_PATH"];
|
||||
[updatedEnvironment setValue:dyldFrameworkPath forKey:@"DYLD_LIBRARY_PATH"];
|
||||
return [[FBProcessSpawnConfiguration alloc] initWithLaunchPath:extractedBinary arguments:processConfiguration.arguments environment:updatedEnvironment io:processConfiguration.io mode:processConfiguration.mode];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
/// Verifies that we can extract desired architecture from binary
|
||||
-(FBFuture<NSNull *> *)verifyArchitectureAvailable:(NSString *)binary architecture:(FBArchitecture)architecture queue:(dispatch_queue_t)queue {
|
||||
return [[[[[[[FBProcessBuilder
|
||||
withLaunchPath:@"/usr/bin/lipo" arguments:@[binary, @"-verify_arch", architecture]]
|
||||
withStdOutToDevNull]
|
||||
withStdErrToDevNull]
|
||||
runUntilCompletionWithAcceptableExitCodes:[NSSet setWithObject:@0]]
|
||||
rephraseFailure:@"Desired architecture %@ not found in %@ binary", architecture, binary]
|
||||
mapReplace:[NSNull null]]
|
||||
timeout:10 waitingFor:@"lipo -verify_arch"];
|
||||
}
|
||||
|
||||
-(FBFuture<NSString *> *)extractArchitecture:(FBArchitecture)architecture processConfiguration:(FBProcessSpawnConfiguration *)processConfiguration queue:(dispatch_queue_t)queue outputPath:(NSURL *)outputPath {
|
||||
return [[[[[[[FBProcessBuilder
|
||||
withLaunchPath:@"/usr/bin/lipo" arguments:@[processConfiguration.launchPath, @"-extract", architecture, @"-output", [outputPath path]]]
|
||||
withStdOutToDevNull]
|
||||
withStdErrLineReader:^(NSString * _Nonnull line) {
|
||||
NSLog(@"LINE %@\n", line);
|
||||
}]
|
||||
runUntilCompletionWithAcceptableExitCodes:[NSSet setWithObject:@0]]
|
||||
rephraseFailure:@"Failed to thin %@ architecture out from %@ binary", architecture, processConfiguration.launchPath]
|
||||
mapReplace:[NSNull null]]
|
||||
timeout:10 waitingFor:@"lipo -extract"];
|
||||
}
|
||||
|
||||
/// After we lipoed out arch from binary, new binary placed into temporary folder.
|
||||
/// That makes all dynamic library imports become incorrect. To fix that up we
|
||||
/// have to specify `DYLD_FRAMEWORK_PATH` correctly.
|
||||
-(FBFuture<NSString *> *)getFixedupDyldFrameworkPathFromOriginalBinary:(NSString *)binary queue:(dispatch_queue_t)queue {
|
||||
NSString *binaryFolder = [[binary stringByResolvingSymlinksInPath] stringByDeletingLastPathComponent];
|
||||
return [[[self getOtoolInfoFromBinary:binary queue:queue]
|
||||
onQueue:queue map:^NSSet<NSString *> *(NSString *result) {
|
||||
return [self extractRpathsFromOtoolOutput:result];
|
||||
}]
|
||||
onQueue:queue map:^NSString *(NSSet<NSString *> *result) {
|
||||
NSMutableArray<NSString *> *rpaths = [NSMutableArray new];
|
||||
for (NSString *binaryRpath in result) {
|
||||
if ([binaryRpath hasPrefix:@"@executable_path"]) {
|
||||
[rpaths addObject:[binaryRpath stringByReplacingOccurrencesOfString:@"@executable_path" withString:binaryFolder]];
|
||||
}
|
||||
}
|
||||
return [rpaths componentsJoinedByString:@":"];
|
||||
}];
|
||||
}
|
||||
|
||||
-(FBFuture<NSString *> *)getOtoolInfoFromBinary:(NSString *)binary queue:(dispatch_queue_t)queue {
|
||||
return [[[[[[[FBProcessBuilder
|
||||
withLaunchPath:@"/usr/bin/otool" arguments:@[@"-l", binary]]
|
||||
withStdOutInMemoryAsString]
|
||||
withStdErrToDevNull]
|
||||
runUntilCompletionWithAcceptableExitCodes:[NSSet setWithObject:@0]]
|
||||
rephraseFailure:@"Failed query otool -l from %@", binary]
|
||||
onQueue:queue fmap:^FBFuture<NSString *>*(FBProcess<NSNull *, NSString *, NSString *> *task) {
|
||||
if (task.stdOut) {
|
||||
return [FBFuture futureWithResult: task.stdOut];
|
||||
}
|
||||
return [[FBControlCoreError describeFormat:@"Failed to call otool -l over %@", binary] failFuture];
|
||||
}]
|
||||
timeout:10 waitingFor:@"otool -l"];
|
||||
}
|
||||
|
||||
/// Extracts rpath from full otool output
|
||||
/// Each `LC_RPATH` entry like
|
||||
/// ```
|
||||
/// Load command 19
|
||||
/// cmd LC_RPATH
|
||||
/// cmdsize 48
|
||||
/// path @executable_path/../../Frameworks/ (offset 12)
|
||||
/// ```
|
||||
/// transforms to
|
||||
/// ```
|
||||
/// @executable_path/../../Frameworks/
|
||||
/// ```
|
||||
-(NSSet<NSString *> *)extractRpathsFromOtoolOutput:(NSString *)otoolOutput {
|
||||
NSArray<NSString *> *lines = [otoolOutput componentsSeparatedByString: @"\n"];
|
||||
NSMutableSet<NSString *> *result = [NSMutableSet new];
|
||||
|
||||
// Rpath entry looks like:
|
||||
// ```
|
||||
// Load command 19
|
||||
// cmd LC_RPATH
|
||||
// cmdsize 48
|
||||
// path @executable_path/../../Frameworks/ (offset 12)
|
||||
// ```
|
||||
// So if we found occurence of `cmd LC_RPATH` rpath value will be two lines below.
|
||||
NSUInteger lcRpathValueOffset = 2;
|
||||
|
||||
[lines enumerateObjectsUsingBlock:^(NSString *line, NSUInteger index, BOOL *_) {
|
||||
if ([self isLcPathDefinitionLine:line] && index + lcRpathValueOffset < lines.count) {
|
||||
NSString *rpathLine = lines[index + lcRpathValueOffset];
|
||||
|
||||
NSString *rpath = [self extractRpathValueFromLine:rpathLine];
|
||||
if (rpath) {
|
||||
[result addObject:rpath];
|
||||
}
|
||||
}
|
||||
}];
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Checking for `LC_RPATH` in load commands
|
||||
/// - Parameter line: Single line entry for otool output
|
||||
-(bool)isLcPathDefinitionLine:(NSString *)line {
|
||||
bool hasCMD = false;
|
||||
bool hasLcRpath = false;
|
||||
for (NSString *component in [line componentsSeparatedByString: @" "]) {
|
||||
if ([component isEqualToString:@"cmd"]) {
|
||||
hasCMD = true;
|
||||
} else if ([component isEqualToString:@"LC_RPATH"]) {
|
||||
hasLcRpath = true;
|
||||
}
|
||||
}
|
||||
return (hasCMD && hasLcRpath);
|
||||
}
|
||||
|
||||
// Note: spaces in path names are not available. Currently we use adapter for binaries
|
||||
// inside Xcode that has relative paths to original binary.
|
||||
// And there is no spaces in paths over there.
|
||||
-(nullable NSString *)extractRpathValueFromLine:(NSString *)line {
|
||||
for (NSString *component in [line componentsSeparatedByString: @" "]) {
|
||||
if ([component hasPrefix:@"@executable_path"]) {
|
||||
return component;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+(NSSet<FBArchitecture> *)hostMachineSupportedArchitectures {
|
||||
#if TARGET_CPU_X86_64
|
||||
int isTranslated = processIsTranslated();
|
||||
if (isTranslated == 1) {
|
||||
// Companion running as x86_64 with translation (Rosetta) -> Processor supports Arm64 and x86_64
|
||||
return [NSSet setWithArray:@[FBArchitectureArm64, FBArchitectureX86_64]];
|
||||
} else {
|
||||
// Companion running as x86_64 and translation is disabled or unknown
|
||||
// Assuming processor only supports x86_64 even if translation state is unknown
|
||||
return [NSSet setWithArray:@[FBArchitectureX86_64]];
|
||||
}
|
||||
#else
|
||||
return [NSSet setWithArray:@[FBArchitectureArm64, FBArchitectureX86_64]];
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -21,7 +21,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
/**
|
||||
The Designated Initializer
|
||||
*/
|
||||
- (instancetype)initWithTestBundle:(FBBundleDescriptor *)testBundle applicationLaunchConfiguration:(FBApplicationLaunchConfiguration *)applicationLaunchConfiguration testHostBundle:(nullable FBBundleDescriptor *)testHostBundle timeout:(NSTimeInterval)timeout initializeUITesting:(BOOL)initializeUITesting useXcodebuild:(BOOL)useXcodebuild testsToRun:(nullable NSSet<NSString *> *)testsToRun testsToSkip:(nullable NSSet<NSString *> *)testsToSkip targetApplicationBundle:(nullable FBBundleDescriptor *)targetApplicationBundle xcTestRunProperties:(nullable NSDictionary *)xcTestRunProperties resultBundlePath:(nullable NSString *)resultBundlePath reportActivities:(BOOL)reportActivities coverageDirectoryPath:(nullable NSString *)coverageDirectoryPath logDirectoryPath:(nullable NSString *)logDirectoryPath;
|
||||
- (instancetype)initWithTestBundle:(FBBundleDescriptor *)testBundle applicationLaunchConfiguration:(FBApplicationLaunchConfiguration *)applicationLaunchConfiguration testHostBundle:(nullable FBBundleDescriptor *)testHostBundle timeout:(NSTimeInterval)timeout initializeUITesting:(BOOL)initializeUITesting useXcodebuild:(BOOL)useXcodebuild testsToRun:(nullable NSSet<NSString *> *)testsToRun testsToSkip:(nullable NSSet<NSString *> *)testsToSkip targetApplicationBundle:(nullable FBBundleDescriptor *)targetApplicationBundle xcTestRunProperties:(nullable NSDictionary *)xcTestRunProperties resultBundlePath:(nullable NSString *)resultBundlePath reportActivities:(BOOL)reportActivities coverageDirectoryPath:(nullable NSString *)coverageDirectoryPath enableContinuousCoverageCollection:(BOOL)enableContinuousCoverageCollection logDirectoryPath:(nullable NSString *)logDirectoryPath reportResultBundle:(BOOL)reportResultBundle;
|
||||
|
||||
/**
|
||||
XCTest bundle used for testing
|
||||
|
@ -88,11 +88,21 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
*/
|
||||
@property (nonatomic, copy, readonly, nullable) NSString *coverageDirectoryPath;
|
||||
|
||||
/**
|
||||
Determines whether should enable continuous coverage collection
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) BOOL shouldEnableContinuousCoverageCollection;
|
||||
|
||||
/**
|
||||
The Directory to use for storing logs generated during the execution of the test run.
|
||||
*/
|
||||
@property (nonatomic, nullable, copy, readonly) NSString *logDirectoryPath;
|
||||
|
||||
/*
|
||||
Path to the result bundle.
|
||||
*/
|
||||
@property (nonatomic, assign, readonly) BOOL reportResultBundle;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -17,7 +17,7 @@
|
|||
|
||||
@implementation FBTestLaunchConfiguration
|
||||
|
||||
- (instancetype)initWithTestBundle:(FBBundleDescriptor *)testBundle applicationLaunchConfiguration:(FBApplicationLaunchConfiguration *)applicationLaunchConfiguration testHostBundle:(nullable FBBundleDescriptor *)testHostBundle timeout:(NSTimeInterval)timeout initializeUITesting:(BOOL)initializeUITesting useXcodebuild:(BOOL)useXcodebuild testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip targetApplicationBundle:(nullable FBBundleDescriptor *)targetApplicationBundle xcTestRunProperties:(NSDictionary *)xcTestRunProperties resultBundlePath:(NSString *)resultBundlePath reportActivities:(BOOL)reportActivities coverageDirectoryPath:(NSString *)coverageDirectoryPath logDirectoryPath:(nullable NSString *)logDirectoryPath
|
||||
- (instancetype)initWithTestBundle:(FBBundleDescriptor *)testBundle applicationLaunchConfiguration:(FBApplicationLaunchConfiguration *)applicationLaunchConfiguration testHostBundle:(nullable FBBundleDescriptor *)testHostBundle timeout:(NSTimeInterval)timeout initializeUITesting:(BOOL)initializeUITesting useXcodebuild:(BOOL)useXcodebuild testsToRun:(NSSet<NSString *> *)testsToRun testsToSkip:(NSSet<NSString *> *)testsToSkip targetApplicationBundle:(nullable FBBundleDescriptor *)targetApplicationBundle xcTestRunProperties:(NSDictionary *)xcTestRunProperties resultBundlePath:(NSString *)resultBundlePath reportActivities:(BOOL)reportActivities coverageDirectoryPath:(NSString *)coverageDirectoryPath enableContinuousCoverageCollection:(BOOL)enableContinuousCoverageCollection logDirectoryPath:(nullable NSString *)logDirectoryPath reportResultBundle:(BOOL)reportResultBundle
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
|
@ -37,7 +37,9 @@
|
|||
_resultBundlePath = resultBundlePath;
|
||||
_reportActivities = reportActivities;
|
||||
_coverageDirectoryPath = coverageDirectoryPath;
|
||||
_shouldEnableContinuousCoverageCollection = enableContinuousCoverageCollection;
|
||||
_logDirectoryPath = logDirectoryPath;
|
||||
_reportResultBundle = reportResultBundle;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
@ -70,18 +72,20 @@
|
|||
(self.xcTestRunProperties == configuration.xcTestRunProperties || [self.xcTestRunProperties isEqual:configuration.xcTestRunProperties]) &&
|
||||
(self.resultBundlePath == configuration.resultBundlePath || [self.resultBundlePath isEqual:configuration.resultBundlePath]) &&
|
||||
(self.coverageDirectoryPath == configuration.coverageDirectoryPath || [self.coverageDirectoryPath isEqualToString:configuration.coverageDirectoryPath]) &&
|
||||
(self.logDirectoryPath == configuration.logDirectoryPath || [self.logDirectoryPath isEqualToString:configuration.logDirectoryPath]);
|
||||
(self.shouldEnableContinuousCoverageCollection == configuration.shouldEnableContinuousCoverageCollection) &&
|
||||
(self.logDirectoryPath == configuration.logDirectoryPath || [self.logDirectoryPath isEqualToString:configuration.logDirectoryPath]) &&
|
||||
self.reportResultBundle == configuration.reportResultBundle;
|
||||
}
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
return self.testBundle.hash ^ self.applicationLaunchConfiguration.hash ^ self.testHostBundle.hash ^ (unsigned long) self.timeout ^ (unsigned long) self.shouldInitializeUITesting ^ (unsigned long) self.shouldUseXcodebuild ^ self.testsToRun.hash ^ self.testsToSkip.hash ^ self.targetApplicationBundle.hash ^ self.xcTestRunProperties.hash ^ self.resultBundlePath.hash ^ self.coverageDirectoryPath.hash ^ self.logDirectoryPath.hash;
|
||||
return self.testBundle.hash ^ self.applicationLaunchConfiguration.hash ^ self.testHostBundle.hash ^ (unsigned long) self.timeout ^ (unsigned long) self.shouldInitializeUITesting ^ (unsigned long) self.shouldUseXcodebuild ^ self.testsToRun.hash ^ self.testsToSkip.hash ^ self.targetApplicationBundle.hash ^ self.xcTestRunProperties.hash ^ self.resultBundlePath.hash ^ self.coverageDirectoryPath.hash ^ (unsigned long) self.shouldEnableContinuousCoverageCollection ^ self.logDirectoryPath.hash;
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:
|
||||
@"FBTestLaunchConfiguration TestBundle %@ | AppConfig %@ | HostBundle %@ | UITesting %d | UseXcodebuild %d | TestsToRun %@ | TestsToSkip %@ | Target application bundle %@ xcTestRunProperties %@ | ResultBundlePath %@ | CoverageDirPath %@ | LogDirectoryPath %@" ,
|
||||
@"FBTestLaunchConfiguration TestBundle %@ | AppConfig %@ | HostBundle %@ | UITesting %d | UseXcodebuild %d | TestsToRun %@ | TestsToSkip %@ | Target application bundle %@ xcTestRunProperties %@ | ResultBundlePath %@ | CoverageDirPath %@ | EnableContinuousCoverageCollection %d | LogDirectoryPath %@ | ReportResultBundle %d" ,
|
||||
self.testBundle,
|
||||
self.applicationLaunchConfiguration,
|
||||
self.testHostBundle,
|
||||
|
@ -93,7 +97,9 @@
|
|||
self.xcTestRunProperties,
|
||||
self.resultBundlePath,
|
||||
self.coverageDirectoryPath,
|
||||
self.logDirectoryPath
|
||||
self.shouldEnableContinuousCoverageCollection,
|
||||
self.logDirectoryPath,
|
||||
self.reportResultBundle
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
|
@ -31,7 +31,7 @@ extern FBVideoStreamEncoding const FBVideoStreamEncodingMinicap;
|
|||
@param compressionQuality the compression quality to use.
|
||||
@param scaleFactor the scale factor, between 0-1. nil for no scaling.
|
||||
*/
|
||||
- (instancetype)initWithEncoding:(FBVideoStreamEncoding)encoding framesPerSecond:(nullable NSNumber *)framesPerSecond compressionQuality:(nullable NSNumber *)compressionQuality scaleFactor:(nullable NSNumber *)scaleFactor;
|
||||
- (instancetype)initWithEncoding:(FBVideoStreamEncoding)encoding framesPerSecond:(nullable NSNumber *)framesPerSecond compressionQuality:(nullable NSNumber *)compressionQuality scaleFactor:(nullable NSNumber *)scaleFactor avgBitrate:(nullable NSNumber *)avgBitrate keyFrameRate:(nullable NSNumber *)keyFrameRate;
|
||||
|
||||
/**
|
||||
The encoding of the stream.
|
||||
|
@ -54,6 +54,16 @@ extern FBVideoStreamEncoding const FBVideoStreamEncodingMinicap;
|
|||
*/
|
||||
@property (nonatomic, copy, nullable, readonly) NSNumber *scaleFactor;
|
||||
|
||||
/**
|
||||
Average bitrate.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable, readonly) NSNumber *avgBitrate;
|
||||
|
||||
/**
|
||||
Send a key frame every N seconds.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable, readonly) NSNumber *keyFrameRate;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче