shell技巧6 - iOS最大构建版本文件大小

1、前言

在上传提交ipa文件成功后,发现AppStoreConnect后台一直不显示构建版本,最后邮箱收到:

App Store Connect

Dear Developer,

We identified one or more issues with a recent delivery for your app, "XXX". Please correct the following issues, then upload again.
Invalid Executable Size - The size of your app's executable file 'XXX.app/XXX' is 90374144 bytes, which exceeds the maximum allowed size of 80 MB.
Best regards,
The App Store Team

2、原因

Executable Sizeexecutable file ?这2个是什么东西?Executable 是可执行的意思,所以就是可执行的大小、可执行的文件。
每个 Mach-O 可执行文件(例如,app_name.app/app_name)不得超过这些正文段(__TEXT)文件大小的上限。具体 Mach-O 和 __TEXT 是什么,大家可以看看:ObjC 中国 - Mach-O 可执行文件

那从苹果文档最大构建版本文件大小 - App Store Connect 帮助,可以得到如下:

OS 版本 最大可执行文件大小 备注
iOS 9.0 或更高版本
Apple TVOS 9.0 或更高版本
500 MB 针对二进制文件中所有“__TEXT”部分的总和。
iOS 7.X 至 iOS 8.X 60 MB 针对每个 Architecture Slice(架构片段)中的“__TEXT”部分。
低于 iOS 7.0 80 MB 针对二进制文件中所有“__TEXT”部分的总和。
  • 注:Architecture Slice(架构片段)是针对特定架构的胖二进制布局文件的一部分。例如,一个胖二进制文件可能会包含针对 32 位和 64 位架构的片段。

综上,executable file 'XXX.app/XXX' is 90374144 bytes, which exceeds the maximum allowed size of 80 MB. 就是说,当前可执行文件“__TEXT”部分的总和为 90374144 bytes,超过了 80 MB

  • 是什么?
    当前可执行文件“__TEXT”部分的总和大小超过80MB!

  • 为什么?
    Mach-O可执行文件中包含__TEXT区域,__TEXT包含了被执行的代码,即编译所得到的机器码。所以,也就是我们的应用中代码量或引用的第三方库过大导致。

  • 怎么办?
    因为苹果限制,所以,

    • 第1步是减少第三方库或删除无用的代码。
    • 第2步是要找到什么计算__TEXT的大小?

3、__TEXT大小计算

怎么计算 __TEXTsize ? 在终端用 size XXX就可以打印当前应用的二进制的__TEXT字段大小:

➜  ~ size /Users/HTC/Downloads/WeChat/Payload/WeChat.app/WeChat 
__TEXT  __DATA  __OBJC  others  dec hex
53329920    7929856 0   3162112 64421888    3d70000 /Users/HTC/Downloads/WeChat/Payload/WeChat.app/WeChat (for architecture armv7)
57475072    13647872    0   4298047488  4369170432  1046c4000   /Users/HTC/Downloads/WeChat/Payload/WeChat.app/WeChat (for architecture arm64)
➜  ~ 

注:如果应用支持armv7arm64多种架构,就会显示多个,不会有i386x86_64是因为dis正式包已经移除模拟器架构。像现在新的应用,只支持arm64就不会显示具体架构:

➜  ~ size /Users/HTC/Downloads/Weixin/Payload/WeChat.app/WeChat
__TEXT  __DATA  __OBJC  others  dec hex
65355776    15040512    0   4298145792  4378542080  104fb4000

那么知道了计算公式,根据上面苹果给出的文档要求,需要做应用最低支持系统版本判断计算方式:

  1. iOS 7.X 至 iOS 8.X 每个架构最大为 60 MB
  2. iOS 9.0 或更高版本 500 MB 针对二进制文件中所有“__TEXT”部分的总和。
  3. 低于 iOS 7.0 80 MB 针对二进制文件中所有“__TEXT”部分的总和。

根据这个要求,会出现如下2种计算方式:

  • 计算所有“__TEXT”部分的总和
size app | awk '{print $1}' | grep -E '[0-9]' | awk '{sum += $1}; END {print sum}'

运行示例:

➜  ~ size /Users/HTC/Downloads/WeChat/Payload/WeChat.app/WeChat | awk '{print $1}' | grep -E '[0-9]' | awk '{sum += $1}; END {print sum}'
110804992

脚本说明:首先是用size 列出全部的架构的__TEXT大小,然后用 awkawk文本处理实战 | opengers)过滤出第一列的内容输出,然后用grep -E(扩展的正则表达式)过滤数字的行,最后是计算各行数字相加的和输出。

  • 分别列出各架构的大小
size app | awk '{print $1 "," $10}' | tail -n +2

运行示例:

➜  ~ size /Users/HTC/Downloads/WeChat/Payload/WeChat.app/WeChat | awk '{print $1 "," $10}' | tail -n +2
53329920,armv7)
57475072,arm64)

脚本说明:首先是用size 列出全部的架构的__TEXT(第1列)大小和架构名(第10列),之前用分号,间隔,然后输出,然后用 tail 输出从第2行开始的内容。

以上就是本文的核心内容,具体shell的技巧就不深讲啦,大家有兴趣可以搜索,awktail 命令了解更多,awk文本处理实战 | opengersLinux tail 命令 | 菜鸟教程

4、shell 编程实现

需要注意一下,上面输出的是bytes,而macOS是用1000来转换成MB。

Mebibyte - 维基百科,自由的百科全书

所有版本的Windows系统都会将一个2^20 bytes的文件显示为“1.00MB”,而10^6 bytes的文件显示为976kB。在Mac OS X 10.6之后将文件和磁盘大小都用Megabytes来表示,即将10^6 bytes的文件显示为1MB。

最终效果示例:

================================================
 Enter Mach-O executable file path: /Users/HTC/Downloads/WeChat/Payload/WeChat.app/WeChat
Place enter the number select minimum supported system version for the app? [ 1:gt iOS9 2:lt iOS7 3:iOS7.X~8.X] 
1
executable file size: 110.805 MB
⭕️ [Success] 110.805 MB, 符合苹果 iOS9 小于 500 MB 的要求!

================================================
 Enter Mach-O executable file path: /Users/HTC/Downloads/WeChat/Payload/WeChat.app/WeChat
Place enter the number select minimum supported system version for the app? [ 1:gt iOS9 2:lt iOS7 3:iOS7.X~8.X] 
2
executable file size: 110.805 MB
❌ [Error] 110.805 MB, 不符合苹果 iOS7 小于 80 MB 的要求!

================================================
 Enter Mach-O executable file path: /Users/HTC/Downloads/WeChat/Payload/WeChat.app/WeChat
Place enter the number select minimum supported system version for the app? [ 1:gt iOS9 2:lt iOS7 3:iOS7.X~8.X] 
3
armv7 executable file size: 53.3299 MB
⭕️ [Success] armv7 size: 53.3299 MB, 符合苹果 iOS 7.X 至 iOS 8.X 每个架构最大为 60 MB 的要求!
arm64 executable file size: 57.4751 MB
⭕️ [Success] arm64 size: 57.4751 MB, 符合苹果 iOS 7.X 至 iOS 8.X 每个架构最大为 60 MB 的要求!

具体的代码,可参考我的Github代码:

calculate_Mach-0__Text-Size .sh

#!/bin/bash

# 定义用到的变量
ExecutableFilePath=""

# 定义读取输入字符的函数
getExecutableFilePath() {
    # 输出换行,方便查看
    echo "================================================"
    # 监听输入并且赋值给变量
    read -p " Enter Mach-O executable file path: " ExecutableFilePath
    # 如果为空值,从新监听
    if test -z "$ExecutableFilePath"; then
        echo "Error! Should enter file path "
        getExecutableFilePath
    fi
}

getExecutableFilePath

echo "Place enter the number select minimum supported system version for the app? [ 1:gt iOS9 2:lt iOS7 3:iOS7.X~8.X] "

read number
while([[ $number != 1 ]] && [[ $number != 2 ]] && [[ $number != 3 ]])
do
echo "Error! Should enter 1 or 2 or 3"
echo "Place enter the number you want to export ? [ 1:app-store 2:ad-hoc 3:dev] "
read number
done


if [ $number != 3 ];then
    app_size=$(echo `size ${ExecutableFilePath} | awk '{print $1}' | grep -E '[0-9]' | awk '{sum += $1}; END {print sum/1000/1000}'`)
    echo 'executable file size:' ${app_size} 'MB'
    if [ $number == 1 ];then
        #iOS 9.0 或更高版本    500 MB    针对二进制文件中所有“__TEXT”部分的总和。
        #因为bc和awk都支持浮点数,可以使用bc进行处理:
        if [ `echo "$app_size > 500" | bc` -eq 1 ];then
            echo '❌ [Error]' ${app_size} 'MB,' '不符合苹果 iOS9 小于 500 MB 的要求!'
        else
            echo '⭕️ [Success]' ${app_size} 'MB,' '符合苹果 iOS9 小于 500 MB 的要求!'
        fi
    else
        #低于 iOS 7.0         80 MB     针对二进制文件中所有“__TEXT”部分的总和。
        if [ `echo "$app_size > 80" | bc` -eq 1 ];then
            echo '❌ [Error]' ${app_size} 'MB,' '不符合苹果 iOS7 小于 80 MB 的要求!'
        else
            echo '⭕️ [Success]' ${app_size} 'MB,' '符合苹果 iOS7 小于 80 MB 的要求!'
        fi
    fi
else
    app_size=$(echo `size "$ExecutableFilePath" | awk '{print $1 "," $10}' | tail -n +2`)
    # iOS 7.X 至 iOS 8.X 每个架构最大为 60 MB
    # echo $app_size 
    # 53329920,armv7) 57475072,arm64)
    arch_arr=(`echo $app_size | tr ' ' ' '`) 
    for each in ${arch_arr[@]}
    do
        size=`echo $each | awk -F',' '{print $1}' | awk '{sum += $1}; END {print sum/1000/1000}'`
        arch=`echo $each | awk -F',' '{print $2}'`
        # echo ${arch/%)/}  # 如果字符串arch以)结尾,则用空替换它
        echo ${arch/%)/} 'executable file size:' ${size} 'MB'
        if [ `echo "$size > 60" | bc` -eq 1 ];then
            echo '❌ [Error]' ${arch/%)/} 'size:' ${size} 'MB,' '不符合苹果 iOS 7.X 至 iOS 8.X 每个架构最大为 60 MB 的要求!'
        else
            echo '⭕️ [Success]' ${arch/%)/} 'size:' ${size} 'MB,' '符合苹果 iOS 7.X 至 iOS 8.X 每个架构最大为 60 MB 的要求!'
        fi
    done
fi

5、总结

本次脚本又顺利提高了效率!本文用到非常多的脚本命令,大家不必说记住,主要是知道用来做什么的,或者有需求时要搜索也可以,通过本次脚本编写,大家应该能发现shell脚本的强大而洁的语法,要学到以用,还需要多实践!

参考


  • 如有疑问,欢迎在评论区一起讨论!
  • 如有不正确的地方,欢迎指导!


注:本文首发于 iHTCboy's blog,如若转载,请注来源

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容