Skip to content

Commit

Permalink
Add AppCode support (fixes #147, #49) (#204)
Browse files Browse the repository at this point in the history
  • Loading branch information
yeswolf committed Mar 1, 2020
1 parent 13f21d0 commit 5c932af
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 13 deletions.
1 change: 1 addition & 0 deletions InjectionBundle/InjectionClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ typedef NS_ENUM(int, InjectionCommand) {
InjectionSigned,
InjectionLoad,
InjectionInject,
InjectionIdeProcPath,
InjectionXprobe,
InjectionEval,
InjectionVaccineSettingChanged,
Expand Down
4 changes: 4 additions & 0 deletions InjectionBundle/InjectionClient.mm
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@ - (void)runInBackground {
case InjectionUntrace:
[SwiftTrace removeAllPatches];
break;
case InjectionIdeProcPath: {
[SwiftEval sharedInstance].lastIdeProcPath = [self readString];
break;
}
default: {
NSString *changed = [self readString];
dispatch_async(dispatch_get_main_queue(), ^{
Expand Down
49 changes: 42 additions & 7 deletions InjectionBundle/SwiftEval.swift
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ public class SwiftEval: NSObject {
}

@objc public var injectionNumber = 0
@objc public var lastIdeProcPath = ""

static var compileByClass = [String: (String, String)]()

static var buildCacheFile = "/tmp/eval_builds.plist"
Expand All @@ -173,10 +175,10 @@ public class SwiftEval: NSObject {
// Largely obsolete section used find Xcode paths from source file being injected.

let sourceURL = URL(fileURLWithPath: classNameOrFile.hasPrefix("/") ? classNameOrFile : #file)
guard let derivedData = findDerivedData(url: URL(fileURLWithPath: NSHomeDirectory())) ??
guard let derivedData = findDerivedData(url: URL(fileURLWithPath: NSHomeDirectory()), ideProcPath: self.lastIdeProcPath) ??
(self.projectFile != nil ?
findDerivedData(url: URL(fileURLWithPath: self.projectFile!)) :
findDerivedData(url: sourceURL)) else {
findDerivedData(url: URL(fileURLWithPath: self.projectFile!), ideProcPath: self.lastIdeProcPath) :
findDerivedData(url: sourceURL, ideProcPath: self.lastIdeProcPath)) else {
throw evalError("Could not locate derived data. Is the project under your home directory?")
}
guard let (projectFile, logsDir) =
Expand Down Expand Up @@ -549,20 +551,53 @@ public class SwiftEval: NSObject {
return (compileCommand, sourceFile.replacingOccurrences(of: "\\$", with: "$"))
}

func findDerivedData(url: URL) -> URL? {
func getAppCodeDerivedData(procPath: String) -> String {
//Default with current year
let derivedDataPath = { (year: Int, pathSelector: String) -> String in
"Library/Caches/\(year > 2019 ? "JetBrains/" : "")\(pathSelector)/DerivedData"
}

let year = Calendar.current.component(.year, from: Date())
let month = Calendar.current.component(.month, from: Date())

let defaultPath = derivedDataPath(year, "AppCode\(month / 4 == 0 ? year - 1 : year).\(month / 4 + (month / 4 == 0 ? 3 : 0))")

var plistPath = URL(fileURLWithPath: procPath)
plistPath.deleteLastPathComponent()
plistPath.deleteLastPathComponent()
plistPath = plistPath.appendingPathComponent("Info.plist")

guard let dictionary = NSDictionary(contentsOf: plistPath) as? Dictionary<String, Any> else { return defaultPath }
guard let jvmOptions = dictionary["JVMOptions"] as? Dictionary<String, Any> else { return defaultPath }
guard let properties = jvmOptions["Properties"] as? Dictionary<String, Any> else { return defaultPath }
guard let pathSelector: String = properties["idea.paths.selector"] as? String else { return defaultPath }

let components = pathSelector.replacingOccurrences(of: "AppCode", with: "").components(separatedBy: ".")
guard components.count == 2 else { return defaultPath }

guard let realYear = Int(components[0]) else { return defaultPath }
return derivedDataPath(realYear, pathSelector)
}

func findDerivedData(url: URL, ideProcPath: String) -> URL? {
if url.path == "/" {
return nil
}

for relative in ["DerivedData", "build/DerivedData",
"Library/Developer/Xcode/DerivedData"] {
var relativeDirs = ["DerivedData", "build/DerivedData"]
if ideProcPath.lowercased().contains("appcode") {
relativeDirs.append(getAppCodeDerivedData(procPath: ideProcPath))
} else {
relativeDirs.append("Library/Developer/Xcode/DerivedData")
}
for relative in relativeDirs {
let derived = url.appendingPathComponent(relative)
if FileManager.default.fileExists(atPath: derived.path) {
return derived
}
}

return findDerivedData(url: url.deletingLastPathComponent())
return findDerivedData(url: url.deletingLastPathComponent(), ideProcPath: ideProcPath)
}

func findProject(for source: URL, derivedData: URL) -> (projectFile: URL, logsDir: URL)? {
Expand Down
18 changes: 16 additions & 2 deletions InjectionIII/FileWatcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation

let INJECTABLE_PATTERN = "[^~]\\.(mm?|swift|storyboard|xib)$"

public typealias InjectionCallback = (_ filesChanged: NSArray) -> Void
public typealias InjectionCallback = (_ filesChanged: NSArray, _ ideProcPath: String) -> Void

public class FileWatcher: NSObject {
var fileEvents: FSEventStreamRef! = nil
Expand Down Expand Up @@ -66,7 +66,11 @@ public class FileWatcher: NSObject {
}

if changed.count != 0 {
callback(Array(changed) as NSArray)
var path = ""
if let application = NSWorkspace.shared.frontmostApplication {
path = getProcPath(pid: application.processIdentifier)
}
callback(Array(changed) as NSArray, path)
}
}

Expand All @@ -75,4 +79,14 @@ public class FileWatcher: NSObject {
FSEventStreamInvalidate(fileEvents)
FSEventStreamRelease(fileEvents)
}

func getProcPath(pid: pid_t) -> String {
let pathBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(MAXPATHLEN))
defer {
pathBuffer.deallocate()
}
proc_pidpath(pid, pathBuffer, UInt32(MAXPATHLEN))
let path = String(cString: pathBuffer)
return path
}
}
1 change: 1 addition & 0 deletions InjectionIII/InjectionIII-Bridging-Header.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#import "InjectionClient.h"

#import "DDHotKeyCenter.h"
#import <libproc.h>

#ifdef XPROBE_PORT
#import "../XprobePlugin/Classes/XprobePluginMenuController.h"
Expand Down
9 changes: 5 additions & 4 deletions InjectionIII/InjectionServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ var projectInjected = ["": ["": Date.timeIntervalSinceReferenceDate]]
let MIN_INJECTION_INTERVAL = 1.0

public class InjectionServer: SimpleSocket {
var injector: ((_ changed: NSArray) -> Void)? = nil
var injector: ((_ changed: NSArray, _ ideProcPath:String) -> Void)? = nil
var fileWatchers = [FileWatcher]()
var pending = [String]()

var lastIdeProcPath = ""
override public class func error(_ message: String) -> Int32 {
let saveno = errno
DispatchQueue.main.sync {
Expand Down Expand Up @@ -162,7 +162,7 @@ public class InjectionServer: SimpleSocket {
var testCache = [String: [String]]()

injector = {
(changed: NSArray) in
(changed: NSArray, ideProcPath: String) in
var changed = changed as! [String]

if UserDefaults.standard.bool(forKey: UserDefaultsTDDEnabled) {
Expand Down Expand Up @@ -195,7 +195,7 @@ public class InjectionServer: SimpleSocket {
}
}
}

self.lastIdeProcPath = ideProcPath
if (automatic) {
self.injectPending()
}
Expand Down Expand Up @@ -259,6 +259,7 @@ public class InjectionServer: SimpleSocket {
@objc public func injectPending() {
for swiftSource in pending {
injectionQueue.async {
self.sendCommand(.ideProcPath, with: self.lastIdeProcPath)
self.sendCommand(.inject, with:swiftSource)
}
}
Expand Down

0 comments on commit 5c932af

Please sign in to comment.