Xcodeproj源码: https://github.com/CocoaPods/Xcodeproj
xcodeproj文档:https://www.rubydoc.info/search/gems/xcodeproj
pbxprojHelper: https://github.com/yulingtianxia/pbxprojHelper
ruby库xcodeproj使用心得
Xcodeproj: 使用 ruby 自由的修改Xcode 工程文件
FileUtils
Dir文件遍历
Ruby调用shell脚本总结
利用Xcodeproj自动生成三方没有提供的模拟器版本
完整demo: https://github.com/eye1234456/PodSample/blob/main/auto_mix_simulator_sdk/auto_create_xcodeproj_archive_framework.rb
https://github.com/eye1234456/PodSample/blob/main/auto_mix_simulator_sdk/archive_tool_auto.sh
#!/usr/bin/ruby
require 'xcodeproj'
require "FileUtils"
# 解析framework并读取公共的header,生成对应的文件
def create_header_impl(basePath,framework_name)
# 添加.h .m文件 如下
project_dir = "#{basePath}/#{framework_name}"
source_dir = "#{project_dir}/#{framework_name}"
old_framework_header_dir = "#{basePath}/#{framework_name}.framework/Headers"
puts "basePath: #{basePath}"
puts "project_dir: #{project_dir}"
puts "source_dir: #{source_dir}"
puts "old_framework_header_dir: #{old_framework_header_dir}"
# 清空旧数据
if File.directory?(project_dir) && Dir.exist?(project_dir)
FileUtils.rm_rf(project_dir)
end
# 创建项目文件
FileUtils.mkdir_p(project_dir)
# 创建项目源文件
FileUtils.mkdir_p(source_dir)
####################
Dir.foreach(old_framework_header_dir) { |file_path|
new_header_file_path = File.join(source_dir, File.basename(file_path))
# 复制文件
if file_path.end_with?(".h")
old_header = "#{old_framework_header_dir}/#{file_path}"
puts "old header : #{file_path}"
puts "new header : #{new_header_file_path}"
FileUtils.cp(old_header, new_header_file_path)
end
}
end
# 创建一个xxx.xcodeproj文件
def auto_create_project(old_framework_path)
# puts "================="
# 添加.h .m文件 如下
basePath = File.dirname(old_framework_path) #当前根路径
framework_name = File.basename(old_framework_path, ".*") #=> "TTTPlayerKit"
project_name = framework_name
project_dir = "#{basePath}/#{project_name}"
source_dir = "#{project_dir}/#{project_name}"
project_path = "#{project_dir}/#{project_name}.xcodeproj"
old_framework_header_dir = "#{old_framework_path}/Headers"
puts "project_dir: #{project_dir}"
puts "source_dir: #{source_dir}"
puts "project_path: #{project_path}"
puts "old_framework_header_dir: #{old_framework_header_dir}"
########## 创建project类 核心逻辑###########
if File.exist?(project_path)
# 已经存在就打开
puts "already exists #{project_path}, open"
project = Xcodeproj::Project.open(project_path)
else
# 不存在就创建
puts "not exists #{project_path}, need create"
project = Xcodeproj::Project.new(project_path)
end
# 获取target
if project.targets.size > 0 && project.targets.include?(project_name)
# 已经存在了,则是打开一个文件
puts "already exists target #{project_name}, get"
target = project.targets.select do |t|
t == project_name
end
else
# 不存在,就创建
puts "not exists target #{project_name}, need create"
target = project.new_target(:framework, project_name, :ios, "9.0")
end
# 创建group
#找到要插入的group (参数中true表示如果找不到group,就创建一个group)
groupName = project_name
group = project.main_group.find_subpath(File.join(groupName),true)
#set一下sorce_tree
# group.set_source_tree('SOURCE_ROOT')
group.set_source_tree('SOURCE_ROOT')
# 需要天机的文件路径
# puts header_arr
Dir.foreach(source_dir) do |file_path|
header_file_path = "#{source_dir}/#{file_path}"
if header_file_path.end_with?(".h")
file_ref = group.new_file(header_file_path)
# 要加到Headers public里,framework特有
header = target.headers_build_phase.add_file_reference(file_ref)
# 设置访问权限为'public', 这样导出的framework才可以访问到
header.settings = { 'ATTRIBUTES' => ['Public'] }
elsif header_file_path.end_with?(".m")
# .m文件要加到项目里去
file_ref = group.new_file(header_file_path)
target.source_build_phase.add_file_reference(file_ref, true)
end
end
# 保存
project.save()
end
##########打包并合并framework###########
def auto_make_archive(base_path, project_scheme_name, configuration)
# 项目路径
project_dir_path = "#{base_path}/#{project_scheme_name}"
# FileUtils.cd(project_dir_path)
# .xcodeproj路径
xcodeproj_path = "#{project_dir_path}/#{project_scheme_name}.xcodeproj"
# 临时存放文件的文件夹名字
temp_archive_folder_name = "#{project_scheme_name}_#{project_scheme_name}_XCFramework"
temp_archive_folder_path = "#{project_dir_path}/#{temp_archive_folder_name}/simulator.xcarchive"
# set path for iOS device archive
# 删除之前生成的xcframework的文件夹
# 创建临时文件
FileUtils.rm_rf(temp_archive_folder_path)
FileUtils.mkdir_p(temp_archive_folder_path)
# 创建simulator的framework
sh_archive = %/
xcodebuild archive \
-project "#{xcodeproj_path}" \
-scheme "#{project_scheme_name}" \
-configuration "#{configuration}" \
-destination="iOS Simulator" \
-archivePath "#{temp_archive_folder_path}" \
-sdk iphonesimulator clean build \
SKIP_INSTALL=NO \
BUILD_LIBRARIES_FOR_DISTRIBUTION=YES
/
puts sh_archive
exec(sh_archive)
end
def auto_combine_framework(base_path, project_scheme_name, configuration)
# 生成xcframework
project_dir_path = "#{base_path}/#{project_scheme_name}"
temp_archive_folder_name = "#{project_scheme_name}_#{project_scheme_name}_XCFramework"
# 生成的xcframework
export_dir_path = "#{base_path}/export"
# 生成xcframework的路劲
export_xcframework_path = "#{export_dir_path}/#{project_scheme_name}.xcframework"
# 生成真机、模拟器二和一的路劲
export_mix_framework_path = "#{export_dir_path}/#{project_scheme_name}.framework"
# 创建导出文件地址
FileUtils.rm_rf(export_dir_path)
FileUtils.mkdir_p(export_dir_path)
# set path for iOS simulator archive
# 生成的模拟器的库的文件
simulator_archive_path = "#{project_dir_path}/#{temp_archive_folder_name}/simulator.xcarchive"
simulator_framework_path = "#{simulator_archive_path}/Products/Library/Frameworks/#{project_scheme_name}.framework"
iphone_framework_path = "#{base_path}/#{project_scheme_name}.framework"
sh_xcframework = %/
xcodebuild -create-xcframework \
-framework #{simulator_framework_path} \
-framework #{iphone_framework_path} \
-output "#{export_xcframework_path}"
/
puts sh_xcframework
exec(sh_xcframework)
# 生成真机模拟器二合一framework
# 先复制一个真机的版本到目标
# sh_mix_framework = %/
# cp -rf #{iphone_framework_path} #{export_mix_framework_path}\
# lipo -create \
# #{simulator_framework_path}/#{project_scheme_name} \
# #{export_mix_framework_path}/#{project_scheme_name} \
# -output #{export_mix_framework_path}/#{project_scheme_name}
# /
# exec(sh_mix_framework)
exec("open #{export_dir_path}")
end
#######调用#######
def create_framework_project(old_framework_path)
basePath = File.dirname(old_framework_path) #当前根路径
framework_name = File.basename(old_framework_path, ".*")
puts "old_framework_path: #{old_framework_path}"
puts "basePath: #{basePath}"
puts "framework_name: #{framework_name}"
# 1、解析旧的framework,并复制对应的文件
create_header_impl(basePath,framework_name)
# 2、创建项目
auto_create_project(old_framework_path)
# 3、生成xrchive
# auto_make_archive(basePath, framework_name, "Debug")
# 4、合并成xcframework
# auto_combine_framework(basePath, framework_name, "Debug")
# exec("sh #{basePath}/archive_tool_auto.sh")
end
old_framework_path = "/xxx/xxxx/xxx/AAA.framework"
create_framework_project(old_framework_path)
另外添加使用shell脚本打包合并
#!/bin/bash
packaging(){
# set framework folder name
# 工程名称(Project的名字)
PROJECT_NAME=$1
# scheme名称
SCHEME_NAME=$2
Configuration=$3
# 项目所在的文件
PROJECT_DIR=$4
# 创建的真机的库的地址
IPHONE_Framework_folder=$5
IPHONE_Framework_PATH="${IPHONE_Framework_folder}/${SCHEME_NAME}.framework"
# 导出的xcframework 和 合并的framework的文件夹
EXPORT_FOLDER_PATH=$6
xcodeproj_path="${PROJECT_DIR}/${PROJECT_NAME}.xcodeproj"
# 临时存放文件的文件夹名字
FRAMEWORK_FOLDER_NAME="${PROJECT_NAME}_${SCHEME_NAME}_XCFramework"
TEMP_FRAMEWORK_DIR="${PROJECT_DIR}/${FRAMEWORK_FOLDER_NAME}"
# 生成的xcframework
# 生成xcframework的路劲
EXPORT_XCFRAMEWORK_PATH="${EXPORT_FOLDER_PATH}/${SCHEME_NAME}.xcframework"
# 生成真机、模拟器二和一的路劲
EXPORT_MIX_FRAMEWORK_PATH="${EXPORT_FOLDER_PATH}/${SCHEME_NAME}.framework"
# set path for iOS simulator archive
# 生成的模拟器的库的文件
SIMULATOR_ARCHIVE_PATH="${TEMP_FRAMEWORK_DIR}/simulator.xcarchive"
# set path for iOS device archive
# 生成的真机的库的文件
IOS_DEVICE_ARCHIVE_PATH="${TEMP_FRAMEWORK_DIR}/iOS.xcarchive"
# 删除之前生成的xcframework的文件夹
# 创建临时文件
rm -rf "${TEMP_FRAMEWORK_DIR}"
mkdir "${TEMP_FRAMEWORK_DIR}"
# 创建导出文件地址
rm -rf "${EXPORT_FOLDER_PATH}"
mkdir "${EXPORT_FOLDER_PATH}"
echo '==================start================'
total_startTime_s=`date +%s`
echo '开始模拟器archive'
archive_simulator_startTime_s=`date +%s`
# 创建simulator的framework
xcodebuild archive \
-project ${xcodeproj_path} \
-scheme ${SCHEME_NAME} \
-configuration ${Configuration} \
-destination="iOS Simulator" \
-archivePath "${SIMULATOR_ARCHIVE_PATH}" \
-sdk iphonesimulator clean build \
SKIP_INSTALL=NO \
BUILD_LIBRARIES_FOR_DISTRIBUTION=YES
archive_simulator_endTime_s=`date +%s`
echo "模拟器archive时长:$[$archive_simulator_endTime_s - $archive_simulator_startTime_s]"
echo '结束模拟器archive'
#Creating XCFramework
# 创建的模拟器库的地址
SIMULATOR_Framework_PATH="${SIMULATOR_ARCHIVE_PATH}/Products/Library/Frameworks/${SCHEME_NAME}.framework"
# 生成xcframework
echo '开始合成xcframework'
create_xcframework_startTime_s=`date +%s`
xcodebuild -create-xcframework \
-framework ${SIMULATOR_Framework_PATH} \
-framework ${IPHONE_Framework_PATH} \
-output "${EXPORT_XCFRAMEWORK_PATH}"
create_xcframework_endTime_s=`date +%s`
echo "合成xcframework时长:$[$create_xcframework_endTime_s - $create_xcframework_startTime_s]"
echo '结束合成xcframework'
# 生成真机模拟器二合一framework
echo '开始合成framework'
create_mix_framework_startTime_s=`date +%s`
# 先复制一个真机的版本到目标
cp -rf ${IPHONE_Framework_PATH} ${EXPORT_MIX_FRAMEWORK_PATH}
# 将真机和模拟器合并成一个
lipo -create \
"${SIMULATOR_Framework_PATH}/${SCHEME_NAME}" \
"${IPHONE_Framework_PATH}/${SCHEME_NAME}" \
-output "${EXPORT_MIX_FRAMEWORK_PATH}/${SCHEME_NAME}"
create_mix_framework_endTime_s=`date +%s`
echo "合成framework时长:$[$create_mix_framework_endTime_s - $create_mix_framework_startTime_s]"
echo '结束合成framework'
rm -rf "${TEMP_FRAMEWORK_DIR}"
total_endTime_s=`date +%s`
echo '==================end================'
echo "总共时长:$[$total_endTime_s - $total_startTime_s]"
open "${EXPORT_FOLDER_PATH}"
}
#函数调用
# $1 工程名 $2 scheme名字 $3 Release还是Debug $4项目文件地址 $5真机framework地址 $6导出文件夹地址
# source xxxx.sh
# packaging "HelloSDK" "AAASDK" "Release"
packaging_archive(){
sdkName=$1
pwd_path="/xxx/xxx/xxx"
PROJECT_DIR="${pwd_path}/${sdkName}"
# $1 工程名 $2 scheme名字 $3 Release还是Debug $4项目文件地址 $5真机framework地址 $6导出文件夹地址
iphone_framework_path="${pwd_path}"
export_path_folder="${pwd_path}/export"
packaging ${sdkName} ${sdkName} "Debug" "${PROJECT_DIR}" "${iphone_framework_path}" "${export_path_folder}"
}
# 打包
packaging_archive "AAA"
网友评论