module Jxedt class BinarySandbox attr_accessor :standard_sandbox def initialize(path=nil) @sandbox_path = path end def self.from_sandbox(sandbox) search_path = Jxedt.config.binary_dir binary_dir = search_path.empty? ? nil : sandbox.standard_sandbox_root + search_path binary_sandbox = BinarySandbox.new(binary_dir) binary_sandbox.standard_sandbox = sandbox binary_sandbox end def binary_dir @binary_dir ||= Pathname.new(@sandbox_path) end def target_paths return [] if @sandbox_path.nil? return [] unless binary_dir.exist? return [] if @standard_sandbox.source_lockfile.nil? @targets ||= begin prebuild_targets = binary_dir.children().map do |target_path| if target_path.directory? && (not target_path.children.empty?) hash_key = @standard_sandbox.source_lockfile.spec_checksums_hash_key(target_path.basename.to_s) next if hash_key.nil? checksum_files = target_path.children().select { |path| path.extname == '.checksum' } checksum_exists = (target_path + "#{hash_key}.checksum").exist? target_path if checksum_files.count == 1 && checksum_exists end end.reject(&:nil?).uniq prebuild_targets end @targets end def existed_target_names(name) target_paths.select { |pair| "#{pair.basename}" == "#{name}" }.map { |pair| pair.basename } end def framework_folder_path_for_target_name(name) target_paths.select { |pair| pair.basename == name }.last end def prebuild_vendored_frameworks(name) target_path = target_paths.select { |pair| "#{pair.basename}" == "#{name}" }.last return [] if target_path.nil? configuration_enable = target_path.children().select { |path| "#{path.basename}" == 'Debug' || "#{path.basename}" == 'Release' }.count == 2 if configuration_enable xcconfig_configuration_alias = Jxedt.config.xcconfig_configuration_alias ["#{xcconfig_configuration_alias}-Release/*.{framework,xcframework}"] else ["*.{framework,xcframework}"] end end def prebuild_bundles(name) target_path = target_paths.select { |pair| "#{pair.basename}" == "#{name}" }.last return [] if target_path.nil? configuration_enable = target_path.children().select { |path| "#{path.basename}" == 'Debug' || "#{path.basename}" == 'Release' }.count == 2 if configuration_enable xcconfig_configuration_alias = Jxedt.config.xcconfig_configuration_alias ["#{xcconfig_configuration_alias}-Release/*.bundle"] else ["*.bundle"] end end end class Prebuild def initialize(source_installer) @source_installer = source_installer end def targets_to_prebuild explicit_prebuild_pod_names = @source_installer.aggregate_targets.flat_map { |target| target.target_definition.explicit_prebuild_pod_names }.uniq reject_prebuild_pod_names = @source_installer.aggregate_targets.flat_map { |target| target.target_definition.reject_prebuild_pod_names }.uniq targets = all_supports_targets.select { |target| next if reject_prebuild_pod_names.include?(target.pod_name) explicit_prebuild_pod_names.include?(target.pod_name) || Jxedt.config.all_binary_enabled? } targets end def all_supports_targets targets = @source_installer.pod_targets.select { |target| # 排除不需要编译 next unless target.should_build? # 排除target name和pod name不一致的(有两种情况) # 1. 同一个pod,在两个target下使用了不同的subspec # 2. 同一个pod,在两个不同ios版本的target种引用 next unless target.name == target.pod_name true } # 排除本地pod targets.reject! { |target| @source_installer.sandbox.local?(target.pod_name) } unless Jxedt.config.dev_pods_enabled? # 配置中排除的pods targets.reject! { |target| Jxedt.config.excluded_pods.include?(target.pod_name) } # target中可能存在需要编译的resource文件 Jxedt.config.disable_resource_compilable_pods? && targets.reject! { |target| # 因为编译library静态库会把需要编译的文件拷贝到宿主工程编译,后面我们要把静态库组装成framework,所以需要过滤掉 resource_extension_compilable = false target.resource_paths.each {|name, resources| resources.each {|resource_file| resource_extname = Pathname.new(resource_file).basename.extname resource_extension_compilable = target.class.resource_extension_compilable?(resource_extname) break if resource_extension_compilable } break if resource_extension_compilable } resource_extension_compilable } targets end def build binary_sandbox = Jxedt::BinarySandbox.from_sandbox(@source_installer.sandbox) existed_target_names = binary_sandbox.target_paths.map { |pair| pair.basename.to_s } targets = targets_to_prebuild.reject { |target| existed_target_names.include?(target.name.to_s) } Pod::UI.puts "Prebuild total count: #{targets.size}" Pod::UI.puts "Prebuild targets: #{ targets.map(&:name)}" return [] if targets.empty? require_relative 'pod-room/xcodebuild_command' clear_output_path case targets[0].platform.name when :ios, :tvos, :watchos Jxedt.config.support_configurations.each do |configuration| Pod::UI.puts "Prebuild configuration: #{configuration}" options = make_options options[:configuration] = configuration options[:targets] = targets options[:output_path] = output_path + configuration Jxedt::XcodebuildCommand.new(options).run end when :osx Jxedt.config.support_configurations.each do |configuration| Pod::UI.puts "Prebuild configuration: #{configuration}" options = make_options xcodebuild( sandbox: options[:sandbox], targets: targets, configuration: configuration, sdk: "macosx", args: options[:args] ) end else raise "Unsupported platform for '#{targets[0].name}': '#{targets[0].platform.name}'" end make_prebuild(targets) clear_output_path targets.map(&:pod_name).to_a end def build_targets(names: nil, binary_output: nil) targets = all_supports_targets targets.select! { |target| names.include?(target.pod_name) } if names && names.is_a?(Array) && names.size > 0 Pod::UI.puts "Prebuild total count: #{targets.size}" Pod::UI.puts "Prebuild targets: #{ targets.map(&:name)}" return [] if targets.empty? require_relative 'pod-room/xcodebuild_command' clear_output_path case targets[0].platform.name when :ios, :tvos, :watchos Jxedt.config.support_configurations.each do |configuration| Pod::UI.puts "Prebuild configuration: #{configuration}" options = make_options options[:configuration] = configuration options[:targets] = targets options[:output_path] = output_path + configuration Jxedt::XcodebuildCommand.new(options).run end when :osx Jxedt.config.support_configurations.each do |configuration| Pod::UI.puts "Prebuild configuration: #{configuration}" options = make_options xcodebuild( sandbox: options[:sandbox], targets: targets, configuration: configuration, sdk: "macosx", clean_build: Jxedt.config.clean_build?, args: options[:args] ) end else raise "Unsupported platform for '#{targets[0].name}': '#{targets[0].platform.name}'" end binary_output = Pathname.new(binary_output) make_prebuild(targets, binary_output) clear_output_path targets.map(&:pod_name).to_a end def make_prebuild(targets, binary_output=nil) lockfile = @source_installer.lockfile checksums = lockfile.internal_data["SPEC CHECKSUMS"] || {} checkout_options = lockfile.internal_data["CHECKOUT OPTIONS"] || {} # 目标binary路径 binary_path = output_path.parent binary_path = binary_output if binary_output && binary_output.parent.exist? prebuild_targets = targets.map(&:name).to_a prebuild_targets.map {|target_name| target_path = binary_path + target_name target_path.rmtree if target_path.exist? } # make prebuild files configurations = Jxedt.config.support_configurations Jxedt.config.support_configurations.each do |configuration| configuration_path = output_path + configuration next unless configuration_path.exist? configuration_path.children().each do |child| if child.directory? and (not child.children.empty?) name = child.basename.to_s next unless prebuild_targets.include?(name) target_path = binary_path + name target_path += configuration if configurations.size > 1 target_path.mkpath unless target_path.exist? command = "cp -r #{child}/ #{target_path}" `#{command}` # touch checksum checksum = nil checksum = checkout_options[name][:commit] unless checkout_options[name].nil? # commitid有值则使用commitid checksum = checksums[name] if checksum.nil? `echo #{command} "\n" >> #{binary_path}/#{name}/#{checksum}.checksum` unless checksum.nil? end end end end def make_options options = {} options[:sandbox] = @source_installer.sandbox options[:build_dir] = build_dir options[:output_path] = output_path options[:clean_build] = Jxedt.config.clean_build? options[:bitcode_enabled] = Jxedt.config.bitcode_enabled? options[:device_build_enabled] = Jxedt.config.device_build_enabled? options[:simulator_build_enabled] = Jxedt.config.simulator_build_enabled? options[:disable_dsym] = Jxedt.config.disable_dsym? options[:log_path] = Jxedt.config.build_log_path options[:args] = Jxedt.config.build_args options end def build_dir sandbox = @source_installer.sandbox sandbox.root.parent + "build" end def output_path sandbox = @source_installer.sandbox sandbox.standard_sandbox_root + Jxedt.config.binary_dir + "GeneratedFrameworks" end def clear_output_path path = output_path path.rmtree if path.exist? end end end