# copy from https://github.com/CocoaPods/cocoapods-packager require 'cocoapods-tdf-bin/helpers/framework.rb' require 'English' require 'cocoapods-tdf-bin/config/config_builder' require 'shellwords' module CBin class Framework class Builder include Pod #Debug下还待完成 def initialize(spec, file_accessor, platform, source_dir, isRootSpec = true, build_model="Debug") @spec = spec @source_dir = source_dir @file_accessor = file_accessor @platform = platform @build_model = build_model @isRootSpec = isRootSpec #vendored_static_frameworks 只有 xx.framework 需要拼接为 xx.framework/xx by slj vendored_static_frameworks = file_accessor.vendored_static_frameworks.map do |framework| path = framework extn = File.extname path if extn.downcase == '.framework' path = File.join(path,File.basename(path, extn)) end path end @vendored_libraries = (vendored_static_frameworks + file_accessor.vendored_static_libraries).map(&:to_s) end def build defines = compile build_sim_libraries(defines) defines end def lipo_build(defines) UI.section("Building static Library #{@spec}") do # cp_framework_to_source_dir build_xcframework_for_ios # build_static_library_for_ios copy_resources # link_header end framework end private def cp_framework_to_source_dir framework_name = "#{@spec.name}.framework" framework_dir = "./build-arm64/#{framework_name}" if File.exist?('./build-arm64') target_dir = File.join(CBin::Config::Builder.instance.zip_dir,framework_name) zip_dir = CBin::Config::Builder.instance.zip_dir FileUtils.mkdir_p(zip_dir) unless File.exist?(zip_dir) `cp -fa #{framework_dir} #{target_dir}` end def cp_to_source_dir framework_name = "#{@spec.name}.framework" target_dir = File.join(CBin::Config::Builder.instance.zip_dir,framework_name) FileUtils.rm_rf(target_dir) if File.exist?(target_dir) zip_dir = CBin::Config::Builder.instance.zip_dir FileUtils.mkdir_p(zip_dir) unless File.exist?(zip_dir) `cp -fa #{@platform}/#{framework_name} #{target_dir}` end #模拟器,目前只支持 debug x86-64 def build_sim_libraries(defines) UI.message 'Building simulator libraries' # archs = %w[i386 x86_64] archs = ios_architectures_sim archs.map do |arch| xcodebuild(defines, "-sdk iphonesimulator ARCHS=\'#{arch}\' ", "build-#{arch}", @build_model) end end def static_libs_in_sandbox(build_dir = 'build') file = Dir.glob("#{build_dir}/lib#{target_name}.a") unless file UI.warn "file no find = #{build_dir}/lib#{target_name}.a" end file end =begin 制作 xcframework =end def build_xcframework_for_ios UI.message "Building ios libraries with archs #{ios_architectures}" framework_name = "#{@spec.name}.xcframework" output = File.join(CBin::Config::Builder.instance.zip_dir,framework_name) build_path = Pathname("build") build_path.path unless build_path.exist? # if is_debug_model libs = (ios_architectures + ios_architectures_sim) .map do |arch| library = "-framework build-#{arch}/#{@spec.name}.framework" library end UI.message "xcodebuild -create-xcframework #{libs.join(' ')} -output #{output}" `xcodebuild -create-xcframework #{libs.join(' ')} -output #{output}` end =begin lipo合并 二进制 =end def build_static_library_for_ios UI.message "Building ios libraries with archs #{ios_architectures}" framework_name = "#{@spec.name}.framework/#{@spec.name}" output = File.join(CBin::Config::Builder.instance.zip_dir,framework_name) build_path = Pathname("build") build_path.path unless build_path.exist? # if is_debug_model libs = (ios_architectures + ios_architectures_sim) .map do |arch| library = "build-#{arch}/#{@spec.name}.framework/#{@spec.name}" library end UI.message "lipo -create -output #{output} #{libs.join(' ')}" `lipo -create -output #{output} #{libs.join(' ')}` end def ios_build_options "ARCHS=\'#{ios_architectures.join(' ')}\' OTHER_CFLAGS=\'-fembed-bitcode -Qunused-arguments\'" end def ios_architectures # >armv7 # iPhone4 # iPhone4S # >armv7s 去掉 # iPhone5 # iPhone5C # >arm64 # iPhone5S(以上) # >i386 # iphone5,iphone5s以下的模拟器 # >x86_64 # iphone6以上的模拟器 archs = %w[arm64] # archs = %w[x86_64 arm64 armv7s i386] # @vendored_libraries.each do |library| # archs = `lipo -info #{library}`.split & archs # end archs end def ios_architectures_sim archs = %w[x86_64] archs end def compile defines = "GCC_PREPROCESSOR_DEFINITIONS='$(inherited)'" defines += ' ' defines += @spec.consumer(@platform).compiler_flags.join(' ') options = ios_build_options # if is_debug_model archs = ios_architectures # archs = %w[arm64 armv7 armv7s] archs.map do |arch| xcodebuild(defines, "ARCHS=\'#{arch}\' OTHER_CFLAGS=\'-fembed-bitcode -Qunused-arguments\'","build-#{arch}",@build_model) end # else # xcodebuild(defines,options) # end defines end def is_debug_model @build_model == "Debug" end def target_name #区分多平台,如配置了多平台,会带上平台的名字 # 如libwebp-iOS if @spec.available_platforms.count > 1 "#{@spec.name}-#{Platform.string_name(@spec.consumer(@platform).platform_name)}" else @spec.name end end def xcodebuild(defines = '', args = '', build_dir = 'build', build_model = 'Debug') unless File.exist?("Pods.xcodeproj") #cocoapods-generate v2.0.0 command = "xcodebuild #{defines} #{args} CONFIGURATION_BUILD_DIR=#{File.join(File.expand_path("..", build_dir), File.basename(build_dir))} clean build -configuration #{build_model} -target #{target_name} -project ./Pods/Pods.xcodeproj 2>&1" else command = "xcodebuild #{defines} #{args} CONFIGURATION_BUILD_DIR=#{build_dir} clean build -configuration #{build_model} -target #{target_name} -project ./Pods.xcodeproj 2>&1" end UI.message "command = #{command}" output = `#{command}`.lines.to_a if $CHILD_STATUS.exitstatus != 0 raise <<~EOF Build command failed: #{command} Output: #{output.map { |line| " #{line}" }.join} EOF Process.exit end end def link_header header_dir = "#{CBin::Config::Builder.instance.zip_dir}/#{@spec.name}.xcframework/ios-arm64/#{@spec.name}.framework/Headers" target_headers_dir = "#{CBin::Config::Builder.instance.zip_dir}/#{@spec.name}.xcframework/Headers" FileUtils.mkdir_p(target_headers_dir) unless File.exist?(target_headers_dir) Dir.foreach(header_dir) do |file| if file !="." and file !=".." `ln -s #{header_dir}/#{file} #{target_headers_dir}` end end end def copy_resources resource_dir = './build/*.bundle' resource_dir = './build-arm64/*.bundle' if File.exist?('./build-arm64') framework_name = "#{@spec.name}.xcframework" resource_target_dir = File.join(CBin::Config::Builder.instance.zip_dir,framework_name,"Resources") FileUtils.mkdir_p(resource_target_dir) unless File.exist?(resource_target_dir) bundles = Dir.glob(resource_dir) bundle_names = [@spec, *@spec.recursive_subspecs].flat_map do |spec| consumer = spec.consumer(@platform) consumer.resource_bundles.keys + consumer.resources.map do |r| File.basename(r, '.bundle') if File.extname(r) == 'bundle' end end.compact.uniq bundles.select! do |bundle| bundle_name = File.basename(bundle, '.bundle') bundle_names.include?(bundle_name) end if bundles.count > 0 UI.message "Copying bundle files #{bundles}" bundle_files = bundles.join(' ') `cp -rp #{bundle_files} #{resource_target_dir} 2>&1` end real_source_dir = @source_dir unless @isRootSpec spec_source_dir = File.join(Dir.pwd,"#{@spec.name}") unless File.exist?(spec_source_dir) spec_source_dir = File.join(Dir.pwd,"Pods/#{@spec.name}") end raise "copy_resources #{spec_source_dir} no exist " unless File.exist?(spec_source_dir) real_source_dir = spec_source_dir end resources = [@spec, *@spec.recursive_subspecs].flat_map do |spec| expand_paths(real_source_dir, spec.consumer(@platform).resources) end.compact.uniq if resources.count == 0 && bundles.count == 0 framework.delete_resources return end if resources.count > 0 #把 路径转义。 避免空格情况下拷贝失败 escape_resource = [] resources.each do |source| escape_resource << Shellwords.join(source) end escape_resource_str = escape_resource.join(' ') escape_resource_arr = escape_resource_str.split(' ') escape_resource_arr = escape_resource_arr.select do | source | suffix = File.extname(source) not expand_suffix(suffix) end if escape_resource_arr.count > 0 UI.message "Copying resources #{escape_resource_arr}" `cp -rp #{escape_resource_arr.join(' ')} #{resource_target_dir}` end end end def expand_paths(source_dir, path_specs) path_specs.map do |path_spec| Dir.glob(File.join(source_dir, path_spec)) end end def expand_suffix(suffix) case suffix when '.storyboard' then true when '.xib' then true when '.xcdatamodel' then true when '.xcdatamodeld' then true when '.xcmappingmodel' then true else false end end def framework @framework ||= begin framework = Framework.new(@spec.name, @platform.name.to_s) framework.make framework end end end end end