Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for custom Xcode app path #55

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Sources/HotReloading/InjectionClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ public class InjectionClient: SimpleSocket, InjectionReader {
builder.derivedLogs = nil;
case .watching:
log("Watching files under the directory \(readString() ?? "Missing directory")")
case .xcodeAppPath:
let xcodeAppPath = readString()
log("Xcode app path set at \(xcodeAppPath ?? "Missing path")")
if let xcodeAppPath = xcodeAppPath {
builder.xcodeAppPath = xcodeAppPath
}
case .log:
log(readString() ?? "Missing log message")
case .ideProcPath:
Expand Down
5 changes: 5 additions & 0 deletions Sources/HotReloading/SwiftEval.swift
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,11 @@ public class SwiftEval: NSObject {
}

// Xcode related info
@objc public var xcodeAppPath: String = "/Applications/Xcode.app" {
didSet {
xcodeDev = xcodeAppPath + "/Contents/Developer"
}
}
@objc public var xcodeDev = "/Applications/Xcode.app/Contents/Developer"

@objc public var projectFile: String?
Expand Down
1 change: 1 addition & 0 deletions Sources/HotReloadingGuts/include/InjectionClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ typedef NS_ENUM(int, InjectionCommand) {
// commands to Bundle
InjectionConnected,
InjectionWatching,
InjectionXcodeAppPath,
InjectionLog,
InjectionSigned,
InjectionLoad,
Expand Down
1 change: 1 addition & 0 deletions Sources/HotReloadingGuts/include/UserDefaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ static NSString *const UserDefaultsLookup = @"typeLookup";
static NSString *const UserDefaultsRemote = @"appRemote";
static NSString *const UserDefaultsReplay = @"replayInjections";
static NSString *const UserDefaultsUnlock = @"deviceUnlock";
static NSString *const UserDefaultsXcodeAppPath = @"xcodeAppPath";
39 changes: 33 additions & 6 deletions Sources/injectiond/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,27 +54,36 @@ class AppDelegate : NSObject, NSApplicationDelegate {
@IBOutlet var statusItem: NSStatusItem!

var watchedDirectories = Set<String>()
private(set) var xcodeAppPath: String = "/Applications/Xcode.app"
weak var lastConnection: InjectionServer?
var selectedProject: String?
let openProject = NSLocalizedString("Select Project Directory",
tableName: "Project Directory",
comment: "Project Directory")
let openXcode = NSLocalizedString("Select Xcode app",
comment: "Xcode Directory")

@objc let defaults = UserDefaults.standard
var defaultsMap: [NSMenuItem: String]!

lazy var isSandboxed =
ProcessInfo.processInfo.environment["APP_SANDBOX_CONTAINER_ID"] != nil
var runningXcodeDevURL: URL? =
NSRunningApplication.runningApplications(
withBundleIdentifier: XcodeBundleID).first?
.bundleURL?.appendingPathComponent("Contents/Developer")
var runningXcodeAppURL: URL? = NSRunningApplication.runningApplications(
withBundleIdentifier: XcodeBundleID).first?
.bundleURL
var runningXcodeDevURL: URL? {
return runningXcodeAppURL?.appendingPathComponent("Contents/Developer")
}
var derivedLogs: String?

@objc func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
appDelegate = self

if let path = defaults.string(forKey: UserDefaultsXcodeAppPath) {
xcodeAppPath = path
}

let statusBar = NSStatusBar.system
statusItem = statusBar.statusItem(withLength: statusBar.thickness)
statusItem.highlightMode = true
Expand Down Expand Up @@ -119,17 +128,18 @@ class AppDelegate : NSObject, NSApplicationDelegate {
#if !SWIFT_PACKAGE
InjectionServer.startServer(INJECTION_ADDRESS)
if !FileManager.default.fileExists(atPath:
"/Applications/Xcode.app/Contents/Developer") {
"\(xcodeAppPath)/Contents/Developer") {
let alert: NSAlert = NSAlert()
alert.messageText = "Missing Xcode at required location"
alert.informativeText = """
Xcode.app not found at path /Applications/Xcode.app. \
Xcode.app not found at path \(xcodeAppPath). \
You may need to have an Xcode at this location to be able \
to use InjectionIII. A symbolic link at that path is fine.
"""
alert.alertStyle = .critical
alert.addButton(withTitle: "OK")
_ = alert.runModal()
selectXcode()
}
#endif

Expand Down Expand Up @@ -399,6 +409,23 @@ class AppDelegate : NSObject, NSApplicationDelegate {
}
}

@IBAction func selectXcode(_ sender: Any) {
selectXcode()
}

private func selectXcode() {
let open = NSOpenPanel()
open.prompt = openXcode
open.allowsMultipleSelection = false
open.canChooseDirectories = false
open.canChooseFiles = true
open.directoryURL = URL(fileURLWithPath: "/Applications")
guard open.runModal() == .OK, let url = open.urls.first else { return }
xcodeAppPath = url.path
lastConnection?.setXcodeAppPath(xcodeAppPath)
defaults.set(xcodeAppPath, forKey: UserDefaultsXcodeAppPath)
}

@IBAction func openProject(_ sender: Any) {
_ = application(NSApp, openFile: "")
}
Expand Down
14 changes: 11 additions & 3 deletions Sources/injectiond/InjectionServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,18 +122,22 @@ public class InjectionServer: SimpleSocket {
self.log("Signing with identity: \(identity!)")
}
return SignerService.codesignDylib(
self.builder.tmpDir+"/eval"+$0, identity: identity)
self.builder.tmpDir+"/eval"+$0,
identity: identity,
xcodePath: appDelegate.xcodeAppPath
)
}

// Xcode specific config
if let xcodeDevURL = appDelegate.runningXcodeDevURL {
builder.xcodeDev = xcodeDevURL.path
if let xcodeAppURL = appDelegate.runningXcodeAppURL {
builder.xcodeAppPath = xcodeAppURL.path
}

builder.projectFile = projectFile

appDelegate.setMenuIcon(.ok)
appDelegate.lastConnection = self
setXcodeAppPath(appDelegate.xcodeAppPath)
pending = []

var lastInjected = projectInjected[projectFile]
Expand Down Expand Up @@ -345,6 +349,10 @@ public class InjectionServer: SimpleSocket {
sendCommand(.watching, with: directory)
}

public func setXcodeAppPath(_ path: String) {
sendCommand(.xcodeAppPath, with: path)
}

@objc public func injectPending() {
for swiftSource in pending {
recompileAndInject(source: swiftSource)
Expand Down
15 changes: 11 additions & 4 deletions Sources/injectiondGuts/SignerService.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,19 @@

@implementation SignerService

+ (BOOL)codesignDylib:(NSString *)dylib identity:(NSString *)identity {
+ (BOOL)codesignDylib:(NSString *)dylib identity:(NSString *)identity xcodePath:(NSString *)xcodePath {
static NSString *adhocSign = @"-";
const char *envIdentity = getenv("EXPANDED_CODE_SIGN_IDENTITY")
?: getenv("CODE_SIGN_IDENTITY");
const char *toolchainDir = getenv("TOOLCHAIN_DIR") ?:
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain";
const char *toolchainDir = nil;
if (getenv("TOOLCHAIN_DIR")) {
toolchainDir = getenv("TOOLCHAIN_DIR");
} else if (xcodePath) {
toolchainDir = [[NSString stringWithFormat: @"%@/Contents/Developer/Toolchains/XcodeDefault.xctoolchain", xcodePath] UTF8String];
} else {
toolchainDir = "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain";
}

if (envIdentity && strlen(envIdentity)) {
identity = [NSString stringWithUTF8String:envIdentity];
NSLog(@"Signing identity from environment: %@", identity);
Expand All @@ -40,7 +47,7 @@ - (void)runInBackground {
buffer[read(clientSocket, buffer, sizeof buffer-1)] = '\000';
NSString *path = [[NSString stringWithUTF8String:buffer] componentsSeparatedByString:@" "][1];

if ([[self class] codesignDylib:path identity:nil]) {
if ([[self class] codesignDylib:path identity:nil xcodePath:nil]) {
snprintf(buffer, sizeof buffer, "HTTP/1.0 200 OK\r\n\r\n");
write(clientSocket, buffer, strlen(buffer));
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/injectiondGuts/include/SignerService.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@

@interface SignerService: NSObject

+ (BOOL)codesignDylib:(NSString * _Nonnull)dylib identity:(NSString * _Nullable)identity;
+ (BOOL)codesignDylib:(NSString * _Nonnull)dylib identity:(NSString * _Nullable)identity xcodePath:(NSString * _Nullable)xcodePath;

@end