0001    //
0002    //  Xcode.swift
0003    //  SourceKitten
0004    //
0005    //  Created by JP Simard on 7/15/15.
0006    //  Copyright © 2015 SourceKitten. All rights reserved.
0007    //
0008    
0009    import Foundation
0010    
0011    /**
0012    Run `xcodebuild clean build` along with any passed in build arguments.
0013    
0014    - parameter arguments: Arguments to pass to `xcodebuild`.
0015    - parameter path:      Path to run `xcodebuild` from.
0016    
0017    - returns: `xcodebuild`'s STDERR+STDOUT output combined.
0018    */
0019    internal func runXcodeBuild
ClangTranslationUnit.swift:80
        let xcodeBuildOutput = runXcodeBuild(xcodeBuildArguments + ["-dry-run"], inPath: path) ?? ""
Module.swift:45
        let xcodeBuildOutput = runXcodeBuild(xcodeBuildArguments, inPath: path) ?? ""
(arguments: [String], inPath path: String) -> String? { 0020 fputs("Running xcodebuild\n", stderr) 0021 0022 let task = NSTask() 0023 task.launchPath = "/usr/bin/xcodebuild" 0024 task.currentDirectoryPath = path 0025 task.arguments = arguments + ["clean", "build", "CODE_SIGN_IDENTITY=", "CODE_SIGNING_REQUIRED=NO"] 0026 0027 let pipe = NSPipe() 0028 task.standardOutput = pipe 0029 task.standardError = pipe 0030 0031 task.launch() 0032 0033 let file = pipe.fileHandleForReading 0034 let xcodebuildOutput = NSString(data: file.readDataToEndOfFile(), encoding: NSUTF8StringEncoding) 0035 file.closeFile() 0036 0037 return xcodebuildOutput as String? 0038 } 0039 0040 /** 0041 Parses likely module name from compiler or `xcodebuild` arguments. 0042 0043 Will the following values, in this priority: module name, target name, scheme name. 0044 0045 - parameter arguments: Compiler or `xcodebuild` arguments to parse. 0046 0047 - returns: Module name if successful. 0048 */ 0049 internal func moduleNameFromArguments
Module.swift:46
        guard let arguments = parseCompilerArguments(xcodeBuildOutput, language: .Swift, moduleName: name ?? moduleNameFromArguments(xcodeBuildArguments)) else {
Module.swift:54
        guard let moduleName = moduleNameFromArguments(arguments) else {
(arguments: [String]) -> String? { 0050 let flags = ["-module-name", "-target", "-scheme"] 0051 for flag in flags { 0052 if let flagIndex = arguments.indexOf(flag) { 0053 if flagIndex + 1 < arguments.count { 0054 return arguments[flagIndex + 1] 0055 } 0056 } 0057 } 0058 return nil 0059 } 0060 0061 /** 0062 Partially filters compiler arguments from `xcodebuild` to something that SourceKit/Clang will accept. 0063 0064 - parameter args: Compiler arguments, as parsed from `xcodebuild`. 0065 0066 - returns: A tuple of partially filtered compiler arguments in `.0`, and whether or not there are 0067 more flags to remove in `.1`. 0068 */ 0069 private func partiallyFilterArguments
Xcode.swift:97
        (args, shouldContinueToFilterArguments) = partiallyFilterArguments(args)
(args: [String]) -> ([String], Bool) { 0070 var args = args 0071 var didRemove = false 0072 let flagsToRemove = [ 0073 "-output-file-map" 0074 ] 0075 for flag in flagsToRemove { 0076 if let index = args.indexOf(flag) { 0077 didRemove = true 0078 args.removeAtIndex(index.successor()) 0079 args.removeAtIndex(index) 0080 } 0081 } 0082 return (args, didRemove) 0083 } 0084 0085 /** 0086 Filters compiler arguments from `xcodebuild` to something that SourceKit/Clang will accept. 0087 0088 - parameter args: Compiler arguments, as parsed from `xcodebuild`. 0089 0090 - returns: Filtered compiler arguments. 0091 */ 0092 private func filterArguments
Xcode.swift:142
    let args = filterArguments(xcodebuildOutput
(args: [String]) -> [String] { 0093 var args = args 0094 args.appendContentsOf(["-D", "DEBUG"]) 0095 var shouldContinueToFilterArguments = true 0096 while shouldContinueToFilterArguments { 0097 (args, shouldContinueToFilterArguments) = partiallyFilterArguments(args) 0098 } 0099 return args.filter { 0100 ![ 0101 "-parseable-output", 0102 "-incremental", 0103 "-serialize-diagnostics", 0104 "-emit-dependencies" 0105 ].contains($0) 0106 }.map { 0107 if $0 == "-O" { 0108 return "-Onone" 0109 } else if $0 == "-DNDEBUG=1" { 0110 return "-DDEBUG=1" 0111 } 0112 return $0 0113 } 0114 } 0115 0116 /** 0117 Parses the compiler arguments needed to compile the `language` files. 0118 0119 - parameter xcodebuildOutput: Output of `xcodebuild` to be parsed for compiler arguments. 0120 - parameter language: Language to parse for. 0121 - parameter moduleName: Name of the Module for which to extract compiler arguments. 0122 0123 - returns: Compiler arguments, filtered for suitable use by SourceKit if `.Swift` or Clang if `.ObjC`. 0124 */ 0125 internal func parseCompilerArguments
ClangTranslationUnit.swift:81
        guard let clangArguments = parseCompilerArguments(xcodeBuildOutput, language: .ObjC, moduleName: nil) else {
Module.swift:46
        guard let arguments = parseCompilerArguments(xcodeBuildOutput, language: .Swift, moduleName: name ?? moduleNameFromArguments(xcodeBuildArguments)) else {
(xcodebuildOutput: NSString, language: Language, moduleName: String?) -> [String]? { 0126 let pattern: String 0127 if language == .ObjC { 0128 pattern = "/usr/bin/clang.*" 0129 } else if let moduleName = moduleName { 0130 pattern = "/usr/bin/swiftc.*-module-name \(moduleName) .*" 0131 } else { 0132 pattern = "/usr/bin/swiftc.*" 0133 } 0134 let regex = try! NSRegularExpression(pattern: pattern, options: []) // Safe to force try 0135 let range = NSRange(location: 0, length: xcodebuildOutput.length) 0136 0137 guard let regexMatch = regex.firstMatchInString(xcodebuildOutput as String, options: [], range: range) else { 0138 return nil 0139 } 0140 0141 let escapedSpacePlaceholder = "\u{0}" 0142 let args = filterArguments(xcodebuildOutput 0143 .substringWithRange(regexMatch.range) 0144 .stringByReplacingOccurrencesOfString("\\ ", withString: escapedSpacePlaceholder) 0145 .componentsSeparatedByString(" ")) 0146 0147 // Remove first argument (swiftc/clang) and re-add spaces in arguments 0148 return (args[1..<args.count]).map { 0149 $0.stringByReplacingOccurrencesOfString(escapedSpacePlaceholder, withString: " ") 0150 } 0151 } 0152 0153 /** 0154 Extracts Objective-C header files and `xcodebuild` arguments from an array of header files followed by `xcodebuild` arguments. 0155 0156 - parameter sourcekittenArguments: Array of Objective-C header files followed by `xcodebuild` arguments. 0157 0158 - returns: Tuple of header files and xcodebuild arguments. 0159 */ 0160 public func parseHeaderFilesAndXcodebuildArguments(sourcekittenArguments: [String]) -> (headerFiles: [String], xcodebuildArguments: [String]) { 0161 var xcodebuildArguments = sourcekittenArguments 0162 var headerFiles = [String]() 0163 while let headerFile = xcodebuildArguments.first where headerFile.isObjectiveCHeaderFile() { 0164 headerFiles.append(xcodebuildArguments.removeAtIndex(0).absolutePathRepresentation()) 0165 } 0166 return (headerFiles, xcodebuildArguments) 0167 } 0168 0169 public func sdkPath() -> String { 0170 let task = NSTask() 0171 task.launchPath = "/usr/bin/xcrun" 0172 task.arguments = ["--show-sdk-path"] 0173 0174 let pipe = NSPipe() 0175 task.standardOutput = pipe 0176 0177 task.launch() 0178 0179 let file = pipe.fileHandleForReading 0180 let sdkPath = NSString(data: file.readDataToEndOfFile(), encoding: NSUTF8StringEncoding) 0181 file.closeFile() 0182 return sdkPath?.stringByReplacingOccurrencesOfString("\n", withString: "") ?? "" 0183 } 0184