fastlane/lib/fastlane/actions/create_xcframework.rb in fastlane-2.191.0 vs fastlane/lib/fastlane/actions/create_xcframework.rb in fastlane-2.192.0
- old
+ new
@@ -3,16 +3,23 @@
module SharedValues
XCFRAMEWORK_PATH ||= :XCFRAMEWORK_PATH
end
class CreateXcframeworkAction < Action
+ PARAMETERS_TO_OPTIONS = { headers: '-headers', dsyms: '-debug-symbols' }
+
def self.run(params)
- UI.user_error!("Please provide either :frameworks or :libraries to be packaged into the xcframework") unless params[:frameworks] || params[:libraries]
+ artifacts = normalized_artifact_info(params[:frameworks], [:dsyms]) ||
+ normalized_artifact_info(params[:frameworks_with_dsyms], [:dsyms]) ||
+ normalized_artifact_info(params[:libraries], [:headers, :dsyms]) ||
+ normalized_artifact_info(params[:libraries_with_headers_or_dsyms], [:headers, :dsyms])
+ UI.user_error!("Please provide either :frameworks, :frameworks_with_dsyms, :libraries or :libraries_with_headers_or_dsyms to be packaged into the xcframework") unless artifacts
+
+ artifacts_type = params[:frameworks] || params[:frameworks_with_dsyms] ? '-framework' : '-library'
create_command = ['xcodebuild', '-create-xcframework']
- create_command << params[:frameworks].map { |framework| ['-framework', "\"#{framework}\""] }.flatten if params[:frameworks]
- create_command << params[:libraries].map { |library, headers| ['-library', "\"#{library}\""] + (headers.empty? ? [] : ['-headers', "\"#{headers}\""]) } if params[:libraries]
+ create_command << artifacts.map { |artifact, artifact_info| [artifacts_type, "\"#{artifact}\""] + artifact_info_as_options(artifact_info) }.flatten
create_command << ['-output', "\"#{params[:output]}\""]
create_command << ['-allow-internal-distribution'] if params[:allow_internal_distribution]
if File.directory?(params[:output])
UI.message("Deleting existing: #{params[:output]}")
@@ -22,10 +29,36 @@
Actions.lane_context[SharedValues::XCFRAMEWORK_PATH] = params[:output]
sh(create_command)
end
+ def self.normalized_artifact_info(artifacts_with_info, valid_info)
+ case artifacts_with_info
+ when Array
+ artifacts_with_info.map { |artifact| [artifact, {}] }.to_h
+ when Hash
+ # Convert keys of artifact info to symbols ('dsyms' to :dsyms) and only keep keys we are interested in
+ # For example with valid_info = [:dsyms]
+ # { 'FrameworkA.framework' => { 'dsyms' => 'FrameworkA.framework.dSYM', 'foo' => bar } }
+ # gets converted to
+ # { 'FrameworkA.framework' => { dsyms: 'FrameworkA.framework.dSYM' } }
+ artifacts_with_info.transform_values { |artifact_info| artifact_info.transform_keys(&:to_sym).slice(*valid_info) }
+ else
+ artifacts_with_info
+ end
+ end
+
+ def self.artifact_info_as_options(artifact_info)
+ artifact_info.map { |type, file| [PARAMETERS_TO_OPTIONS[type], "\"#{file}\""] }.flatten
+ end
+
+ def self.check_artifact_info(artifact_info)
+ UI.user_error!("Headers and dSYMs information should be a hash") unless artifact_info.kind_of?(Hash)
+ UI.user_error!("#{artifact_info[:headers]} doesn't exist or is not a directory") if artifact_info[:headers] && !File.directory?(artifact_info[:headers])
+ UI.user_error!("#{artifact_info[:dsyms]} doesn't seem to be a dSYM archive") if artifact_info[:dsyms] && !File.directory?(artifact_info[:dsyms])
+ end
+
#####################################################
# @!group Documentation
#####################################################
def self.description
@@ -35,48 +68,93 @@
def self.details
<<~DETAILS
Utility for packaging multiple build configurations of a given library
or framework into a single xcframework.
- If you want to package several frameworks just provide an array containing
- the list of frameworks to be packaged using the :frameworks parameter.
+ If you want to package several frameworks just provide one of:
- If you want to package several libraries with their corresponding headers
- provide a hash containing the library as the key and the directory containing
- its headers as the value (or an empty string if there are no headers associated
- with the provided library).
+ * An array containing the list of frameworks using the :frameworks parameter
+ (if they have no associated dSYMs):
+ ['FrameworkA.framework', 'FrameworkB.framework']
+ * A hash containing the list of frameworks with their dSYMs using the
+ :frameworks_with_dsyms parameter:
+ {
+ 'FrameworkA.framework' => {},
+ 'FrameworkB.framework' => { dsyms: 'FrameworkB.framework.dSYM' }
+ }
+
+ If you want to package several libraries just provide one of:
+
+ * An array containing the list of libraries using the :libraries parameter
+ (if they have no associated headers or dSYMs):
+ ['LibraryA.so', 'LibraryB.so']
+
+ * A hash containing the list of libraries with their headers and dSYMs
+ using the :libraries_with_headers_or_dsyms parameter:
+ {
+ 'LibraryA.so' => { dsyms: 'libraryA.so.dSYM' },
+ 'LibraryB.so' => { headers: 'headers' }
+ }
+
Finally specify the location of the xcframework to be generated using the :output
parameter.
DETAILS
end
def self.available_options
[
FastlaneCore::ConfigItem.new(key: :frameworks,
env_name: "FL_CREATE_XCFRAMEWORK_FRAMEWORKS",
- description: "Frameworks to add to the target xcframework",
+ description: "Frameworks (without dSYMs) to add to the target xcframework",
type: Array,
optional: true,
- conflicting_options: [:libraries],
+ conflicting_options: [:frameworks_with_dsyms, :libraries, :libraries_with_headers_or_dsyms],
verify_block: proc do |value|
- value.each do |framework|
+ normalized_artifact_info(value, [:dsyms]).each do |framework, framework_info|
UI.user_error!("#{framework} doesn't end with '.framework'. Is this really a framework?") unless framework.end_with?('.framework')
UI.user_error!("Couldn't find framework at #{framework}") unless File.exist?(framework)
UI.user_error!("#{framework} doesn't seem to be a framework") unless File.directory?(framework)
+ check_artifact_info(framework_info)
end
end),
+ FastlaneCore::ConfigItem.new(key: :frameworks_with_dsyms,
+ env_name: "FL_CREATE_XCFRAMEWORK_FRAMEWORKS_WITH_DSYMS",
+ description: "Frameworks (with dSYMs) to add to the target xcframework",
+ type: Hash,
+ optional: true,
+ conflicting_options: [:frameworks, :libraries, :libraries_with_headers_or_dsyms],
+ verify_block: proc do |value|
+ normalized_artifact_info(value, [:dsyms]).each do |framework, framework_info|
+ UI.user_error!("#{framework} doesn't end with '.framework'. Is this really a framework?") unless framework.end_with?('.framework')
+ UI.user_error!("Couldn't find framework at #{framework}") unless File.exist?(framework)
+ UI.user_error!("#{framework} doesn't seem to be a framework") unless File.directory?(framework)
+ check_artifact_info(framework_info)
+ end
+ end),
FastlaneCore::ConfigItem.new(key: :libraries,
env_name: "FL_CREATE_XCFRAMEWORK_LIBRARIES",
- description: "Libraries to add to the target xcframework, with their corresponding headers",
+ description: "Libraries (without headers or dSYMs) to add to the target xcframework",
+ type: Array,
+ optional: true,
+ conflicting_options: [:frameworks, :frameworks_with_dsyms, :libraries_with_headers_or_dsyms],
+ verify_block: proc do |value|
+ normalized_artifact_info(value, [:headers, :dsyms]).each do |library, library_info|
+ UI.user_error!("Couldn't find library at #{library}") unless File.exist?(library)
+ check_artifact_info(library_info)
+ end
+ end),
+ FastlaneCore::ConfigItem.new(key: :libraries_with_headers_or_dsyms,
+ env_name: "FL_CREATE_XCFRAMEWORK_LIBRARIES_WITH_HEADERS_OR_DSYMS",
+ description: "Libraries (with headers or dSYMs) to add to the target xcframework",
type: Hash,
optional: true,
- conflicting_options: [:frameworks],
+ conflicting_options: [:frameworks, :frameworks_with_dsyms, :libraries],
verify_block: proc do |value|
- value.each do |library, headers|
+ normalized_artifact_info(value, [:headers, :dsyms]).each do |library, library_info|
UI.user_error!("Couldn't find library at #{library}") unless File.exist?(library)
- UI.user_error!("#{headers} doesn't exist or is not a directory") unless headers.empty? || File.directory?(headers)
+ check_artifact_info(library_info)
end
end),
FastlaneCore::ConfigItem.new(key: :output,
env_name: "FL_CREATE_XCFRAMEWORK_OUTPUT",
description: "The path to write the xcframework to",
@@ -101,10 +179,12 @@
end
def self.example_code
[
"create_xcframework(frameworks: ['FrameworkA.framework', 'FrameworkB.framework'], output: 'UniversalFramework.xcframework')",
- "create_xcframework(libraries: { 'LibraryA.so' => '', 'LibraryB.so' => 'LibraryBHeaders'}, output: 'UniversalFramework.xcframework')"
+ "create_xcframework(frameworks_with_dsyms: {'FrameworkA.framework' => {}, 'FrameworkB.framework' => { dsyms: 'FrameworkB.framework.dSYM' } }, output: 'UniversalFramework.xcframework')",
+ "create_xcframework(libraries: ['LibraryA.so', 'LibraryB.so'], output: 'UniversalFramework.xcframework')",
+ "create_xcframework(libraries_with_headers_or_dsyms: { 'LibraryA.so' => { dsyms: 'libraryA.so.dSYM' }, 'LibraryB.so' => { headers: 'LibraryBHeaders' } }, output: 'UniversalFramework.xcframework')"
]
end
def self.category
:building