diff --git a/Package.swift b/Package.swift index 67608c28..f3a670af 100644 --- a/Package.swift +++ b/Package.swift @@ -51,10 +51,14 @@ let package = Package( name: "xcld", dependencies: ["XCRemoteCache"] ), + .target( + name: "xcldplusplus", + dependencies: ["XCRemoteCache"] + ), .target( // Wrapper target that builds all binaries but does nothing in runtime name: "Aggregator", - dependencies: ["xcprebuild", "xcswiftc", "xclibtool", "xcpostbuild", "xcprepare", "xcld"] + dependencies: ["xcprebuild", "xcswiftc", "xclibtool", "xcpostbuild", "xcprepare", "xcld", "xcldplusplus"] ), .testTarget( name: "XCRemoteCacheTests", diff --git a/README.md b/README.md index 55d42e80..ad4cfed2 100755 --- a/README.md +++ b/README.md @@ -196,6 +196,7 @@ Configure Xcode targets that **should use** XCRemoteCache: * `SWIFT_EXEC` - location of `xcprepare` (e.g. `xcremotecache/xcswiftc`) * `LIBTOOL` - location of `xclibtool` (e.g. `xcremotecache/xclibtool`) * `LD` - location of `xcld` (e.g. `xcremotecache/xcld`) +* `LDPLUSPLUS` - location of `xcldplusplus` (e.g. `xcremotecache/xcldplusplus`) * `XCRC_PLATFORM_PREFERRED_ARCH` - `$(LINK_FILE_LIST_$(CURRENT_VARIANT)_$(PLATFORM_PREFERRED_ARCH):dir:standardizepath:file:default=arm64)`
@@ -265,7 +266,7 @@ $ xcremotecache/xcprepare mark --configuration Debug --platform iphonesimulator That command creates an empty file on a remote server which informs that for given sha, configuration, platform, Xcode versions etc. all artifacts are available. -_Note that for the `producer` mode, the prebuild build phase and `xccc`, `xcld`, `xclibtool` wrappers become no-op, so it is recommended to not add them for the `producer` mode._ +_Note that for the `producer` mode, the prebuild build phase and `xccc`, `xcld`, `xcldplusplus`, `xclibtool` wrappers become no-op, so it is recommended to not add them for the `producer` mode._ ## A full list of configuration parameters: diff --git a/Rakefile b/Rakefile index e56b58a2..e72ef99a 100644 --- a/Rakefile +++ b/Rakefile @@ -10,7 +10,7 @@ DERIVED_DATA_DIR = File.join('.build').freeze RELEASES_ROOT_DIR = File.join('releases').freeze EXECUTABLE_NAME = 'XCRemoteCache' -EXECUTABLE_NAMES = ['xclibtool', 'xcpostbuild', 'xcprebuild', 'xcprepare', 'xcswiftc', 'xcld'] +EXECUTABLE_NAMES = ['xclibtool', 'xcpostbuild', 'xcprebuild', 'xcprepare', 'xcswiftc', 'xcld', 'xcldplusplus'] PROJECT_NAME = 'XCRemoteCache' SWIFTLINT_ENABLED = true diff --git a/Sources/XCRemoteCache/Commands/Prepare/Integrate/IntegrateContext.swift b/Sources/XCRemoteCache/Commands/Prepare/Integrate/IntegrateContext.swift index ffcaa269..c8750a56 100644 --- a/Sources/XCRemoteCache/Commands/Prepare/Integrate/IntegrateContext.swift +++ b/Sources/XCRemoteCache/Commands/Prepare/Integrate/IntegrateContext.swift @@ -53,6 +53,7 @@ extension IntegrateContext { swiftc: binariesDir.appendingPathComponent("xcswiftc"), libtool: binariesDir.appendingPathComponent("xclibtool"), ld: binariesDir.appendingPathComponent("xcld"), + ldplusplus: binariesDir.appendingPathComponent("xcldplusplus"), prebuild: binariesDir.appendingPathComponent("xcprebuild"), postbuild: binariesDir.appendingPathComponent("xcpostbuild") ) diff --git a/Sources/XCRemoteCache/Commands/Prepare/Integrate/XCRCBinariesPaths.swift b/Sources/XCRemoteCache/Commands/Prepare/Integrate/XCRCBinariesPaths.swift index 9aef409f..5325e7e2 100644 --- a/Sources/XCRemoteCache/Commands/Prepare/Integrate/XCRCBinariesPaths.swift +++ b/Sources/XCRemoteCache/Commands/Prepare/Integrate/XCRCBinariesPaths.swift @@ -26,6 +26,7 @@ struct XCRCBinariesPaths { let swiftc: URL let libtool: URL let ld: URL + let ldplusplus: URL let prebuild: URL let postbuild: URL } diff --git a/Sources/xcld/XCLDMain.swift b/Sources/xcld/XCLDMain.swift index e6d338fb..ba4aeb4b 100644 --- a/Sources/xcld/XCLDMain.swift +++ b/Sources/xcld/XCLDMain.swift @@ -21,7 +21,7 @@ import Foundation import XCRemoteCache /// Wrapper for a `LD` program that copies the dynamic executable from a cached-downloaded location -/// Fallbacks to a standard `clang` when the Ramote cache is not applicable (e.g. modified sources) +/// Fallbacks to a standard `clang` when the Remote cache is not applicable (e.g. modified sources) public class XCLDMain { public func main() { let args = ProcessInfo().arguments diff --git a/Sources/xcldplusplus/XCLDPlusPlus.swift b/Sources/xcldplusplus/XCLDPlusPlus.swift new file mode 100644 index 00000000..c6334014 --- /dev/null +++ b/Sources/xcldplusplus/XCLDPlusPlus.swift @@ -0,0 +1,75 @@ +// Copyright (c) 2021 Spotify AB. +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import Foundation +import XCRemoteCache + +/// Wrapper for a `LDPLUSPLUS` program that copies the dynamic executable from a cached-downloaded location +/// Fallbacks to a standard `clang++` when the Remote cache is not applicable (e.g. modified sources) +public class XCLDPlusPlusMain { + public func main() { + let args = ProcessInfo().arguments + var output: String? + var filelist: String? + var dependencyInfo: String? + var i = 0 + while i < args.count { + switch args[i] { + case "-o": + output = args[i + 1] + i += 1 + case "-filelist": + filelist = args[i + 1] + i += 1 + case "-dependency_info": + // Skip following `-Xlinker` argument. Sample call: + // `clang -dynamiclib ... -Xlinker -dependency_info -Xlinker /path/Target_dependency_info.dat` + dependencyInfo = args[i + 2] + i += 2 + default: + break + } + i += 1 + } + guard let outputInput = output, let filelistInput = filelist, let dependencyInfoInput = dependencyInfo else { + let ldCommand = "clang++" + print("Fallbacking to compilation using \(ldCommand).") + + let args = ProcessInfo().arguments + let paramList = [ldCommand] + args.dropFirst() + let cargs = paramList.map { strdup($0) } + [nil] + execvp(ldCommand, cargs) + + /// C-function `execv` returns only when the command fails + exit(1) + } + + + // TODO: consider using `clang_command` from .rcinfo + /// concrete clang path should be taken from the current toolchain + let fallbackCommand = "clang++" + XCCreateBinary( + output: outputInput, + filelist: filelistInput, + dependencyInfo: dependencyInfoInput, + fallbackCommand: fallbackCommand, + stepDescription: "xcldplusplus" + ).run() + } +} diff --git a/Sources/xcldplusplus/main.swift b/Sources/xcldplusplus/main.swift new file mode 100644 index 00000000..623dbb87 --- /dev/null +++ b/Sources/xcldplusplus/main.swift @@ -0,0 +1,22 @@ +// Copyright (c) 2021 Spotify AB. +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import XCRemoteCache + +XCLDPlusPlusMain().main() diff --git a/Tests/XCRemoteCacheTests/Commands/Prepare/Integrate/XcodeProjBuildSettingsIntegrateAppenderTests.swift b/Tests/XCRemoteCacheTests/Commands/Prepare/Integrate/XcodeProjBuildSettingsIntegrateAppenderTests.swift index 939f6dca..ea1d677d 100644 --- a/Tests/XCRemoteCacheTests/Commands/Prepare/Integrate/XcodeProjBuildSettingsIntegrateAppenderTests.swift +++ b/Tests/XCRemoteCacheTests/Commands/Prepare/Integrate/XcodeProjBuildSettingsIntegrateAppenderTests.swift @@ -35,6 +35,7 @@ class XcodeProjBuildSettingsIntegrateAppenderTests: XCTestCase { swiftc: binariesDir.appendingPathComponent("xcswiftc"), libtool: binariesDir.appendingPathComponent("xclibtool"), ld: binariesDir.appendingPathComponent("xcld"), + ldplusplus: binariesDir.appendingPathComponent("xcldplusplus"), prebuild: binariesDir.appendingPathComponent("xcprebuild"), postbuild: binariesDir.appendingPathComponent("xcpostbuild") ) diff --git a/cocoapods-plugin/lib/cocoapods-xcremotecache/command/hooks.rb b/cocoapods-plugin/lib/cocoapods-xcremotecache/command/hooks.rb index 34f5fb0b..02cd75f8 100644 --- a/cocoapods-plugin/lib/cocoapods-xcremotecache/command/hooks.rb +++ b/cocoapods-plugin/lib/cocoapods-xcremotecache/command/hooks.rb @@ -127,6 +127,7 @@ def self.enable_xcremotecache(target, repo_distance, xc_location, xc_cc_path, mo config.build_settings['SWIFT_EXEC'] = ["$SRCROOT/#{srcroot_relative_xc_location}/xcswiftc"] config.build_settings['LIBTOOL'] = ["$SRCROOT/#{srcroot_relative_xc_location}/xclibtool"] config.build_settings['LD'] = ["$SRCROOT/#{srcroot_relative_xc_location}/xcld"] + config.build_settings['LDPLUSPLUS'] = ["$SRCROOT/#{srcroot_relative_xc_location}/xcldplusplus"] config.build_settings['XCREMOTE_CACHE_FAKE_SRCROOT'] = fake_src_root config.build_settings['XCRC_PLATFORM_PREFERRED_ARCH'] = ["$(LINK_FILE_LIST_$(CURRENT_VARIANT)_$(PLATFORM_PREFERRED_ARCH):dir:standardizepath:file:default=arm64)"] @@ -213,6 +214,7 @@ def self.disable_xcremotecache_for_target(target) config.build_settings.delete('SWIFT_EXEC') if config.build_settings.key?('SWIFT_EXEC') config.build_settings.delete('LIBTOOL') if config.build_settings.key?('LIBTOOL') config.build_settings.delete('LD') if config.build_settings.key?('LD') + config.build_settings.delete('LDPLUSPLUS') if config.build_settings.key?('LDPLUSPLUS') # Remove Fake src root for ObjC & Swift config.build_settings.delete('XCREMOTE_CACHE_FAKE_SRCROOT') config.build_settings.delete('XCRC_PLATFORM_PREFERRED_ARCH') @@ -236,7 +238,7 @@ def self.save_rcinfo(info, directory) end def self.download_xcrc_if_needed(local_location) - required_binaries = ['xcld', 'xclibtool', 'xcpostbuild', 'xcprebuild', 'xcprepare', 'xcswiftc'] + required_binaries = ['xcld', 'xcldplusplus', 'xclibtool', 'xcpostbuild', 'xcprebuild', 'xcprepare', 'xcswiftc'] binaries_exist = required_binaries.reduce(true) do |exists, filename| file_path = File.join(local_location, filename) exists = exists && File.exist?(file_path)