Class: Fastlane::Actions::CreateXcframeworkAction
- Inherits:
-
Action
- Object
- Action
- Fastlane::Actions::CreateXcframeworkAction
- Defined in:
- lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb
Class Method Summary collapse
- .authors ⇒ Object
- .available_destinations ⇒ Object
- .available_options ⇒ Object
- .category ⇒ Object
- .clean(params) ⇒ Object
- .copy_BCSymbolMaps(params) ⇒ Object
- .copy_dSYMs(params) ⇒ Object
- .create_xcframework(params) ⇒ Object
- .debug_symbols(index:, params:, framework: nil) ⇒ Object
- .delete_xcframework(params) ⇒ Object
-
.description ⇒ Object
Documentation #.
- .details ⇒ Object
- .example_code ⇒ Object
- .is_supported?(platform) ⇒ Boolean
- .output ⇒ Object
- .provide_shared_values(params) ⇒ Object
-
.remove_module_reference(params) ⇒ Object
#Fix compile problem go to xcframework and run this command (developer.apple.com/forums/thread/123253): #The generated file includes many module references that Swift thinks are class references because it uses the class ahead of the module #The problem is that in the Swiftinterface file, we have a class named ABCConnections, but the module is also called ABCConnections.
- .run(params) ⇒ Object
- .sign_xcframework(params) ⇒ Object
- .update_destinations(params) ⇒ Object
- .update_xcargs(params) ⇒ Object
- .verify_delicate_params(params) ⇒ Object
- .zip_xcframework(params) ⇒ Object
Class Method Details
.authors ⇒ Object
388 389 390 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 388 def self. ['ykhandelwal'] end |
.available_destinations ⇒ Object
348 349 350 351 352 353 354 355 356 357 358 359 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 348 def self.available_destinations { 'iOS' => ['generic/platform=iOS', 'generic/platform=iOS Simulator'], 'iPadOS' => ['generic/platform=iPadOS', 'generic/platform=iPadOS Simulator'], 'tvOS' => ['generic/platform=tvOS', 'generic/platform=tvOS Simulator'], 'watchOS' => ['generic/platform=watchOS', 'generic/platform=watchOS Simulator'], 'carPlayOS' => ['generic/platform=carPlayOS', 'generic/platform=carPlayOS Simulator'], 'macOS' => ['generic/platform=macOS'], 'maccatalyst' => ['generic/platform=macOS,variant=Mac Catalyst'], 'visionOS' => ['generic/platform=visionOS', 'generic/platform=visionOS Simulator'] } end |
.available_options ⇒ Object
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 397 def self. XcarchiveAction. + [ FastlaneCore::ConfigItem.new( key: :scheme, description: "The project's scheme. Make sure it's marked as Shared", optional: false ), FastlaneCore::ConfigItem.new( key: :destinations, description: 'Use custom destinations for building the xcframework', optional: true, is_string: false, default_value: ['iOS'] ), FastlaneCore::ConfigItem.new( key: :xcframework_output_directory, description: 'The directory in which the xcframework should be stored in', optional: true ), FastlaneCore::ConfigItem.new( key: :include_dSYMs, description: 'Includes dSYM files in the xcframework', optional: true, default_value: true ), FastlaneCore::ConfigItem.new( key: :include_BCSymbolMaps, description: 'Includes BCSymbolMap files in the xcframework', optional: true, default_value: true ), FastlaneCore::ConfigItem.new( key: :include_debug_symbols, description: 'This feature was added in Xcode 12.0.' \ 'If this is set to false, the dSYMs and BCSymbolMaps wont be added to XCFramework itself', optional: true, default_value: true ), FastlaneCore::ConfigItem.new( key: :product_name, description: 'The name of your module. Optional if equals to :scheme. Equivalent to CFBundleName', optional: true ), FastlaneCore::ConfigItem.new( key: :remove_xcarchives, description: 'This option will auto-remove the xcarchive files once the plugin finishes.' \ 'Set this to false to preserve the xcarchives', optional: true, default_value: true ), FastlaneCore::ConfigItem.new( key: :allow_internal_distribution, description: 'This option will create an xcframework with the allow-internal-distribution flag.' \ 'Allows the usage of @testable when importing the created xcframework in tests', optional: true, default_value: false ), FastlaneCore::ConfigItem.new( key: :override_xcargs, description: 'This option will override xcargs SKIP_INSTALL and BUILD_LIBRARY_FOR_DISTRIBUTION.' \ 'If set to true, SKIP_INSTALL will be set to NO and BUILD_LIBRARY_FOR_DISTRIBUTION will be set to YES' \ 'Set this to false to preserve the passed xcargs', optional: true, default_value: true ), FastlaneCore::ConfigItem.new( key: :frameworks, description: 'List of frameworks to create xcframework for', optional: true, is_string: false, type: Array, default_value: []), FastlaneCore::ConfigItem.new( key: :code_sign_identity, description: 'Code sign identity to use for building the xcframework', optional: true ), FastlaneCore::ConfigItem.new( key: :zip_xcframework, description: 'Zip the xcframework after creation', optional: true, default_value: false ), FastlaneCore::ConfigItem.new( key: :ignore_module_reference, description: 'List of module references to remove from the xcframework', optional: true, is_string: false, type: Array, default_value: []), FastlaneCore::ConfigItem.new( key: :delete_xcframework, description: 'Delete the xcframework after creation', optional: true, default_value: false ), FastlaneCore::ConfigItem.new( key: :combine_all_zip, description: 'Combine all xcframeworks into one zip file', optional: true, default_value: false ) ] end |
.category ⇒ Object
503 504 505 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 503 def self.category :building end |
.clean(params) ⇒ Object
65 66 67 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 65 def self.clean(params) FileUtils.rm_rf(@xchelper.xcarchive_path) if params[:remove_xcarchives] end |
.copy_BCSymbolMaps(params) ⇒ Object
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 284 def self.copy_BCSymbolMaps(params) return if params[:include_BCSymbolMaps] == false frameworks = params[:frameworks] || [nil] frameworks.each_with_index do |framework, framework_index| symbols_output_dir = framework ? @xchelper.framework_BCSymbolMaps_path(framework) : @xchelper.xcframework_BCSymbolMaps_path FileUtils.mkdir_p(symbols_output_dir) symbols_xcarchive_dir = @xchelper.xcarchive_BCSymbolMaps_path(framework_index) next unless Dir.exist?(symbols_xcarchive_dir) FileUtils.cp_r("#{symbols_xcarchive_dir}/.", symbols_output_dir) UI.important("▸ Copying #{Dir.children(symbols_xcarchive_dir)} to #{symbols_output_dir}") end end |
.copy_dSYMs(params) ⇒ Object
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 265 def self.copy_dSYMs(params) return if params[:include_dSYMs] == false frameworks = params[:frameworks] || [nil] frameworks.each_with_index do |framework, framework_index| dSYMs_output_dir = framework ? @xchelper.framework_dSYMs_path(framework) : @xchelper.xcframework_dSYMs_path FileUtils.mkdir_p(dSYMs_output_dir) dSYM_source = "#{@xchelper.xcarchive_dSYMs_path(framework_index)}/#{framework}.dSYM" identifier = @xchelper.library_identifier(framework_index) dSYM = "#{framework}.#{identifier}.dSYM" dSYM_destination = "#{dSYMs_output_dir}/#{dSYM}" UI.important("▸ Copying #{dSYM} to #{dSYMs_output_dir}") FileUtils.cp_r(dSYM_source, dSYM_destination) end end |
.create_xcframework(params) ⇒ Object
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 69 def self.create_xcframework(params) UI.("params: #{params}") if params[:frameworks] params[:frameworks].each do |framework| UI.("▸ Creating xcframework for #{framework}") xcframework = @xchelper.get_xcframework_path(framework) UI.("▸ Creating xcframework for #{framework} at path: #{xcframework}") begin FileUtils.rm_rf(xcframework) if File.exist?(xcframework) arguments = ['-create-xcframework'] arguments << '-allow-internal-distribution' if params[:allow_internal_distribution] params[:destinations].each_with_index do |_, index| arguments << "-framework #{@xchelper.get_xcarchive_framework_path(index, framework)}" arguments << debug_symbols(index: index, params: params, framework: framework) end arguments << "-output #{xcframework}" UI.("set -o pipefail && xcodebuild #{arguments.reject(&:empty?).join(' ')}") Actions.sh("set -o pipefail && xcodebuild #{arguments.reject(&:empty?).join(' ')}") rescue StandardError => e UI.user_error!(e) end end else xcframework = @xchelper.xcframework_path UI.("▸ Creating xcframework at path: #{xcframework}") begin FileUtils.rm_rf(xcframework) if File.exist?(xcframework) arguments = ['-create-xcframework'] arguments << '-allow-internal-distribution' if params[:allow_internal_distribution] params[:destinations].each_with_index do |_, index| arguments << "-framework #{@xchelper.xcarchive_framework_path(index)}" arguments << debug_symbols(index: index, params: params, framework: @xchelper.framework) end arguments << "-output #{xcframework}" Actions.sh("set -o pipefail && xcodebuild #{arguments.reject(&:empty?).join(' ')}") rescue StandardError => e UI.user_error!(e) end end end |
.debug_symbols(index:, params:, framework: nil) ⇒ Object
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 240 def self.debug_symbols(index:, params:, framework: nil) return '' if !Helper.xcode_at_least?('12.0.0') || params[:include_debug_symbols] == false frameworks = params[:frameworks] || [framework] debug_symbols = [] frameworks.each do |fw| # Include dSYMs in xcframework if params[:include_dSYMs] != false debug_symbols << "-debug-symbols #{@xchelper.xcarchive_dSYMs_path(index)}/#{fw}.dSYM" end # Include BCSymbols in xcframework if params[:include_BCSymbolMaps] != false bc_symbols_dir = @xchelper.xcarchive_BCSymbolMaps_path(index) if Dir.exist?(bc_symbols_dir) arguments = Dir.children(bc_symbols_dir).map { |path| "-debug-symbols #{File.("#{bc_symbols_dir}/#{path}")}" } debug_symbols << arguments.join(' ') end end end debug_symbols.join(' ') end |
.delete_xcframework(params) ⇒ Object
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 197 def self.delete_xcframework(params) return if params[:delete_xcframework].nil? || params[:delete_xcframework] == false frameworks = params[:frameworks] || [nil] frameworks.each do |framework| xcframework = framework ? @xchelper.get_xcframework_path(framework) : @xchelper.xcframework_path UI.("▸ Deleting xcframework at path: #{xcframework}") begin FileUtils.rm_rf(xcframework) rescue StandardError => e UI.user_error!(e) end end end |
.description ⇒ Object
Documentation #
365 366 367 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 365 def self.description 'Fastlane plugin that creates xcframework for given list of destinations.' end |
.details ⇒ Object
392 393 394 395 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 392 def self.details 'Create xcframework plugin generates xcframework for specified destinations. ' \ 'The output of this action consists of the xcframework itself, which contains dSYM and BCSymbolMaps, if bitcode is enabled.' end |
.example_code ⇒ Object
369 370 371 372 373 374 375 376 377 378 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 369 def self.example_code [ create_xcframework( workspace: 'path/to/your.xcworkspace', scheme: 'framework scheme', destinations: ['iOS'], xcframework_output_directory: 'output_directory' ) ] end |
.is_supported?(platform) ⇒ Boolean
507 508 509 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 507 def self.is_supported?(platform) [:ios, :mac].include?(platform) end |
.output ⇒ Object
380 381 382 383 384 385 386 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 380 def self.output [ ['XCFRAMEWORK_OUTPUT_PATH', 'The path to the newly generated xcframework'], ['XCFRAMEWORK_DSYM_OUTPUT_PATH', 'The path to the folder with dSYMs'], ['XCFRAMEWORK_BCSYMBOLMAPS_OUTPUT_PATH', 'The path to the folder with BCSymbolMaps'] ] end |
.provide_shared_values(params) ⇒ Object
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 49 def self.provide_shared_values(params) frameworks = params[:frameworks] || [nil] frameworks.each do |framework| xcframework_path = framework ? @xchelper.get_xcframework_path(framework) : @xchelper.xcframework_path dsyms_path = framework ? @xchelper.framework_dSYMs_path(framework) : @xchelper.xcframework_dSYMs_path bcsymbolmaps_path = framework ? @xchelper.framework_BCSymbolMaps_path(framework) : @xchelper.xcframework_BCSymbolMaps_path Actions.lane_context[SharedValues::XCFRAMEWORK_OUTPUT_PATH] = File.(xcframework_path) ENV[SharedValues::XCFRAMEWORK_OUTPUT_PATH.to_s] = File.(xcframework_path) Actions.lane_context[SharedValues::XCFRAMEWORK_DSYM_OUTPUT_PATH] = File.(dsyms_path) ENV[SharedValues::XCFRAMEWORK_DSYM_OUTPUT_PATH.to_s] = File.(dsyms_path) Actions.lane_context[SharedValues::XCFRAMEWORK_BCSYMBOLMAPS_OUTPUT_PATH] = File.(bcsymbolmaps_path) ENV[SharedValues::XCFRAMEWORK_BCSYMBOLMAPS_OUTPUT_PATH.to_s] = File.(bcsymbolmaps_path) end end |
.remove_module_reference(params) ⇒ Object
#Fix compile problem go to xcframework and run this command (developer.apple.com/forums/thread/123253): #The generated file includes many module references that Swift thinks are class references because it uses the class ahead of the module #The problem is that in the Swiftinterface file, we have a class named ABCConnections, but the module is also called ABCConnections.
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 217 def self.remove_module_reference(params) return if params[:ignore_module_reference].nil? || params[:ignore_module_reference] == false frameworks = params[:frameworks] || [nil] frameworks.each do |framework| output_path = framework ? @xchelper.get_xcframework_path(framework) : @xchelper.xcframework_path params[:ignore_module_reference].each do |module_reference| begin command = "find #{output_path} -name '*.swiftinterface' -exec sed -i -e 's/#{module_reference}\\.//g' {} \\;" success = system(command) unless success UI.error("Failed to execute command: #{command}") else UI.success("▸ Removed module reference for #{module_reference} in all matching files") end rescue => e UI.error("Error: #{e.}") end end end end |
.run(params) ⇒ Object
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 14 def self.run(params) if Helper.xcode_at_least?('11.0.0') verify_delicate_params(params) params[:destinations] = update_destinations(params) params[:xcargs] = update_xcargs(params) @xchelper = Helper::CreateXcframeworkHelper.new(params) params[:destinations].each_with_index do |destination, framework_index| params[:destination] = destination params[:archive_path] = @xchelper.xcarchive_path_for_destination(framework_index) XcarchiveAction.run(params) end create_xcframework(params) remove_module_reference(params) sign_xcframework(params) zip_xcframework(params) delete_xcframework(params) copy_dSYMs(params) copy_BCSymbolMaps(params) clean(params) provide_shared_values(params) else UI.important('xcframework can be produced only using Xcode 11 and above') end end |
.sign_xcframework(params) ⇒ Object
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 117 def self.sign_xcframework(params) return if params[:code_sign_identity].nil? || params[:code_sign_identity] == false frameworks = params[:frameworks] || [nil] frameworks.each do |framework| xcframework = framework ? @xchelper.get_xcframework_path(framework) : @xchelper.xcframework_path UI.("▸ Signing xcframework with '#{params[:code_sign_identity]}' at path: #{xcframework}") begin command = "codesign --force --sign '#{params[:code_sign_identity]}' --timestamp=none #{xcframework}" UI.(command) Actions.sh(command) rescue StandardError => e UI.user_error!(e) end end end |
.update_destinations(params) ⇒ Object
337 338 339 340 341 342 343 344 345 346 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 337 def self.update_destinations(params) return available_destinations.values[0] if params[:destinations].nil? requested_destinations = params[:destinations].map do |requested_de| available_destinations.select { |available_de, _| available_de == requested_de }.values end UI.user_error!("Error: available destinations: #{available_destinations.keys}") if requested_destinations.any?(&:empty?) requested_destinations.flatten end |
.update_xcargs(params) ⇒ Object
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 308 def self.update_xcargs(params) xcargs = params[:override_xcargs].to_s.strip.split(' ') skip_install_set = false build_library_for_distribution_set = false xcargs.each do |arg| if arg.match?(/SKIP_INSTALL(=|\s+)/) skip_install_set = true unless arg.match?(/SKIP_INSTALL=NO/) xcargs.delete(arg) xcargs << 'SKIP_INSTALL=NO' end end if arg.match?(/BUILD_LIBRARY_FOR_DISTRIBUTION(=|\s+)/) build_library_for_distribution_set = true unless arg.match?(/BUILD_LIBRARY_FOR_DISTRIBUTION=YES/) xcargs.delete(arg) xcargs << 'BUILD_LIBRARY_FOR_DISTRIBUTION=YES' end end end xcargs << 'SKIP_INSTALL=NO' unless skip_install_set xcargs << 'BUILD_LIBRARY_FOR_DISTRIBUTION=YES' unless build_library_for_distribution_set xcargs.join(' ') end |
.verify_delicate_params(params) ⇒ Object
301 302 303 304 305 306 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 301 def self.verify_delicate_params(params) UI.user_error!('Error: :scheme is required option') if params[:scheme].nil? if !params[:destinations].nil? && !params[:destinations].kind_of?(Array) UI.user_error!('Error: :destinations option should be presented as Array') end end |
.zip_xcframework(params) ⇒ Object
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/fastlane/plugin/mobile_tools/actions/create_xcframework_action.rb', line 137 def self.zip_xcframework(params) return if params[:zip_xcframework].nil? || params[:zip_xcframework] == false # Check if the zip utility is installed unless system("which zip > /dev/null 2>&1") UI.important("zip utility is not installed. Installing...") system("brew install zip") || UI.crash!("Error: Failed to install zip utility") end frameworks = params[:frameworks] || [nil] if params[:combine_all_zip] Dir.mktmpdir do |tmpdir| frameworks.each do |framework| xcframework = framework ? @xchelper.get_xcframework_path(framework) : @xchelper.xcframework_path UI.("▸ Copying xcframework at path: #{xcframework} to tmp directory") FileUtils.cp_r(xcframework, tmpdir) end combined_zip_path = File.join(tmpdir, 'combined_xcframeworks.zip') UI.("▸ Listing all files in tmp directory") UI.(Dir.entries(tmpdir)) Dir.chdir(tmpdir) do command = "zip -r -X #{combined_zip_path} ." UI.("▸ Zipping all xcframeworks into one zip at path: #{combined_zip_path}") UI.(command) begin Actions.sh(command) rescue StandardError => e UI.user_error!(e) end end output_directory = @xchelper.output_directory || '.' final_zip_path = File.join(output_directory, 'combined_xcframeworks.zip') FileUtils.mv(combined_zip_path, final_zip_path) UI.("▸ Moved combined zip to output directory at path: #{final_zip_path}") end else frameworks.each do |framework| xcframework = framework ? @xchelper.get_xcframework_path(framework) : @xchelper.xcframework_path UI.("▸ Zipping xcframework at path: #{xcframework}") begin zip_path = "#{xcframework}.zip" FileUtils.rm_f(zip_path) if File.exist?(zip_path) command = "zip -r -X #{zip_path} #{xcframework}" UI.(command) Actions.sh(command) rescue StandardError => e UI.user_error!(e) end end end end |