|
| 1 | +import TSCBasic |
| 2 | +import TSCUtility |
| 3 | + |
| 4 | +extension DarwinToolchain { |
| 5 | + private func findARCLiteLibPath() throws -> AbsolutePath? { |
| 6 | + let path = try getToolPath(.swiftCompiler) |
| 7 | + .parentDirectory // 'swift' |
| 8 | + .parentDirectory // 'bin' |
| 9 | + .appending(components: "lib", "arc") |
| 10 | + |
| 11 | + if localFileSystem.exists(path) { return path } |
| 12 | + |
| 13 | + // If we don't have a 'lib/arc/' directory, find the "arclite" library |
| 14 | + // relative to the Clang in the active Xcode. |
| 15 | + if let clangPath = try? getToolPath(.clang) { |
| 16 | + return clangPath |
| 17 | + .parentDirectory // 'clang' |
| 18 | + .parentDirectory // 'bin' |
| 19 | + .appending(components: "lib", "arc") |
| 20 | + } |
| 21 | + return nil |
| 22 | + } |
| 23 | + |
| 24 | + private func addProfileGenerationArgs( |
| 25 | + to commandLine: inout [Job.ArgTemplate], |
| 26 | + parsedOptions: inout ParsedOptions, |
| 27 | + targetTriple: Triple |
| 28 | + ) throws { |
| 29 | + guard parsedOptions.hasArgument(.profile_generate) else { return } |
| 30 | + let clangPath = try clangLibraryPath(for: targetTriple, |
| 31 | + parsedOptions: &parsedOptions) |
| 32 | + |
| 33 | + let runtime: String |
| 34 | + if targetTriple.os.isiOS { |
| 35 | + runtime = targetTriple.os.isTvOS ? "tvos" : "ios" |
| 36 | + } else if targetTriple.os.isWatchOS { |
| 37 | + runtime = "watchos" |
| 38 | + } else { |
| 39 | + assert(targetTriple.os.isMacOSX) |
| 40 | + runtime = "osx" |
| 41 | + } |
| 42 | + |
| 43 | + var sim = "" |
| 44 | + if targetTriple.isSimulatorEnvironment { |
| 45 | + sim = "sim" |
| 46 | + } |
| 47 | + |
| 48 | + var clangRTPath = clangPath |
| 49 | + .appending(component: "libclang_rt.profile_\(runtime)\(sim).a") |
| 50 | + |
| 51 | + // FIXME: Continue accepting the old path for simulator libraries for now. |
| 52 | + if targetTriple.isSimulatorEnvironment && |
| 53 | + !localFileSystem.exists(clangRTPath) { |
| 54 | + clangRTPath = clangRTPath.parentDirectory |
| 55 | + .appending(component: "libclang_rt.profile_\(runtime).a") |
| 56 | + } |
| 57 | + |
| 58 | + commandLine.appendPath(clangRTPath) |
| 59 | + } |
| 60 | + |
| 61 | + private func addDeploymentTargetArgs( |
| 62 | + to commandLine: inout [Job.ArgTemplate], |
| 63 | + targetTriple: Triple |
| 64 | + ) { |
| 65 | + // FIXME: Properly handle deployment targets. |
| 66 | + assert(targetTriple.os.isiOS || targetTriple.os.isWatchOS || targetTriple.os.isMacOSX) |
| 67 | + |
| 68 | + if (targetTriple.os.isiOS) { |
| 69 | + if (targetTriple.os.isTvOS) { |
| 70 | + if targetTriple.isSimulatorEnvironment { |
| 71 | + commandLine.appendFlag("-tvos_simulator_version_min") |
| 72 | + } else { |
| 73 | + commandLine.appendFlag("-tvos_version_min") |
| 74 | + } |
| 75 | + } else { |
| 76 | + if targetTriple.isSimulatorEnvironment { |
| 77 | + commandLine.appendFlag("-ios_simulator_version_min") |
| 78 | + } else { |
| 79 | + commandLine.appendFlag("-iphoneos_version_min") |
| 80 | + } |
| 81 | + } |
| 82 | + commandLine.appendFlag(targetTriple.iOSVersion().description) |
| 83 | + } else if targetTriple.os.isWatchOS { |
| 84 | + if targetTriple.isSimulatorEnvironment { |
| 85 | + commandLine.appendFlag("-watchos_simulator_version_min") |
| 86 | + } else { |
| 87 | + commandLine.appendFlag("-watchos_version_min") |
| 88 | + } |
| 89 | + commandLine.appendFlag(targetTriple.watchOSVersion().description) |
| 90 | + } else { |
| 91 | + commandLine.appendFlag("-macosx_version_min") |
| 92 | + commandLine.appendFlag(targetTriple.getMacOSXVersion().1.description) |
| 93 | + } |
| 94 | + } |
| 95 | + |
| 96 | + /// Returns true if the compiler depends on features provided by the ObjC |
| 97 | + /// runtime that are not present on the deployment target indicated by |
| 98 | + /// `triple`. |
| 99 | + private func wantsObjCRuntime(triple: Triple) -> Bool { |
| 100 | + // When updating the versions listed here, please record the most recent |
| 101 | + // feature being depended on and when it was introduced: |
| 102 | + // |
| 103 | + // - Make assigning 'nil' to an NSMutableDictionary subscript delete the |
| 104 | + // entry, like it does for Swift.Dictionary, rather than trap. |
| 105 | + if triple.os.isiOS { |
| 106 | + return triple.iOSVersion() < Triple.Version(9, 0, 0) |
| 107 | + } else if triple.os.isMacOSX { |
| 108 | + return triple.getMacOSXVersion().1 < Triple.Version(10, 11, 0) |
| 109 | + } else if triple.os.isWatchOS { |
| 110 | + return false |
| 111 | + } |
| 112 | + fatalError("unknown Darwin OS") |
| 113 | + } |
| 114 | + |
| 115 | + private func addArgsToLinkARCLite( |
| 116 | + to commandLine: inout [Job.ArgTemplate], |
| 117 | + parsedOptions: inout ParsedOptions, |
| 118 | + targetTriple: Triple |
| 119 | + ) throws { |
| 120 | + if !parsedOptions.hasFlag(positive: .link_objc_runtime, |
| 121 | + negative: .no_link_objc_runtime, |
| 122 | + default: wantsObjCRuntime(triple: targetTriple)) { |
| 123 | + return |
| 124 | + } |
| 125 | + |
| 126 | + guard let arcLiteLibPath = try findARCLiteLibPath(), |
| 127 | + let platformName = targetTriple.platformName else { |
| 128 | + return |
| 129 | + } |
| 130 | + let fullLibPath = arcLiteLibPath |
| 131 | + .appending(components: "libarclite_\(platformName).a") |
| 132 | + |
| 133 | + commandLine.appendFlag("-force_load") |
| 134 | + commandLine.appendPath(fullLibPath) |
| 135 | + |
| 136 | + // Arclite depends on CoreFoundation. |
| 137 | + commandLine.appendFlag(.framework) |
| 138 | + commandLine.appendFlag("CoreFoundation") |
| 139 | + } |
| 140 | + |
| 141 | + /// Adds the arguments necessary to link the files from the given set of |
| 142 | + /// options for a Darwin platform. |
| 143 | + public func addPlatformSpecificLinkerArgs( |
| 144 | + to commandLine: inout [Job.ArgTemplate], |
| 145 | + parsedOptions: inout ParsedOptions, |
| 146 | + linkerOutputType: LinkOutputType, |
| 147 | + inputs: [TypedVirtualPath], |
| 148 | + outputFile: VirtualPath, |
| 149 | + sdkPath: String?, |
| 150 | + targetTriple: Triple |
| 151 | + ) throws -> AbsolutePath { |
| 152 | + |
| 153 | + // FIXME: If we used Clang as a linker instead of going straight to ld, |
| 154 | + // we wouldn't have to replicate a bunch of Clang's logic here. |
| 155 | + |
| 156 | + // Always link the regular compiler_rt if it's present. |
| 157 | + // |
| 158 | + // Note: Normally we'd just add this unconditionally, but it's valid to build |
| 159 | + // Swift and use it as a linker without building compiler_rt. |
| 160 | + let darwinPlatformSuffix = |
| 161 | + targetTriple.darwinLibraryNameSuffix(distinguishSimulator: false)! |
| 162 | + let compilerRTPath = |
| 163 | + try clangLibraryPath( |
| 164 | + for: targetTriple, parsedOptions: &parsedOptions) |
| 165 | + .appending(component: "libclang_rt.\(darwinPlatformSuffix).a") |
| 166 | + if localFileSystem.exists(compilerRTPath) { |
| 167 | + commandLine.append(.path(.absolute(compilerRTPath))) |
| 168 | + } |
| 169 | + |
| 170 | + // Set up for linking. |
| 171 | + let linkerTool: Tool |
| 172 | + switch linkerOutputType { |
| 173 | + case .dynamicLibrary: |
| 174 | + // Same as an executable, but with the -dylib flag |
| 175 | + commandLine.appendFlag("-dylib") |
| 176 | + fallthrough |
| 177 | + case .executable: |
| 178 | + linkerTool = .dynamicLinker |
| 179 | + let fSystemArgs = parsedOptions.filter { |
| 180 | + $0.option == .F || $0.option == .Fsystem |
| 181 | + } |
| 182 | + for opt in fSystemArgs { |
| 183 | + commandLine.appendFlag(.F) |
| 184 | + commandLine.appendPath(try VirtualPath(path: opt.argument.asSingle)) |
| 185 | + } |
| 186 | + |
| 187 | + // FIXME: Sanitizer args |
| 188 | + |
| 189 | + commandLine.appendFlag("-arch") |
| 190 | + commandLine.appendFlag(targetTriple.archName) |
| 191 | + |
| 192 | + try addArgsToLinkStdlib( |
| 193 | + to: &commandLine, |
| 194 | + parsedOptions: &parsedOptions, |
| 195 | + sdkPath: sdkPath, |
| 196 | + targetTriple: targetTriple |
| 197 | + ) |
| 198 | + |
| 199 | + // These custom arguments should be right before the object file at the |
| 200 | + // end. |
| 201 | + try commandLine.append( |
| 202 | + contentsOf: parsedOptions.filter { $0.option.group == .linker_option } |
| 203 | + ) |
| 204 | + try commandLine.appendAllArguments(.Xlinker, from: &parsedOptions) |
| 205 | + |
| 206 | + case .staticLibrary: |
| 207 | + linkerTool = .staticLinker |
| 208 | + commandLine.appendFlag(.static) |
| 209 | + break |
| 210 | + } |
| 211 | + |
| 212 | + try addArgsToLinkARCLite( |
| 213 | + to: &commandLine, |
| 214 | + parsedOptions: &parsedOptions, |
| 215 | + targetTriple: targetTriple |
| 216 | + ) |
| 217 | + addDeploymentTargetArgs( |
| 218 | + to: &commandLine, |
| 219 | + targetTriple: targetTriple |
| 220 | + ) |
| 221 | + try addProfileGenerationArgs( |
| 222 | + to: &commandLine, |
| 223 | + parsedOptions: &parsedOptions, |
| 224 | + targetTriple: targetTriple |
| 225 | + ) |
| 226 | + |
| 227 | + commandLine.appendFlags( |
| 228 | + "-lobjc", |
| 229 | + "-lSystem", |
| 230 | + "-no_objc_category_merging" |
| 231 | + ) |
| 232 | + |
| 233 | + // Add the SDK path |
| 234 | + if let sdkPath = sdkPath { |
| 235 | + commandLine.appendFlag("-syslibroot") |
| 236 | + commandLine.appendPath(try VirtualPath(path: sdkPath)) |
| 237 | + } |
| 238 | + |
| 239 | + if parsedOptions.contains(.embed_bitcode) || |
| 240 | + parsedOptions.contains(.embed_bitcode_marker) { |
| 241 | + commandLine.appendFlag("-bitcode_bundle") |
| 242 | + } |
| 243 | + |
| 244 | + if parsedOptions.contains(.enable_app_extension) { |
| 245 | + commandLine.appendFlag("-application_extension") |
| 246 | + } |
| 247 | + |
| 248 | + // Add inputs. |
| 249 | + commandLine.append(contentsOf: inputs.map { .path($0.file) }) |
| 250 | + |
| 251 | + // Add the output |
| 252 | + commandLine.appendFlag("-o") |
| 253 | + commandLine.appendPath(outputFile) |
| 254 | + |
| 255 | + return try getToolPath(linkerTool) |
| 256 | + } |
| 257 | +} |
0 commit comments