-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Hello Carthage team!
As you know in order to get things done we need to invoke copy-frameworks at the Build Phase. This also involves specifying a list of input files for each linked framework. This seems to be a good subject for an automation. Currently it might be a little annoying to keep this list in sync and to manually fill it in. This is the main motivation.
From my attempt to make a draft of such functionality here is what I found:
Initially we need to grab list of the linked frameworks that was built by Carthage. Turns out that they are stored in the Frameworks Build Phase. Unfortunately built-in tool xcodebuild doesn't allow us to read such information, therefore we used to read contents of the .xcodeproj itself. There are well-known tools such as gem xcodeproj and its Swift counterpart xcodeproj.
First of all we're reading contents of the Frameworks Build Phase. The rest of the process is very simple: select only those frameworks that are located in Carthage/Build but not in the Static subfolder.
Then we simply iterating over found frameworks and filling in env variables like SCRIPT_INPUT_FILE_ and so on.
Below you can find working draft in ruby:
#!/usr/bin/env ruby
require 'xcodeproj'
# We need to find linked frameworks that are located in the `Carthage/Build/*` folder
# but also doesn't appear to be static.
# Focus on the linked frameworks prevents us from accidental copying of the
# frameworks that belong to a different target (e.g. UnitTests).
def find_linked_frameworks_refs
# These env variables passed by Xcode
# during invocation of the Run Script Build Phase
# Therefore running script on its own will have no effect.
project_file_path = ENV["PROJECT_FILE_PATH"]
target_name = ENV["TARGET_NAME"]
return [] if project_file_path.nil? || target_name.nil?
project = Xcodeproj::Project.open(project_file_path)
target = project.targets.detect { |target| target.name == target_name }
return [] if target.nil?
refs = target.frameworks_build_phases.files_references
# Selecting only Dynamic Framworks built by Carthage.
# Static frameworks also appears in this list but we don't need to process them.
regexp = %r{^Carthage\/Build\/((?!\/Static\/).)*$}
refs.select do |ref|
regexp.match(ref.path) != nil
end
end
def export_io_vars_for_refs(refs)
# The same: env vars exported by Xcode. Manual invocation will have no effect.
srcroot = ENV["SRCROOT"]
build_products_directory = ENV["BUILT_PRODUCTS_DIR"]
frameworks_folder_path = ENV["FRAMEWORKS_FOLDER_PATH"]
return if srcroot.nil? || build_products_directory.nil? || frameworks_folder_path.nil?
refs.each_with_index do |ref, index|
# Ref has a relative path. We assume that Carthage folder located in the $SRCROOT.
input_path = File.join(srcroot, ref.path)
# Exporting variables for `carthage copy-frameworks`.
ENV["SCRIPT_INPUT_FILE_#{index}"] = input_path
# Specify output files to speed up execution.
# My concern at this point is that I'm not sure whether specifying these
# vars within script itself actually have any impact on Xcode.
# It might be the case when Xcode evaluates equality of the input/output
# files prior to run of the script.
output_path = File.join(build_products_directory, frameworks_folder_path, File.basename(ref.path))
ENV["SCRIPT_OUTPUT_FILE_#{index}"] = output_path
end
ENV["SCRIPT_INPUT_FILE_COUNT"] = refs.count.to_s
ENV["SCRIPT_OUTPUT_FILE_COUNT"] = refs.count.to_s
end
refs = find_linked_frameworks_refs
if refs.count > 0 then
export_io_vars_for_refs(refs)
exec "/usr/local/bin/carthage copy-frameworks"
endSimply put it to the new build phase and invoke as follow:
ruby ${SRCROOT}/copy-frameworks.rb
Important: script requires Xcodeproj gem. Therefore is case you're managing ruby via rbenv, please use following script:
export PATH=~/.rbenv/shims:$PATH
ruby ${SRCROOT}/copy-frameworks.rb
The only downside at this point is that this automation would require from Carthage to add one more dependency (ruby gem or swift counterpart).
UPD:
Alternatives considered:
Simply copy contents of the Carthage/Build/... to the destination.
Pros: removes extra dependency
Cons: copies frameworks from other targets (if any) to the destination target.