-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
234 lines (111 loc) · 124 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>电脑使用技巧(Computer Skills)</title>
<link href="/2024/01/01/computer-skills/"/>
<url>/2024/01/01/computer-skills/</url>
<content type="html"><![CDATA[<h1 id="电脑使用技巧-Computer-Skills"><a href="#电脑使用技巧-Computer-Skills" class="headerlink" title="电脑使用技巧(Computer Skills)"></a>电脑使用技巧(Computer Skills)</h1><h2 id="Unix-Shell-命令"><a href="#Unix-Shell-命令" class="headerlink" title="Unix Shell 命令"></a>Unix Shell 命令</h2><h3 id="查找与删除文件"><a href="#查找与删除文件" class="headerlink" title="查找与删除文件"></a>查找与删除文件</h3><ul><li><p>在当前文件夹中查找与删除(忽略子文件夹,即<font color=red>非递归</font>>查找与删除)</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">ls *.o</span><br><span class="line">rm *.o</span><br></pre></td></tr></table></figure></li><li><p>在当前文件夹与子文件夹中查找与删除(即<font color=red>递归</font>查找与删除)</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">find . -name "*.o" -type f</span><br><span class="line">find . -name "*.o" -type f -delete</span><br></pre></td></tr></table></figure></li></ul>]]></content>
<tags>
<tag> Computer,电脑使用技巧, Mac </tag>
</tags>
</entry>
<entry>
<title>Cmake Tutorial</title>
<link href="/2023/06/24/cmake-tutorial/"/>
<url>/2023/06/24/cmake-tutorial/</url>
<content type="html"><![CDATA[<h1 id="CMake-快速指南"><a href="#CMake-快速指南" class="headerlink" title="CMake 快速指南"></a>CMake 快速指南</h1><p>首先,请自行在电脑里安装好 CMake, 可以去官网直接下载 GUI 程序: <a href="https://cmake.org/download/" target="_blank" rel="noopener">https://cmake.org/download/</a>, </p><p>也可以通过命令行工具直接安装。</p><h2 id="创建项目文件"><a href="#创建项目文件" class="headerlink" title="创建项目文件"></a>创建项目文件</h2><p>接下来,我们一起来创建一个名为 Demo 的项目,来介绍 CMake 的使用,以下我就直接贴上命令了,直接在命令终端敲一行,执行一行就OK。</p><ul><li><p>创建项目目录结构</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">mkdir Demo</span><br><span class="line">cd Demo</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> <span class="built_in">source</span> 用于存放源文件</span></span><br><span class="line">mkdir source</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> include 用于存放提供给外界使用的头文件</span></span><br><span class="line">mkdir include</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> build 用于生成工程及编译</span></span><br><span class="line">mkdir build</span><br></pre></td></tr></table></figure></li><li><p>在 <strong>Demo</strong> 根目录下新增 <code>CMakeLists.txt</code> 空文件。</p></li><li><p>在 <strong>Demo</strong> 根目录新增源代码 <code>main.cpp</code></p><figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><iostream></span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="built_in">std</span>::<span class="built_in">cout</span> << <span class="string">"Hello CMake, I love you!"</span> << <span class="built_in">std</span>::<span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>在 <strong>include</strong> 文件夹中新增 <code>Tutor.h</code> 头文件</p></li><li>在 <strong>source</strong> 文件夹中新增 <code>Tutor.cpp</code> 源文件</li></ul><h2 id="编写-CMakeLists-txt"><a href="#编写-CMakeLists-txt" class="headerlink" title="编写 CMakeLists.txt"></a>编写 CMakeLists.txt</h2><h4 id="仅支持本机"><a href="#仅支持本机" class="headerlink" title="仅支持本机"></a>仅支持本机</h4><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">cmake_minimum_required(VERSION 3.10)</span><br><span class="line"></span><br><span class="line">set(projectName Demo)</span><br><span class="line"><span class="meta">project($</span><span class="bash">{projectName} VERSION 1.0.0)</span></span><br><span class="line"></span><br><span class="line">set(CMAKE_CXX_STANDARD 11)</span><br><span class="line"></span><br><span class="line"><span class="meta">add_executable($</span><span class="bash">{projectName} main.cpp <span class="built_in">source</span>/Tutor.cpp)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">target_include_directories($</span><span class="bash">{projectName} PUBLIC <span class="variable">${CMAKE_CURRENT_SOURCE_DIR}</span>/include)</span></span><br></pre></td></tr></table></figure><h4 id="跨平台"><a href="#跨平台" class="headerlink" title="跨平台"></a>跨平台</h4><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">cmake_minimum_required(VERSION 3.10)</span><br><span class="line"></span><br><span class="line">set(projectName Demo)</span><br><span class="line"><span class="meta">project($</span><span class="bash">{projectName} VERSION 1.0.0)</span></span><br><span class="line"></span><br><span class="line">set(CMAKE_CXX_STANDARD 11)</span><br><span class="line"></span><br><span class="line">if(WIN32)</span><br><span class="line"> set(CMAKE_CXX_COMPILER "MSVC")</span><br><span class="line">elseif(UNIX)</span><br><span class="line"> set(CMAKE_CXX_COMPILER "g++")</span><br><span class="line">endif()</span><br><span class="line"></span><br><span class="line"><span class="meta">add_executable($</span><span class="bash">{projectName} main.cpp <span class="built_in">source</span>/Tutor.cpp)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">target_include_directories($</span><span class="bash">{projectName} PUBLIC <span class="variable">${CMAKE_CURRENT_SOURCE_DIR}</span>/include)</span></span><br></pre></td></tr></table></figure><h2 id="构建"><a href="#构建" class="headerlink" title="构建"></a>构建</h2><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">cd build</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 根据 CMakeLists.txt 文件生成工程</span></span><br><span class="line">cmake ..</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 将源文件编译成目标文件,并进行链接, 生成可执行文件</span></span><br><span class="line">make</span><br></pre></td></tr></table></figure><h2 id="运行程序"><a href="#运行程序" class="headerlink" title="运行程序"></a>运行程序</h2><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 编译完成后,在 build 目录下会生成可执行文件 Demo</span></span><br><span class="line">./Demo</span><br></pre></td></tr></table></figure><h2 id="附录"><a href="#附录" class="headerlink" title="附录"></a>附录</h2><p>本文用到的 Demo 示例:<a href="https://gitee.com/evanxlh/cmake-tutorials.git" target="_blank" rel="noopener">https://gitee.com/evanxlh/cmake-tutorials.git</a></p>]]></content>
<categories>
<category> Build & Compile </category>
</categories>
<tags>
<tag> CMake, Compile, Cross platform, 跨平台 </tag>
</tags>
</entry>
<entry>
<title>使用 Hexo + github.io 搭建个人博客</title>
<link href="/2023/06/24/hexo-create-personal-blog/"/>
<url>/2023/06/24/hexo-create-personal-blog/</url>
<content type="html"><![CDATA[<h1 id="Hexo-github-io-搭建个人博客"><a href="#Hexo-github-io-搭建个人博客" class="headerlink" title="Hexo + github.io 搭建个人博客"></a>Hexo + github.io 搭建个人博客</h1><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p>在安装 Hexo 之前,您需要在您的电脑里安装以下内容:</p><ul><li>Git</li><li>Node.js</li></ul><p>接下来在命令终端执行以下两行命令就可以完成 Hexo 安装:</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 使用 npm 安装 Hexo</span></span><br><span class="line">npm install -g hexo-cli</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> Hexo 3.0 把服务器独立成了个别模块,您必须先安装 hexo-server 才能使用</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> Hexo Server 可以用于本地预览</span></span><br><span class="line">npm install hexo-server --save</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 安装部署到远程git仓库的插件,方便后面的一键部署</span></span><br><span class="line">npm install hexo-deployer-git --save</span><br></pre></td></tr></table></figure><p>安装这一部分的内容,您都可以在 <a href="https://hexo.io/zh-cn/docs/" target="_blank" rel="noopener">Hexo 官方文档</a> 中找到,这里就不再累赘。</p><h2 id="个人博客"><a href="#个人博客" class="headerlink" title="个人博客"></a>个人博客</h2><p>安装好 Hexo 后,我们就可以开始搭建我们自己的博客啦!下面将以创建一个名为 <strong>MyBlog</strong> 的博客为例:</p><h3 id="建站"><a href="#建站" class="headerlink" title="建站"></a>建站</h3><p>在命令终端中输入以下命令,您就可以在本机查看你的个人博客了,是不是非常方便呢?</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> Hexo 在指定文件夹中新建您的博客项目</span></span><br><span class="line">hexo init MyBlog</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 启动Hexo 服务器, 您就可以在 http://localhost:4000 浏览你的个人博客了</span></span><br><span class="line">cd MyBlog</span><br><span class="line">hexo server</span><br></pre></td></tr></table></figure><p>更多内容,请点击 <a href="https://hexo.io/zh-cn/docs/setup" target="_blank" rel="noopener">Hexo 建站</a>。</p><h3 id="创建-github-io-仓库"><a href="#创建-github-io-仓库" class="headerlink" title="创建 github.io 仓库"></a>创建 github.io 仓库</h3><p>github.io 仓库用于部署你的个人博客</p><ol><li>建立名为 <code><你的 GitHub 用户名>.github.io</code> 的储存库,若之前已将 Hexo 上传至其他储存库,将该储存库重命名即可。</li><li><p>将 Hexo 文件夹中的文件 push 到储存库的默认分支,默认分支通常名为 <code>main</code>,旧一点的储存库可能名为 <code>master</code>。</p></li><li><p>将 <code>main</code> 分支 push 到 GitHub:</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> git push -u origin main</span></span><br></pre></td></tr></table></figure></li><li><p>前往 <code>https://<你的 GitHub 用户名>.github.io</code> 查看网站</p></li></ol><h3 id="开始写作"><a href="#开始写作" class="headerlink" title="开始写作"></a>开始写作</h3><ul><li><p>创建新文章:以一篇名为 <<<strong>My First Article</strong>>> 文章为例:</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">hexo new "My First Article"</span><br></pre></td></tr></table></figure></li><li><p>编辑文章:<code>MyBlog/source/_posts/My-First-Article.md</code></p><p>新创建的文章默认位于 source/_posts 目录下面,直接编辑 mark down 文件即可。</p></li></ul><p>有关更多写作的内容,请查看 <a href="https://hexo.io/zh-cn/docs/writing" target="_blank" rel="noopener">Hexo 写作</a> 。</p><h3 id="部署"><a href="#部署" class="headerlink" title="部署"></a>部署</h3><p>在部署前,您需要修改 <strong>MyBlog</strong> 根目录下的 <code>_config.yml</code> 文件,需要您填写部署的目标仓库地址(可以配多个),例如:</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">deploy:</span><br><span class="line"> type: git</span><br><span class="line"> repo:</span><br><span class="line"> # 2021.8.13, github 不再支持密码登陆,所以这里使用 ssh 方式登陆</span><br><span class="line"> github: [email protected]:username/username.github.io.git</span><br><span class="line"> gitee: [email protected]:username/myblog.git</span><br><span class="line"> branch: master</span><br></pre></td></tr></table></figure><font color=red>2021.8.13起,Github要求使用基于令牌的身份验证, 不再支持密码登陆,详情见: https://zhuanlan.zhihu.com/p/401978754</font><p>部署的目标仓库设置好的,就可以一键生成静态网页,并进行发布:</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">hexo g -d</span><br></pre></td></tr></table></figure>]]></content>
<tags>
<tag> Blog, 博客, Hexo, Github io </tag>
</tags>
</entry>
<entry>
<title>String in Swift</title>
<link href="/2021/12/25/string-in-swift/"/>
<url>/2021/12/25/string-in-swift/</url>
<content type="html"><![CDATA[<h1 id="String-in-Swift"><a href="#String-in-Swift" class="headerlink" title="String in Swift"></a>String in Swift</h1><p>在使用 Swift 操作字符串时,尤其是在处理 text filed/text view 时,需要做输入限制的时候,难免会遇到一些坑,甚至 crash。原因就是不同用户有着不一样的输入习惯,有些用户喜欢用纯文字,有些喜欢使用表情(emoj) 等,如果处理不当,就很容易出现问题。下面就是我遇到的一个 emoj 问题,因为刚开始我没有使用 utf16 来处理 index, 导致 index 越界发生 crash。以下为正确的用法:</p><h2 id="NSRange-Range-互转"><a href="#NSRange-Range-互转" class="headerlink" title="NSRange Range 互转"></a>NSRange Range<String.Index> 互转</h2><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">extension</span> <span class="title">String</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">func</span> <span class="title">range</span><span class="params">(from nsRange: NSRange)</span></span> -> <span class="type">Range</span><<span class="type">String</span>.<span class="type">Index</span>>? {</span><br><span class="line"> <span class="keyword">let</span> view = utf16</span><br><span class="line"> <span class="keyword">guard</span> <span class="keyword">let</span> start = view.index(view.startIndex, offsetBy: nsRange.location, limitedBy: view.endIndex),</span><br><span class="line"> <span class="keyword">let</span> end = view.index(start, offsetBy: nsRange.length, limitedBy: view.endIndex),</span><br><span class="line"> <span class="keyword">let</span> startIndex = <span class="type">String</span>.<span class="type">Index</span>(start, within: <span class="keyword">self</span>),</span><br><span class="line"> <span class="keyword">let</span> endIndex = <span class="type">String</span>.<span class="type">Index</span>(end, within: <span class="keyword">self</span>) <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> startIndex..<endIndex</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">func</span> <span class="title">nsRange</span><span class="params">(from range: Range<String.Index>)</span></span> -> <span class="type">NSRange?</span> {</span><br><span class="line"> <span class="keyword">let</span> view = utf16</span><br><span class="line"> <span class="keyword">guard</span> <span class="keyword">let</span> start = range.lowerBound.samePosition(<span class="keyword">in</span>: view),</span><br><span class="line"> <span class="keyword">let</span> end = range.upperBound.samePosition(<span class="keyword">in</span>: view) <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> <span class="type">NSRange</span>(</span><br><span class="line"> location: view.<span class="built_in">distance</span>(from: view.startIndex, to: start),</span><br><span class="line"> length: view.<span class="built_in">distance</span>(from: start, to: end)</span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="在处理文字输入时使用-Range"><a href="#在处理文字输入时使用-Range" class="headerlink" title="在处理文字输入时使用 Range"></a>在处理文字输入时使用 Range</h2><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ViewController</span>: <span class="title">NSViewController</span>, <span class="title">NSTextViewDelegate</span> </span>{</span><br><span class="line"> <span class="meta">@IBOutlet</span> <span class="keyword">var</span> textView: <span class="type">NSTextView!</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">override</span> <span class="function"><span class="keyword">func</span> <span class="title">viewDidLoad</span><span class="params">()</span></span> {</span><br><span class="line"> <span class="keyword">super</span>.viewDidLoad()</span><br><span class="line"> textView.delegate = <span class="keyword">self</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">func</span> <span class="title">textView</span><span class="params">(<span class="number">_</span> textView: NSTextView, shouldChangeTextIn affectedCharRange: NSRange, replacementString: String?)</span></span> -> <span class="type">Bool</span> {</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">let</span> string = textView.string</span><br><span class="line"> <span class="keyword">guard</span> <span class="keyword">let</span> range = string.range(from: affectedCharRange) <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">let</span> newString = string.replacingCharacters(<span class="keyword">in</span>: range, with: replacementString ?? <span class="string">""</span>)</span><br><span class="line"> <span class="built_in">print</span>(finalString)</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> Swift </category>
</categories>
<tags>
<tag> Swift </tag>
<tag> macOS </tag>
<tag> NSTextView </tag>
<tag> UITextFiled </tag>
<tag> String </tag>
</tags>
</entry>
<entry>
<title>在 MacOS 上编译 FFmpeg 相关音视频库</title>
<link href="/2020/07/19/compile-ffmpeg-on-macos/"/>
<url>/2020/07/19/compile-ffmpeg-on-macos/</url>
<content type="html"><![CDATA[<h1 id="MacOS-编译-FFmpeg-相关音视频库-01"><a href="#MacOS-编译-FFmpeg-相关音视频库-01" class="headerlink" title="MacOS 编译 FFmpeg 相关音视频库 - 01"></a>MacOS 编译 FFmpeg 相关音视频库 - 01</h1><p>做音视频开发,除了平台提供的SDK外,我们经常会用到一些优秀的第三方开源库,这里我们将介绍下lame, fad-aac, x264的编译及使用。</p><h2 id="音视频库介绍"><a href="#音视频库介绍" class="headerlink" title="音视频库介绍"></a>音视频库介绍</h2><h3 id="lame"><a href="#lame" class="headerlink" title="lame"></a><a href="http://lame.sourceforge.net/download.php" target="_blank" rel="noopener">lame</a></h3><p>目前,LAME被认为是中高比特率和VBR的最佳MP3编码器,主要得益于其开发人员的专注工作和开源许可模式,使该项目能够利用来自世界各地的工程资源。 质量和速度的提升仍在继续,可能使LAME成为唯一仍在积极开发的MP3编码器。</p><h3 id="fdk-aac"><a href="#fdk-aac" class="headerlink" title="fdk-aac"></a><a href="https://sourceforge.net/projects/opencore-amr/files/fdk-aac/" target="_blank" rel="noopener">fdk-aac</a></h3><p>Fraunhofer FDK AAC是由Fraunhofer IIS开发的高质量开源AAC编码器库。 它是针对Android正式发布的,但已被移植到其他平台。 Winamp中包含的许可Fraunhofer AAC编解码器(通常称为FhG AAC)与FDK AAC编解码器不同。FDK AAC是一个目前效率很高的aac编解码库。</p><h3 id="x264"><a href="#x264" class="headerlink" title="x264"></a><a href="https://www.videolan.org/developers/x264.html" target="_blank" rel="noopener">x264</a></h3><p>x264是一个免费的开源软件库和VideoLAN开发的命令行实用程序,用于将视频流编码为H.264 / MPEG-4 AVC格式。 它是根据GNU通用公共许可证条款发布的。</p><h2 id="编译"><a href="#编译" class="headerlink" title="编译"></a>编译</h2><p>lame, fad-aac, x264的编译其实很简单,github上有现成的build shell, 但有些并非在机器上就能跑起来。所以针对一些错误,我对一些build shell做了少许改动,支持 x86_64, arm64e, arm64, armv7s, armv7架构的编译。</p><p>接下来我们就要开始编译了,请先从<a href="https://github.com/masterav/CompileAVLibs" target="_blank" rel="noopener">这里下载</a>编译需要用的代码、脚本和一些说明,下载后,直接进行编译就行,省时省力。</p><h3 id="Build-lame"><a href="#Build-lame" class="headerlink" title="Build lame"></a>Build lame</h3><ol><li>进入目录 <strong>lame-3.100</strong> , <code>cd lame-3.100</code>.</li><li>更改脚本文件的权限, <code>sudo chmod 777 build-lame.sh</code>.</li><li>运行脚本, <code>./build-lame.sh</code> </li></ol><p>你也可以参考脚本使用说明 <a href="https://github.com/masterav/CompileAVLibs/blob/master/lame-3.100/README.md" target="_blank" rel="noopener">lame shell 脚本使用说明</a>.</p><h3 id="Build-fdk-aac"><a href="#Build-fdk-aac" class="headerlink" title="Build fdk-aac"></a>Build fdk-aac</h3><ol><li>进入目录 <strong>fdk-aac-2.0.0</strong> , <code>cd fdk-aac-2.0.0</code>.</li><li>更改脚本文件的权限, <code>sudo chmod 777 build-fdk-aac.sh</code>.</li><li>运行脚本, <code>./build-fdk-aac.sh</code> </li></ol><p>你也可以参考脚本使用说明 <a href="https://github.com/masterav/CompileAVLibs/blob/master/fdk-aac-2.0.0/README.md" target="_blank" rel="noopener">fdk-aac shell 脚本使用说明</a>.</p><h3 id="Build-x264"><a href="#Build-x264" class="headerlink" title="Build x264"></a>Build x264</h3><ol><li>进入目录 <strong>x264-20190809-2245</strong> , <code>cd x264-20190809-2245</code>.</li><li>更改脚本文件的权限, <code>sudo chmod 777 build-x264.sh</code>.</li><li>运行脚本, <code>./build-x264.sh</code> </li></ol><p>你也可以参考脚本使用说明 <a href="https://github.com/masterav/CompileAVLibs/blob/master/x264-20190809-2245/README.md" target="_blank" rel="noopener">x264 shell 脚本使用说明</a>.</p><h3 id="FFmpeg"><a href="#FFmpeg" class="headerlink" title="FFmpeg"></a><a href="https://ffmpeg.org" target="_blank" rel="noopener">FFmpeg</a></h3><p>一种完整的跨平台解决方案,用于录制,转换和流式传输音频和视频。</p><h4 id="Build-FFmpeg"><a href="#Build-FFmpeg" class="headerlink" title="Build FFmpeg"></a>Build FFmpeg</h4><ol><li>进入目录 <code>ffmpeg-4.2</code> , <code>cd x264-20190809-2245</code>.</li><li>更改脚本文件的权限, <code>sudo chmod 777 build-ffmpeg.sh</code>.</li><li>如果想让ffmpeg使用<code>x264, lame, fdk-aac</code>的话,还请手动将之前编译环节中产生的<code>fat-x264, fat-lame, fat-fdk-aac</code>三个文件夹复制到<code>ffmpeg-4.2/external-libs</code>下面。反之,你需要把<code>X264, LAME, FDK_AAC</code>从<code>build-ffmpeg.sh</code>中注释掉.</li><li>运行脚本, <code>./build-ffmpeg.sh</code>。</li><li>最后,你可以在<code>fat-ffmpeg</code>目录下找到相关静态库。</li></ol><p>你也可以参考脚本使用说明 <a href="https://github.com/masterav/CompileAVLibs/blob/master/ffmpeg-4.2/README.md" target="_blank" rel="noopener">ffmpeg shell 脚本使用说明</a>.</p><h4 id="编译可能碰到的问题"><a href="#编译可能碰到的问题" class="headerlink" title="编译可能碰到的问题"></a>编译可能碰到的问题</h4><ul><li><p><code>No working C compiler found.</code><br>可能是xcode路径问题,终端输入命令:<br>sudo xcode-select —switch /Applications/Xcode.app/Contents/Developer/</p></li><li><p><code>Found no assembler,Minimum version is nasm-x.x.x</code> 或<br><code>Found no assembler,Minimum version is yasm-x.x.x</code><br> 原因是没有安装yasm/nasm或yasm/nasm版本太低,需要重新安装nasm/yasm。<br><code>brew install nasm</code> 或 <code>brew install yasm</code>.</p></li></ul><ul><li>编译i386遇到<code>No working C compiler found</code><br>有两种解决办法,任选一种就好:<ol><li>可以直接将i386略过编译,将<code>i386</code>从编译脚本<strong>ARCHS=”arm64 x86_64 i386 armv7 armv7s”</strong> 中删除,然后重新编译</li><li>在终端输入<code>./build-x264.sh arm64e arm64 x86_64 armv7s</code>进行编译。</li></ol></li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://www.jianshu.com/p/b7881a4467db" target="_blank" rel="noopener">https://www.jianshu.com/p/b7881a4467db</a></li><li><a href="https://www.jianshu.com/p/91bf522a199c" target="_blank" rel="noopener">https://www.jianshu.com/p/91bf522a199c</a></li></ul>]]></content>
<categories>
<category> 音视频 </category>
</categories>
<tags>
<tag> FFmpeg </tag>
</tags>
</entry>
<entry>
<title>CocoaPods 便捷手册</title>
<link href="/2020/06/30/cocoapods-manual/"/>
<url>/2020/06/30/cocoapods-manual/</url>
<content type="html"><![CDATA[<h1 id="CocoaPods-便捷手册"><a href="#CocoaPods-便捷手册" class="headerlink" title="CocoaPods 便捷手册"></a>CocoaPods 便捷手册</h1><h2 id="安装-CocoaPods"><a href="#安装-CocoaPods" class="headerlink" title="安装 CocoaPods"></a>安装 CocoaPods</h2><p>CocoaPods 的安装都是通过 <code>gem</code> 来管理,所以安装/更新 CocoaPods 之前,我们需要先更新下 <code>gem</code>:</p><p><code>sudo gem update --system</code> 。</p><ul><li><p>安装 CocoaPods</p><p>sudo gem install cocoapods -n /usr/local/bin</p></li><li><p>更新CocoaPods</p><p>sudo gem update cocoapods -n /usr/local/bin</p></li><li><p>回到上一版本</p><p>sudo gem install cocoapods -n /usr/local/bin —pre</p></li><li><p>卸载 CocoaPods</p><p>sudo gem uninstall cocoapods -n /usr/local/bin</p></li></ul><p><strong>Pod仓库显示</strong></p><p><code>pod repo</code> 或者 <code>pod repo list</code></p><h2 id="使用-Podfile"><a href="#使用-Podfile" class="headerlink" title="使用 Podfile"></a>使用 Podfile</h2><h3 id="常用操作"><a href="#常用操作" class="headerlink" title="常用操作"></a>常用操作</h3><ul><li><p>在当前目录下创建Podfile</p><p>pod init</p></li><li><p>安装依赖库</p><p>pod install</p></li><li><p>更新依赖库</p><p>pod update</p></li><li><p>更新特定依赖库</p><p>pod update LibraryName —verbose —no-repo-update</p></li></ul><h3 id="Podfile样例"><a href="#Podfile样例" class="headerlink" title="Podfile样例"></a>Podfile样例</h3><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">source 'https://cdn.cocoapods.org/'</span><br><span class="line">platform :ios, '9.0'</span><br><span class="line">use_frameworks!</span><br><span class="line"></span><br><span class="line">workspace 'App.xcworkspace'</span><br><span class="line"></span><br><span class="line">target ’SDK' do</span><br><span class="line"> project 'SDK/SDK.xcodeproj'</span><br><span class="line"> pod 'YYKit'</span><br><span class="line"> pod 'FBSDKCoreKit', '~> 4.31.0'</span><br><span class="line"> pod 'FBSDKLoginKit', '~> 4.31.0'</span><br><span class="line"> pod 'FBSDKShareKit', '~> 4.31.0</span><br><span class="line">end</span><br><span class="line"></span><br><span class="line">target ‘App' do</span><br><span class="line"> project ‘App.xcodeproj’</span><br><span class="line"><span class="meta"> #</span><span class="bash"> To use repo on your <span class="built_in">local</span> machine:</span></span><br><span class="line"> pod 'Alamofire', :path => '~/Documents/Alamofire'</span><br><span class="line"></span><br><span class="line"><span class="meta"> #</span><span class="bash"> To use the master branch of the repo:</span></span><br><span class="line"> pod 'Alamofire', :git => 'https://github.com/Alamofire/Alamofire.git'</span><br><span class="line"></span><br><span class="line"><span class="meta"> #</span><span class="bash"> To use a different branch of the repo:</span></span><br><span class="line"> pod 'Alamofire', :git => 'https://github.com/Alamofire/Alamofire.git', :branch => ‘dev'</span><br><span class="line"></span><br><span class="line"><span class="meta"> #</span><span class="bash"> To use a tag of the repo:</span></span><br><span class="line">pod 'Alamofire', :git => 'https://github.com/Alamofire/Alamofire.git', :tag => ‘3.1.1’</span><br><span class="line"></span><br><span class="line"> # Or specify a commit:</span><br><span class="line"> pod 'Alamofire', :git => 'https://github.com/Alamofire/Alamofire.git', :commit => ‘0f506b1c45'</span><br><span class="line">end</span><br><span class="line"></span><br><span class="line">post_install do |installer|</span><br><span class="line"> installer.pods_project.targets.each do |target|</span><br><span class="line"> target.build_configurations.each do |config|</span><br><span class="line"> config.build_settings['GCC_WARN_INHIBIT_ALL_WARNINGS'] = 'YES'</span><br><span class="line"> config.build_settings['ARCHS'] = 'arm64'</span><br><span class="line"> end</span><br><span class="line"> end</span><br><span class="line">end</span><br></pre></td></tr></table></figure><h2 id="发布-Pod-库"><a href="#发布-Pod-库" class="headerlink" title="发布 Pod 库"></a>发布 Pod 库</h2><h3 id="发布公有-Pod-库"><a href="#发布公有-Pod-库" class="headerlink" title="发布公有 Pod 库"></a>发布公有 Pod 库</h3><h4 id="操作步骤"><a href="#操作步骤" class="headerlink" title="操作步骤"></a>操作步骤</h4><ol><li><p>注册CocoaPods</p><p>pod trunk register [email protected] ‘Evan Xie’ —description=’NEVER GIVE UP!!!’</p></li><li><p>将你的 pod spec 上传到 CocoaPods 远程仓库</p><p>pod trunk push YourLibraryName.podspec —allow-warnings</p></li><li><p>创建 podspec 文件</p><p>pod spec create YourLibraryName</p></li><li><p>验证你的Pod库</p><p>pod lib lint —allow-warnings</p></li><li><p>创建 tag 并 push 到 git 远程仓库</p><p>git tag 0.0.1</p><p>git push —tags</p></li></ol><h4 id="公有-Pod-Spec-管理"><a href="#公有-Pod-Spec-管理" class="headerlink" title="公有 Pod Spec 管理"></a>公有 Pod Spec 管理</h4><ul><li><p>查看个人信息</p><p>pod trunk me</p></li><li><p>废弃 podspec </p><p>pod trunk deprecate YourLibraryName</p></li><li><p>删除某个版本</p><p>pod trunk mepod trunk delete YourLibraryName PodspecVersion</p></li></ul><h3 id="发布私有-Pod-库"><a href="#发布私有-Pod-库" class="headerlink" title="发布私有 Pod 库"></a>发布私有 Pod 库</h3><h4 id="操作步骤-1"><a href="#操作步骤-1" class="headerlink" title="操作步骤"></a>操作步骤</h4><ol><li><p>创建存放 pod specs 的 Git 仓库, 并添加到 Pod 仓库中</p><p>pod repo add PrivatePodSpecs [email protected]:xxx/PrivatePodSpecs.git</p></li><li><p>在本地查看私有仓库</p><p>第一步添加完成后,查看文件夹 <code>~/.cocoapods/repos</code>, 可以发现增加了一个 <code>PrivatePodSpecs</code> 的仓库.</p></li><li><p>创建代码仓库</p><p>代码仓库创建完成克隆到本地,然后就可以创建私有 <em>podspec</em> 了。</p></li><li><p>创建 podspec 文件</p><p>pod spec create YourLibraryName</p></li><li><p>验证你的Pod库</p><p>pod lib lint —allow-warnings</p></li><li><p>创建 tag 并 push 到 git 远程仓库</p><p>git tag 0.0.1</p><p>git push —tags</p></li><li><p>将 podspec 推送到创建好的私有存放 PodSpecs 的 Git 仓库</p><p>pod repo push PrivatePodSpecs YourLibraryName.podspec —verbose —allow-warnings</p></li><li><p>Push成功后,查看 ~/.cocoapods/repos, 就可以发现多了一个 <code>YourLibraryName.podspec</code>。如果未找到,可以先更新下 PrivatePodSpecs 仓库: <code>pod repo update PrivatePodSpecs</code> 。</p></li></ol><h4 id="使用私有Pod库"><a href="#使用私有Pod库" class="headerlink" title="使用私有Pod库"></a>使用私有Pod库</h4><p>在 Podfile 中指明私有 pod spec 仓库地址:</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">source '[email protected]:xxx/PrivatePodSpecs.git’</span><br></pre></td></tr></table></figure><p><strong>Podfile</strong></p><p>最后你的Podfile可能长这个样子:</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">source 'https://cdn.cocoapods.org/'</span><br><span class="line">source '[email protected]:xxx/PrivatePodSpecs.git'</span><br><span class="line"></span><br><span class="line">platform :ios, '9.0'</span><br><span class="line"></span><br><span class="line">workspace 'MyApp.xcworkspace'</span><br><span class="line"></span><br><span class="line">target 'MyApp' do</span><br><span class="line"> use_frameworks!</span><br><span class="line"> </span><br><span class="line"><span class="meta"> #</span><span class="bash"> 你的私有库</span></span><br><span class="line"> pod 'YourLibraryName'</span><br><span class="line"> </span><br><span class="line"><span class="meta">#</span><span class="bash"> 公有库</span></span><br><span class="line">pod 'RxSwift' </span><br><span class="line">end</span><br></pre></td></tr></table></figure><h4 id="指明私有-pod-spec-仓库地址"><a href="#指明私有-pod-spec-仓库地址" class="headerlink" title="指明私有 pod spec 仓库地址"></a>指明私有 pod spec 仓库地址</h4><p>如果私有 pod 仓库不能访问,可以尝试下域名与IP的映射。</p><p>打开 /etc目录,编辑 hosts文件,添加映射即可。例如:</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"><span class="comment">#</span></span></span><br><span class="line"><span class="meta">#</span><span class="bash"> Host Database</span></span><br><span class="line"><span class="meta">#</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> localhost is used to configure the loopback interface</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> when the system is booting. Do not change this entry.</span></span><br><span class="line"><span class="meta">#</span><span class="bash"><span class="comment">#</span></span></span><br><span class="line">127.0.0.1localhost</span><br><span class="line">255.255.255.255broadcasthost</span><br><span class="line">::1 localhost</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 自己添加的域名与 IP 的映射</span></span><br><span class="line">10.xxx.xxx.111 gitlab.compnayname.com</span><br></pre></td></tr></table></figure><h3 id="Pod-Spec-Beta-版本说明"><a href="#Pod-Spec-Beta-版本说明" class="headerlink" title="Pod Spec Beta 版本说明"></a>Pod Spec Beta 版本说明</h3><p><strong>比如我们要发的正式版本是 1.0.0, 不过目前还在开发过程中,所以我们可以使用用 beta 版本。</strong></p><p>假设我们发了两个版本: 1.0.0-beta1, 1.0.0-beta2,然后我们在 Podfile 中可以这样使用:</p><p>pod ‘MyLibrary’, ‘~> 1.0.0-beta’</p><p>pod install 后,就会自动更新到 1.0.0-beta2.</p><p>假设我们正式发布了 1.0.0版本,如果 Podfile 中还是用 1.0.0-beta 版本,那么 pod install 后, 会自动升级到正式版本 1.0.0。</p><p>更多版本号说明,请看这里: <a href="https://semver.org" target="_blank" rel="noopener">Semantic Versioning</a> 。</p><p><strong>常见问题</strong></p><ol><li>pod lib lint 报xcrun unable to find simctl问题</li></ol><p> 打开Xcode > Preferences > Locations 更改一下 Command Line Tools选项就可以了。</p>]]></content>
<categories>
<category> 开发工具 </category>
</categories>
<tags>
<tag> CocoaPods </tag>
</tags>
</entry>
<entry>
<title>Git 命令便捷手册</title>
<link href="/2020/06/29/git-manual/"/>
<url>/2020/06/29/git-manual/</url>
<content type="html"><![CDATA[<h1 id="Git-命令便捷手册"><a href="#Git-命令便捷手册" class="headerlink" title="Git 命令便捷手册"></a>Git 命令便捷手册</h1><h2 id="Git配置"><a href="#Git配置" class="headerlink" title="Git配置"></a>Git配置</h2><p>在开始Git之旅之前,我们需要设置一下Git的配置变量,这是一次性的工作。这些设置会在用户文件(用户主目录的.gitconfig)或系统文件(eg. /etc/gitconfig) 中做永久的记录。</p><h3 id="配置用户信息"><a href="#配置用户信息" class="headerlink" title="配置用户信息"></a>配置用户信息</h3><p>git config —global user.name “Evan Xie”</p><p>git config —global user.email [email protected]</p><h3 id="查看配置信息"><a href="#查看配置信息" class="headerlink" title="查看配置信息"></a>查看配置信息</h3><p>git config -l </p><p>git config —system —list</p><p>git config —global —list</p><p>git config —local —list</p><h3 id="设置命令别名"><a href="#设置命令别名" class="headerlink" title="设置命令别名"></a>设置命令别名</h3><p>以给Git命令设置别名,这样命令就可以用别名代替了。如果去掉sudo, 只在本用户的全局配置中添加Git命令别名。</p><p>sudo git config —system alias.st status</p><p>sudo git config —system alias.ci commit</p><p>sudo git config —system alias.co checkout</p><p>sudo git config —system alias.br branch</p><h3 id="开启颜色显示"><a href="#开启颜色显示" class="headerlink" title="开启颜色显示"></a>开启颜色显示</h3><p>git config —global color.ui true</p><h3 id="编辑-Config"><a href="#编辑-Config" class="headerlink" title="编辑 Config"></a>编辑 Config</h3><ul><li><p>打开进行编辑.git/config文件</p><p>git config -e</p></li><li><p>打开进行编辑.gitconfig文件</p><p>git config -e —global</p></li><li><p>打开进行编辑etc/gitconfig文件</p><p>git config -e —system</p></li></ul><h3 id="关闭-SSL-认证"><a href="#关闭-SSL-认证" class="headerlink" title="关闭 SSL 认证"></a>关闭 SSL 认证</h3><p>添加私有仓库可能出现这个问题,可以用以下命令来解决:</p><p>git config —global http.sslVerify false</p><h2 id="Git-仓库"><a href="#Git-仓库" class="headerlink" title="Git 仓库"></a>Git 仓库</h2><h3 id="常用操作"><a href="#常用操作" class="headerlink" title="常用操作"></a>常用操作</h3><ul><li><p>克隆仓库到本地</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git clone [email protected]:ReactiveX/RxSwift.git</span><br></pre></td></tr></table></figure></li><li><p>查看远程仓库地址</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git remote -v</span><br></pre></td></tr></table></figure></li><li><p>更改Git远程仓库</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git remote set-url origin [email protected]:ReactiveX/RxSwift.git</span><br></pre></td></tr></table></figure></li><li><p>添加远程仓库: 我们可以给本地仓库绑定多个远程仓库,这样可以把修改推送到多个远程仓库</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git remote add gitee [email protected]:ReactiveX/RxSwift.git</span><br><span class="line">git push origin</span><br><span class="line">git push gitee</span><br></pre></td></tr></table></figure></li><li><p>将本地仓库推送到远程空仓库</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git remote add origin [email protected]:evanxlh/BluetoothCentral.git</span><br><span class="line">git push -u origin master</span><br></pre></td></tr></table></figure></li></ul><h3 id="Git-Submodule"><a href="#Git-Submodule" class="headerlink" title="Git Submodule"></a>Git Submodule</h3><ul><li><p>添加 Submodule</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git submodule add git://github.com:xxx/project.git deps/mysql</span><br></pre></td></tr></table></figure></li><li><p>初始化</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git submodule init</span><br></pre></td></tr></table></figure></li><li><p>更新</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git submodule update </span><br><span class="line">git submodule update --init --recursive</span><br></pre></td></tr></table></figure></li><li><p>直接下载含有submodule的仓库</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git clone [email protected]:xxx/pod-project.git --recursive</span><br></pre></td></tr></table></figure></li></ul><h2 id="Git分支"><a href="#Git分支" class="headerlink" title="Git分支"></a>Git分支</h2><h3 id="列出分支"><a href="#列出分支" class="headerlink" title="列出分支"></a>列出分支</h3><ul><li><p>列出所有本地分支</p><p>git branch</p></li><li><p>列出远程本地分支</p><p>git branch -r</p></li><li><p>列出远程分支和标签</p><p>git ls-remote</p></li></ul><h3 id="分支创建与切换"><a href="#分支创建与切换" class="headerlink" title="分支创建与切换"></a>分支创建与切换</h3><ul><li><p>创建 hotfix 分支</p><p>git branch hotfix</p></li><li><p>创建并切换分支 </p><p>git checkout -b hotfix </p><p>git checkout -b <new-branch> <existing-branch></p></li><li><p>切换到hotfix分支</p><p>git checkout hotfix </p></li></ul><h3 id="删除分支"><a href="#删除分支" class="headerlink" title="删除分支"></a>删除分支</h3><ul><li><p>删除本地分支</p><p>git branch -D hotfix </p></li><li><p>删除远程hotfix分支</p><p>git push origin —delete hotfix</p></li><li><p>重命名分支 </p><p>git branch -M hotfix1</p></li></ul><h3 id="跟踪远程分支"><a href="#跟踪远程分支" class="headerlink" title="跟踪远程分支"></a>跟踪远程分支</h3><p>跟踪远程分支,就可以在当前分支上直接使用 git push, git fetch, git pull 了。有以下几种方式:</p><ul><li><p>方法一:</p><p>git checkout —track origin/branch_name</p></li><li><p>方法二: 通过修改 config 文件来完成</p><p><code>git config -e</code>, 然后向 config 中添加以下语句:</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">[branch "branch_name"]</span><br><span class="line">remote = origin</span><br><span class="line">merge = refs/heads/branch_name</span><br></pre></td></tr></table></figure></li><li><p>方法三: </p><p>git push —set-upstream origin develop</p></li></ul><h2 id="分支合并"><a href="#分支合并" class="headerlink" title="分支合并"></a>分支合并</h2><p>当我们想把自己的分支修改合并到远程分支,我们就要进行分支合并了。分支代码合并有两种方式:<em>git merge</em> 和 <em>git rebase</em>。接下来我们结合实例来说明它们各自的合并流程,假如我们要将自己的工作分支 <code>me</code> 的修改合并到 <code>develop</code> 分支:</p><h4 id="git-rebase"><a href="#git-rebase" class="headerlink" title="git rebase"></a>git rebase</h4><p><em>rebase</em> 能够按代码提交的时间顺序在目标分支上进行重演,就算合并产生冲突也能完好的保留历史结点。它的流程为:</p><ol><li>切换到 develop 分支,<code>git pull</code> 与服务器同步</li><li>切换到自己的工作分支: <code>git checkout me</code></li><li>将自己的工作分支的修改 rebase 到 develop分支: <code>git rebase develop</code></li><li>如果有冲突,解决冲突,并提交代码</li><li>再次切换到 develop 分支,将自己的工作分支合并到 develop 分支: <code>git merge me</code>,然后 <code>git push</code> 就行</li></ol><h4 id="git-merge"><a href="#git-merge" class="headerlink" title="git merge"></a>git merge</h4><p>如果不是 <em>fast forward</em> 时,<em>git merge</em> 会自动额外的一个 <em>merge commit</em>。尤其在有冲突的情况下,会把它人的 commits 合成一个 <em>merge commit</em> 。所以在非 <em>fast forward</em> 合并时,强烈推荐使用 <em>git rebase</em> 。</p><p> <em>merge</em> 的流程为:</p><ol><li><p>切换到 develop 分支,<code>git pull</code> 与服务器同步</p></li><li><p>执行合并: <code>git merge me</code></p></li><li><p>如果有冲突,解决冲突,并提交代码</p></li><li><p>将合并结果推送到远程服务器 <code>git push</code> </p></li></ol><h2 id="Git-标签"><a href="#Git-标签" class="headerlink" title="Git 标签"></a>Git 标签</h2><h3 id="删除-tag"><a href="#删除-tag" class="headerlink" title="删除 tag"></a>删除 tag</h3><ul><li><p>删除本地所有tags,保持跟服务tag一致</p><p>git fetch origin —prune —tags</p></li><li><p>将本地 tag: <code>1.0</code> 推送到服务器</p><p>git push origin 1.0 </p></li><li><p>将本地所有 tag 推送到服务器</p><p>git push —tags </p></li><li><p>删除本地 tag</p><p>git tag -d 1.0</p></li></ul><h3 id="列出本地-tag"><a href="#列出本地-tag" class="headerlink" title="列出本地 tag"></a>列出本地 tag</h3><p> git tag -l</p><h2 id="代码修改及提交"><a href="#代码修改及提交" class="headerlink" title="代码修改及提交"></a>代码修改及提交</h2><h3 id="忽略不想要提交的文件"><a href="#忽略不想要提交的文件" class="headerlink" title="忽略不想要提交的文件"></a>忽略不想要提交的文件</h3><ul><li><p>将不想要提交的文件加入 .gitignore 中</p><p>vim .gitignore</p></li><li><p>提交 .gitignore</p><p>git add .gitignore</p><p>git commit -m “Add .gitignore”</p></li></ul><h3 id="将代码加入-git-跟踪管理"><a href="#将代码加入-git-跟踪管理" class="headerlink" title="将代码加入 git 跟踪管理"></a>将代码加入 git 跟踪管理</h3><ul><li><p>将指定文件加入修改跟踪区(可以是单个或多个文件)</p><p>git add file1 file2</p></li><li><p>将所有文件加入修改跟踪区</p><p>git add .</p></li></ul><h3 id="提交本地修改"><a href="#提交本地修改" class="headerlink" title="提交本地修改"></a>提交本地修改</h3><ul><li><p>提交 git 修改跟踪区里的内容</p><p>git commit -m “human readable comment”</p></li><li><p>提交所有本地修改</p><p>git commit -a -m “human readable comment”</p></li></ul><h3 id="暂存修改的代码"><a href="#暂存修改的代码" class="headerlink" title="暂存修改的代码"></a>暂存修改的代码</h3><ul><li><p>将当前的修改保存到Git栈中,备份当前的工作区内容</p><p>git stash </p></li><li><p>从Git栈中读取最近一次保存的内容,并且恢复工作区的相关内容</p><p>git stash pop</p></li><li><p>显示Git栈中所有的备份</p><p>git stash list</p></li><li><p>清空Git栈中所有备份</p><p>git stash clear</p></li><li><p>取出版本号为stash@{1}的暂存内容,但并未将此记录从Git栈中清除</p><p>git stash apply stash@{1} </p></li></ul><h3 id="Git-Patch"><a href="#Git-Patch" class="headerlink" title="Git Patch"></a>Git Patch</h3><p>详情可参考这里:<a href="https://baijiahao.baidu.com/s?id=1627219440390872098&wfr=spider&for=pc" target="_blank" rel="noopener">Git命令解析-patch、apply、diff</a> 。</p><h4 id="创建-Patch"><a href="#创建-Patch" class="headerlink" title="创建 Patch"></a>创建 Patch</h4><p>创建好的 patch 会保存在你的当前工作目录下。</p><ul><li><p>生成最近的1次commit的patch</p><p>git format-patch HEAD^</p></li><li><p>生成最近的2次commit的patch</p><p> git format-patch HEAD^^</p></li><li><p>生成最近的3次commit的patch</p><p>git format-patch HEAD^^^</p></li><li><p>生成最近的4次commit的patch</p><p>git format-patch HEAD^^^^</p></li><li><p>生成两个commit间的修改的patch (包含两个commit. <r1>和<r2>都是具体的commit号)</p><p>git format-patch <r1>..<r2></p></li><li><p>生成单个commit的patch</p><p>git format-patch -1 <r1></p></li><li><p>生成某commit之后的所有修改patch(不包含该commit)</p><p>git format-patch <r1> </p></li><li><p>生成从根到r1提交的所有patch</p><p>git format-patch —root <r1></p></li></ul><h4 id="应用-Patch"><a href="#应用-Patch" class="headerlink" title="应用 Patch"></a>应用 Patch</h4><p> git apply 0001-Add-rspec-to-gemfile.patch</p><h2 id="代码恢复"><a href="#代码恢复" class="headerlink" title="代码恢复"></a>代码恢复</h2><p><em>reflog</em> 是 git 提供的一个内部工具,用于记录对git仓库进行的各种操作。</p><h3 id="查看操作日志,以下两种方式都可以"><a href="#查看操作日志,以下两种方式都可以" class="headerlink" title="查看操作日志,以下两种方式都可以"></a>查看操作日志,以下两种方式都可以</h3><ul><li>git reflog show</li><li>git log -g</li></ul><h3 id="进行恢复"><a href="#进行恢复" class="headerlink" title="进行恢复"></a>进行恢复</h3><p>git reset —hard commit_id_you_want_to_reset</p><h2 id="成功的-Git-分支模型"><a href="#成功的-Git-分支模型" class="headerlink" title="成功的 Git 分支模型"></a>成功的 Git 分支模型</h2><p>如果你还在探索如何高效的利用分支开发时,这里我强烈推荐下以下两篇文章:</p><p><a href="https://nvie.com/posts/a-successful-git-branching-model/" target="_blank" rel="noopener">A successful Git branching model</a></p><p><a href="https://www.atlassian.com/git/tutorials/comparing-workflows/feature-branch-workflow#:~:text=Git%20Feature%20Branch%20Workflow%20is%20branching%20model%20focused%2C,Branch%20Workflow%20can%20be%20incorporated%20into%20other%20workflows." target="_blank" rel="noopener">Git Feature Branch Workflow</a></p><h2 id="意外发现"><a href="#意外发现" class="headerlink" title="意外发现"></a>意外发现</h2><p>即将发布本文时,意外发现个 <a href="https://github.com/521xueweihan/git-tips" target="_blank" rel="noopener">Git的奇技淫巧</a> ,英文版请点这里:<a href="https://github.com/git-tips/tips" target="_blank" rel="noopener">Git Tips</a>。</p>]]></content>
<categories>
<category> 开发工具 </category>
</categories>
<tags>
<tag> Git </tag>
</tags>
</entry>
<entry>
<title>Xcode 制作 Framework & XCFramework</title>
<link href="/2020/06/14/make-frameworks-using-xcode/"/>
<url>/2020/06/14/make-frameworks-using-xcode/</url>
<content type="html"><![CDATA[<h1 id="使用-Xcode-制作-Framework-与-XCFramework"><a href="#使用-Xcode-制作-Framework-与-XCFramework" class="headerlink" title="使用 Xcode 制作 Framework 与 XCFramework"></a>使用 Xcode 制作 Framework 与 XCFramework</h1><p>最近公司有个项目外包,我就负责提供离在线语音识别 SDK 和数据埋点 SDK 封装,在制作 Framework 的过程中,遇到了很多问题。所以在这篇文章里我们会主要介绍下 如何制作 Frameworks ,以及如何解决遇到的一些问题。</p><h2 id="编译过程简述"><a href="#编译过程简述" class="headerlink" title="编译过程简述"></a>编译过程简述</h2><p>在制作 Framework 之前,我想简单阐述下编译器的工作原理,这有助于我们理解<a href="https://baike.baidu.com/item/静态库" target="_blank" rel="noopener">静态库</a>与<a href="">动态库</a>的制作。如果想了解编译器的详细设计,请点<a href="https://www.tutorialspoint.com/compiler_design/compiler_design_overview.htm">这里</a>。</p><p>我们都知道,计算机没法直接理解我们人类用高级语言写的程序,所以编译器可以帮助我们将高级语言写的程序转换成计算机能懂的二进制。下面我们结合一个简单的 C 程序来讲解下具体的编译过程。</p><h3 id="使用-GCC-编译源程序"><a href="#使用-GCC-编译源程序" class="headerlink" title="使用 GCC 编译源程序"></a>使用 GCC 编译源程序</h3><p>GCC(GNU Compiler Collection,GNU编译器套件)是由GNU开发的编程语言译器。GNU编译器套件包括C、C++、 Objective-C、 Fortran、Java、Ada和Go语言前端,也包括了这些语言的库(如libstdc++,libgcj等。) </p><p>GCC 的初衷是为 GNU 操作系统专门编写的一款编译器。GNU 系统是彻底的<a href="https://baike.baidu.com/item/自由软件/405190" target="_blank" rel="noopener">自由软件</a>,此处“自由”的含义是它尊重用户的自由。</p><h4 id="编写源程序"><a href="#编写源程序" class="headerlink" title="编写源程序"></a>编写源程序</h4><p>我们先编写一个简单的 C 程序,然后存储为 <code>hello.c</code> 源文件。</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">"hello, world\n"</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="GCC-编译"><a href="#GCC-编译" class="headerlink" title="GCC 编译"></a>GCC 编译</h4><ul><li><p>编写好源程序后,在控制终端输入以下命令,gcc 自动会完成所有编译过程,最后输出可执行文件 <code>hello</code>。</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">gcc -o hello hello.c</span><br></pre></td></tr></table></figure></li><li><p>在终端运行可执行文件 <code>hello</code>,终端就会输出 <code>hello, world</code>。</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">./hello</span><br></pre></td></tr></table></figure></li></ul><p>我们可以看到,一个 gcc 命令就将源程序转变成了可执行文件,它使用源程序编译变得非常简单。但我们还是得深究下,gcc 究竟做了些啥事。接下来,我们就简单介绍下编译器的四个阶段:预处理、编译、汇编,以及链接。</p><p><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/make-frameworks-xcode/compiler-phases.png" alt="编译过程"></p><h3 id="预处理阶段"><a href="#预处理阶段" class="headerlink" title="预处理阶段"></a>预处理阶段</h3><p><a href="https://www.tutorialspoint.com/cprogramming/c_preprocessors.htm" target="_blank" rel="noopener">CPP</a>,即 C Pre-Processor, 即 C 预处理器,是一个独立于C 编译器的小程序,预编译器并不理解 C 语言语法,它仅是在程序源文件被编译之前,实现文本替换的功能。</p><p>拿 <code>hello.c</code> 源程序为例,CPP 会根据 <strong><font color=green>#</font></strong> 开头的指令来修改源程序。比如 <code>hello.c</code> 中的第一行 <font color=green>#include <stdio.h></font> 就是告诉预处理器去把系统头文件 <font color=green>stdio.h</font> 内容读取出来,并直接插入到 <code>hello.c</code> 源程序中来,替换 <font color=green>#include <stdio.h></font>。</p><p>修改后的 C 程序一般另保存为 <strong><font color=green>.i</font></strong> 后缀的文本文件(本例为 <code>hello.i</code>),输出的 <code>hello.i</code> 将用于下一个阶段。 </p><h3 id="编译阶段"><a href="#编译阶段" class="headerlink" title="编译阶段"></a>编译阶段</h3><p>接下来编译器 <a href="https://unix.stackexchange.com/questions/77779/relationship-between-cc1-and-gcc" target="_blank" rel="noopener">cc1</a> 将 <code>hello.i</code> 编译成汇编程序,并保存为 <strong><font color=green>.s</font></strong> 后缀的汇编语言文本文件(本例为 <code>hello.s</code>)。</p><p>编译成汇编语言程序有个好处,就是对于不同的编译器,不同的高级语言,都会编译输出一样的汇编程序。</p><p>例如,C 编译器 和 Fortran 编译器,编译后都会输出同样的汇编程序。</p><h3 id="汇编阶段"><a href="#汇编阶段" class="headerlink" title="汇编阶段"></a>汇编阶段</h3><h4 id="目标文件-Object-File"><a href="#目标文件-Object-File" class="headerlink" title="目标文件 (Object File)"></a>目标文件 (Object File)</h4><p>在介绍汇编阶段前,我们先了了解下目标文件(Object File), 它其实有 3 种形式:</p><ul><li><p>可重定位目标文件 (Relocatable Object File)</p><p>包含可与其它 <code>relocatable object file</code> 相结合的二进制代码和数据,由编译器和汇编器产生。</p></li></ul><ul><li><p>可执行文件 (Executable Object File)</p><p>包含可直接复制到内存并执行的二进制代码和数据,由链接器生成。</p></li></ul><ul><li><p>共享目标文件 (Shared Object File)</p><p>一种特殊的 <code>relocatable object file</code>,它可以被装载入内存,并且可以在装载或运行的时候动态地链接。</p><p>由编译器和汇编器产生。</p></li></ul><h4 id="汇编阶段的工作内容"><a href="#汇编阶段的工作内容" class="headerlink" title="汇编阶段的工作内容"></a>汇编阶段的工作内容</h4><p>汇编阶段的工作就是将汇编文本程序翻译成机器指令,输出目标文件。</p><p>汇编器 (as) 将汇编程序 <code>hello.s</code> 翻译成机器指令,然后包装成<a href="">可重定位目标程序(Relocatable Object Program)</a>,并将其结果保存在 <code>hello.o</code> 文件中。<code>hello.o</code> 为二进制文件。</p><h3 id="链接阶段"><a href="#链接阶段" class="headerlink" title="链接阶段"></a>链接阶段</h3><p>在我们的 hello 程序中,它调用了C 标准库中 <font color=green>printf</font> 方法,而 <font color=green>printf</font> 位于事先编译好的独立目标文件 <font color=green>printf.o</font> 中。所以为了能让 hello 程序运行起来,我们需要采取某种手段将 <font color=green>printf.o</font> 合并入 <font color=green>hello.o</font>。幸运的是,链接器(ld) 就是做这一工作。</p><p>经过链接器的合并操作后,输出 <font color=green>hello</font> 可执行文件。在终端命令行中输入 <code>./hello</code> 回车后, <font color=green>hello</font> 可执行文件被装载入内存(通过加载器Loader来完成),并由系统执行,程序就跑起来了。</p><h2 id="Library"><a href="#Library" class="headerlink" title="Library"></a>Library</h2><p>我们在开发过程中,会把一些通用的函数制作成一个库,或者将一个功能模块制作成一个库 ,然后提供给 App 使用,可以达到共享复用的目的。</p><p>Library 可以理解成目标文件的集合,将相关的目标文件打包在一起,就成了一个 Library。我们按照 Library 是如何链接到 App 中的,可以把 Library 分成静态库和动态库。</p><h3 id="静态库-Static-Libraries"><a href="#静态库-Static-Libraries" class="headerlink" title="静态库(Static Libraries)"></a>静态库(Static Libraries)</h3><p>下图为 App 用到了静态库的情况。App 自身的代码被编译成目标文件后,通过静态链接器将App的目标文件与静态库合并,并生成的可执行文件。这样,App 自身代码生成的目标文件与静态库都被拷贝到可执行文件中,从而静态库也成为了 App 可执行文件的一部分。这样的库呢,我们称之为 静态库,也称为 <I>static archive libraries</I>, 或 <I>static linked shared libraries</I>. </p><p><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/make-frameworks-xcode/link-static-library.png" alt="Link Static Libraries"></p><h4 id="存在形式"><a href="#存在形式" class="headerlink" title="存在形式"></a>存在形式</h4><p>静态库主要以 <strong>.a</strong>, <strong>.lib</strong> 的形式存在,在苹果生态系统中,还可以是 <strong>.framework</strong> 或 <strong>.xcframework</strong> 。</p><h4 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h4><ul><li><p>App 启动的时候就全部载入内存空间,所以在 App 运行过程中,需要使用依赖库的时候不需要额外从外部加载,速度快,但也增加了 App 的启动时间。</p></li><li><p>App 的可执行文件变大,占用内存也会相应增多。因为App依赖的所有静态库都会被静态链接器链接并拷贝到将要生成的 app 可执行文件中。</p></li><li>当静态库需要修改时,必须得重新编译和发布静态库,所以不便于维护。</li></ul><h3 id="动态库-Dynamic-Library"><a href="#动态库-Dynamic-Library" class="headerlink" title="动态库(Dynamic Library)"></a>动态库(Dynamic Library)</h3><p>动态库又 <em>dynamic shared libraries</em>, <em>shared objects</em>, or <em>dynamically linked libraries</em>。我们以 OS X 为例,当 App 启动时,操作系统内核会将 App 代码和数据载入新进程(<em>也就是操作系统为 App 创建的新进程</em>)的地址空间。与此同时呢,操作系统内核也会把<strong>动态加载器</strong>(<em>Dynamic Loader</em>) 载入进程,由<strong>动态加载器</strong>来完成加载 App 依赖的动态库。不过在启动阶段,<strong>动态加载器</strong>只会根据<strong>静态链接器</strong>中记录的 App 已链接的依赖库的名字,然后使用依赖库的 <code>install name</code> 来查找它们是否在文件系统中存在。如果不存在或不兼容,App 启动过程会中断。动态库被完全载入内存,是在代码里使用它的时候。所以相对静态库来说,使用动态库链接的 App 启动过程会更快。</p><p><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/make-frameworks-xcode/link-dynamic-library.png" alt="Dynamic Link"></p><h4 id="存在形式-1"><a href="#存在形式-1" class="headerlink" title="存在形式"></a>存在形式</h4><p>动态库主要以 <strong>.dylib</strong>,<strong>.so</strong>,<strong>dll</strong> 的形式存在,在苹果生态系统中,还可以是 <strong>.framework</strong> 或 <strong>.xcframework</strong> 。</p><p>iOS App 的动态库存放在 <strong>.app</strong> <em>bundle</em> 下的 <em>Frameworks</em> 文件夹。</p><h4 id="特点-1"><a href="#特点-1" class="headerlink" title="特点"></a>特点</h4><ul><li><p>App 按需装载,可以加速 App 的启动。</p></li><li><p>动态库不会被拷贝到 App 的可执行文件中,所以可以动态按需加载。</p></li><li>动态库的维护和更新很方便,只要 APIs 不变,依赖动态库的 App 就不用重新编译 。因为动态库并不是 App 可执行文件的一部分,是独立的,可动态加载的。</li></ul><h2 id="Apple-FrameWorks"><a href="#Apple-FrameWorks" class="headerlink" title="Apple FrameWorks"></a>Apple FrameWorks</h2><h3 id="Framework"><a href="#Framework" class="headerlink" title="Framework"></a>Framework</h3><p>Framework 可以通俗的理解为封装了共享资源的具有层次结构的文件夹。共享资源可以是 nib文件、国际化字符串文件、头文件、库文件等等。它同时也是个 <code>Bundle</code>,里面的内容可以通过 <code>Bundle</code> 相关 API 来访问。Framework 可以是 <code>static framework</code> 或 <code>dynamic framework</code>。<font color=red> 在 iOS App 打包完成后,如果 Framework 包含了模拟器指令集(x86_64 或 i386),那么用 Xcode 发布 App 的时候,会报 unsupported architectures 的错误,所以需要我们手动或脚本去移除。</font></p><h3 id="XCFramework"><a href="#XCFramework" class="headerlink" title="XCFramework"></a>XCFramework</h3><p>XCFramework 是由 Xcode 创建的一个可分发的二进制包,它包含了 <em>framework</em> 或 library 的一个或多个变体,因此可以在多个平台(iOS、macOS、tvOS、watchOS) 上使用,包括模拟器。<em>XCFramework</em> 可以是静态的,也可以是动态的。<code>xcframework</code> 的好处就是用 Xcode 发布的时候,Xcode 会自动选用正确的指令集 Frameworks,省去了手动移除动态库中的模拟器指令集的工作。<font color=red>不过值得注意的是,Xcode 11 才引入 XCFramework 。</font></p><h2 id="制作-Frameworks"><a href="#制作-Frameworks" class="headerlink" title="制作 Frameworks"></a>制作 Frameworks</h2><p>关于如何用 Xcode 一步一步创建 Framework 工程的话,我就不多说了,网上一大把教程,您也可以参考 <a href="http://appracatappra.com/2018/03/building-cross-platform-universal-frameworks-using-swift/" target="_blank" rel="noopener">Building Cross Platform Universal Frameworks</a> 或 <a href="https://github.com/TofPlay/SwiftCrossPlatformFramework" target="_blank" rel="noopener">Swift Cross Platform Framework</a>。我重点讲如何用脚本来制作各种类型的 <code>frameworks</code>。为什么要介绍脚本呢,网上不是很多脚本制作 frameworks 吗? 刚开始我也是直接用网上的脚本,可总会有这样那样的问题,各种错误,所以决定在参考大神们文章的同时,自己重新整理下。</p><p>到这里,我假设您的 framework 代码都已经写好了,打包的 <strong>Aggregation Target</strong> 也创建好了。接下来,我将直接讲用脚本来制作 <em>frameworks</em> 。至于是制作 <em>static framework</em> 还是 <em>dynamic framework</em> 可以在 <code>framework target</code> 的 <code>Build Settings</code> 中的 <code>Mach-O Type</code> 选择 <em>framework</em> 的类型,一般选用 <code>Dynamic Library</code> 或者 <code>Static Library</code> 就行。</p><h3 id="开发环境"><a href="#开发环境" class="headerlink" title="开发环境"></a>开发环境</h3><p>本文中使用的开发环境为:</p><ul><li>macOS Catalina 10.15.4</li><li>Xcode 11.5 </li></ul><h3 id="xcarchive-目录结构"><a href="#xcarchive-目录结构" class="headerlink" title=".xcarchive 目录结构"></a>.xcarchive 目录结构</h3><p>在制作 <em>universal framework</em> 与 <em>xcframework</em>,我们都会用到 <code>.xcarchive</code> 包,所以我们先来看下它的目录结构。</p><p><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/make-frameworks-xcode/xcarchive-contents.png" alt="xcarchive contents" style="zoom:67%;" /></p><h3 id="制作-Universal-Framework-脚本"><a href="#制作-Universal-Framework-脚本" class="headerlink" title="制作 Universal Framework 脚本"></a>制作 Universal Framework 脚本</h3><p>你可以在这里直接获取<a href="https://github.com/evanxlh/MyDeveloperToolkit/blob/master/Shells/make_framework.sh" target="_blank" rel="noopener">制作 <em>universal framwork</em> 的完整脚本</a>。</p><h4 id="编译单个平台的函数"><a href="#编译单个平台的函数" class="headerlink" title="编译单个平台的函数"></a>编译单个平台的函数</h4><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 制作完 framework 后,是否在 Finder 中打开</span></span><br><span class="line">REVEAL_FRAMEWORK_IN_FINDER=true </span><br><span class="line"><span class="meta">#</span><span class="bash"> Framework 的名字</span></span><br><span class="line">FREAMEWORK_NAME="${PROJECT_NAME}" </span><br><span class="line"><span class="meta">#</span><span class="bash"> 制作好的 framework 会输出到这个文件夹下面</span></span><br><span class="line">FREAMEWORK_OUTPUT_DIR="${PROJECT_DIR}/Distribution" </span><br><span class="line"><span class="meta">#</span><span class="bash"> Device Archive 生成的 .xcarchive 存放路径。在工程的根目录下生成 Build 文件夹。</span></span><br><span class="line">ARCHIVE_PATH_IOS_DEVICE="./Build/ios_device.xcarchive" </span><br><span class="line"><span class="meta">#</span><span class="bash"> Simulator Archive 生成的 .xcarchive 存放路径。</span></span><br><span class="line">ARCHIVE_PATH_IOS_SIMULATOR="./Build/ios_simulator.xcarchive"</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 我们可以编译更多平台的 xcarchive</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> ARCHIVE_PATH_MACOS=<span class="string">"./build/macos.xcarchive"</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 生成单个平台的 .xcarchive. 接收4个参数, scheme, destination, archivePath,指令集.</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> xcpretty 可以删除,这里用来使 Xcode 输出的日志更加人性化。</span></span><br><span class="line">function archiveOnePlatform {</span><br><span class="line">echo "▸ Starts archiving the scheme: ${1} for destination: ${2};\n▸ Archive path: ${3}"</span><br><span class="line"></span><br><span class="line">xcodebuild archive \</span><br><span class="line">-scheme "${1}" \</span><br><span class="line">-destination "${2}" \</span><br><span class="line">-archivePath "${3}" \</span><br><span class="line">VALID_ARCHS="${4}" \</span><br><span class="line">SKIP_INSTALL=NO \</span><br><span class="line"> BUILD_LIBRARY_FOR_DISTRIBUTION=YES | xcpretty</span><br><span class="line"> # BUILD_LIBRARY_FOR_DISTRIBUTION=YES</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> sudo gem install -n /usr/<span class="built_in">local</span>/bin xcpretty</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> xcpretty makes xcode compile information much more readable.</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="编译所有平台的函数"><a href="#编译所有平台的函数" class="headerlink" title="编译所有平台的函数"></a>编译所有平台的函数</h4><p>以下方法可以编译并生成 <em>iOS device, simulator</em> 两个平台的 <code>.xcarchive</code>,此方法接收一个参数:<em>scheme</em>, 即 对应 <em>app target</em> 的 <em>scheme</em>。通常情况下,<em>scheme</em> 和 <em>framework name</em> 是相同的。</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">function archiveAllPlatforms {</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> https://www.mokacoding.com/blog/xcodebuild-destination-options/</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> PlatformDestination</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> iOSgeneric/platform=iOS</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> iOS Simulatorgeneric/platform=iOS Simulator</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> iPadOSgeneric/platform=iPadOS</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> iPadOS Simulatorgeneric/platform=iPadOS Simulator</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> macOSgeneric/platform=macOS</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> tvOSgeneric/platform=tvOS</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> watchOSgeneric/platform=watchOS</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> watchOS Simulatorgeneric/platform=watchOS Simulator</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> carPlayOSgeneric/platform=carPlayOS</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> carPlayOS Simulatorgeneric/platform=carPlayOS Simulator</span></span><br><span class="line"></span><br><span class="line">SCHEME=${1}</span><br><span class="line"></span><br><span class="line">archiveOnePlatform $SCHEME "generic/platform=iOS Simulator" ${ARCHIVE_PATH_IOS_SIMULATOR} "x86_64"</span><br><span class="line"> archiveOnePlatform $SCHEME "generic/platform=iOS" ${ARCHIVE_PATH_IOS_DEVICE} "armv7 arm64"</span><br><span class="line"><span class="meta"> #</span><span class="bash"> archiveOnePlatform <span class="variable">$SCHEME</span> <span class="string">"generic/platform=macOS"</span> <span class="variable">${ARCHIVE_PATH_MACOS}</span></span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这个方法执行完后,在本例中会得到以下 <em>Build</em> 文件夹内的内容:</p><p><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/make-frameworks-xcode/xcarchives.png" alt="Generated xcarchives" style="zoom:67%;" /></p><h4 id="生成-Universal-Framework的函数"><a href="#生成-Universal-Framework的函数" class="headerlink" title="生成 Universal Framework的函数"></a>生成 Universal Framework的函数</h4><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">function makeUniversalFramework {</span><br><span class="line"><span class="meta">#</span><span class="bash"> xcarchive 包中的 Frameworks 目录相对路径</span></span><br><span class="line">FRAMEWORK_RELATIVE_PATH="Products/Library/Frameworks"</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 接下来的三个路径分别是模拟器平台的framework路径,真机平台的framework路径,以及输出的universal framework路径</span></span><br><span class="line">SIMULATOR_FRAMEWORK="${ARCHIVE_PATH_IOS_SIMULATOR}/${FRAMEWORK_RELATIVE_PATH}/${FREAMEWORK_NAME}.framework"</span><br><span class="line">DEVICE_FRAMEWORK="${ARCHIVE_PATH_IOS_DEVICE}/${FRAMEWORK_RELATIVE_PATH}/${FREAMEWORK_NAME}.framework"</span><br><span class="line">OUTPUT_FRAMEWORK="${FREAMEWORK_OUTPUT_DIR}/${FREAMEWORK_NAME}.framework"</span><br><span class="line"></span><br><span class="line">mkdir -p "${OUTPUT_FRAMEWORK}"</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> Copy all the contents of iphoneos framework to output framework dir.</span></span><br><span class="line">cp -rf "${DEVICE_FRAMEWORK}/." "${OUTPUT_FRAMEWORK}"</span><br><span class="line"></span><br><span class="line">lipo "${SIMULATOR_FRAMEWORK}/${FREAMEWORK_NAME}" "${DEVICE_FRAMEWORK}/${FREAMEWORK_NAME}" \</span><br><span class="line">-create -output "${OUTPUT_FRAMEWORK}/${FREAMEWORK_NAME}"</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> For Swift framework, Swiftmodule needs to be copied <span class="keyword">in</span> the universal framework</span></span><br><span class="line">if [ -d "${SIMULATOR_FRAMEWORK}/Modules/${FRAMEWORK_NAME}.swiftmodule/" ]; then</span><br><span class="line">cp -f "${SIMULATOR_FRAMEWORK}/Modules/${FRAMEWORK_NAME}.swiftmodule/*" "${OUTPUT_FRAMEWORK}/Modules/${FRAMEWORK_NAME}.swiftmodule/" | echo</span><br><span class="line">fi</span><br><span class="line">if [ -d "${DEVICE_FRAMEWORK}/Modules/${FRAMEWORK_NAME}.swiftmodule/" ]; then</span><br><span class="line">cp -f "${DEVICE_FRAMEWORK}/Modules/${FRAMEWORK_NAME}.swiftmodule/*" "${OUTPUT_FRAMEWORK}/Modules/${FRAMEWORK_NAME}.swiftmodule/" | echo</span><br><span class="line">fi</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="开始制作"><a href="#开始制作" class="headerlink" title="开始制作"></a>开始制作</h4><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">echo "#####################"</span><br><span class="line">echo "▸ Cleaning Framework output dir: ${FREAMEWORK_OUTPUT_DIR}"</span><br><span class="line">rm -rf "$FREAMEWORK_OUTPUT_DIR"</span><br><span class="line"></span><br><span class="line">echo "▸ Archive framework: ${FREAMEWORK_NAME}"</span><br><span class="line">archiveAllPlatforms "$FREAMEWORK_NAME"</span><br><span class="line"></span><br><span class="line">echo "▸ Make universal framework: ${FREAMEWORK_NAME}.framework"</span><br><span class="line">makeUniversalFramework</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> Clean Build</span></span><br><span class="line">rm -rf "./Build"</span><br><span class="line"></span><br><span class="line">if [ ${REVEAL_FRAMEWORK_IN_FINDER} = true ]; then</span><br><span class="line"> open "${FREAMEWORK_OUTPUT_DIR}/"</span><br><span class="line">fi</span><br></pre></td></tr></table></figure><h4 id="去除动态库中的模拟器指令集"><a href="#去除动态库中的模拟器指令集" class="headerlink" title="去除动态库中的模拟器指令集"></a>去除动态库中的模拟器指令集</h4><p>正如之前提到的,App 打包过程中,需要将 App 依赖的动态库中的模拟器指令集去除,这里是<a href="https://github.com/evanxlh/MyDeveloperToolkit/blob/master/Shells/ios-remove-fat-framework-simulator-archs.sh" target="_blank" rel="noopener">完整的脚本</a>, 如何使用,请点 <a href="https://ikennd.ac/blog/2015/02/stripping-unwanted-architectures-from-dynamic-libraries-in-xcode/" target="_blank" rel="noopener">Stripping unwanted architectures from dynamic libraries</a>。在 <em>app target</em> 的 <strong>Build Phase</strong> 下新建 <em>Run Script</em>,并放到 <em>Embed Frameworks</em> 下面,然后将脚本复制进去就行。</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash">!/bin/sh</span></span><br><span class="line"></span><br><span class="line">APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"</span><br><span class="line"></span><br><span class="line">echo $APP_PATH</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> This script loops through the frameworks embedded <span class="keyword">in</span> the application and</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> removes unused architectures.</span></span><br><span class="line">find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK</span><br><span class="line">do</span><br><span class="line"> FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)</span><br><span class="line"> FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"</span><br><span class="line"> echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"</span><br><span class="line"></span><br><span class="line"> EXTRACTED_ARCHS=()</span><br><span class="line"></span><br><span class="line"> for ARCH in $ARCHS</span><br><span class="line"> do</span><br><span class="line"> echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"</span><br><span class="line"> lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"</span><br><span class="line"> EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")</span><br><span class="line"> done</span><br><span class="line"></span><br><span class="line"> echo "Merging extracted architectures: ${ARCHS}"</span><br><span class="line"> lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"</span><br><span class="line"> rm "${EXTRACTED_ARCHS[@]}"</span><br><span class="line"></span><br><span class="line"> echo "Replacing original executable with thinned version"</span><br><span class="line"> rm "$FRAMEWORK_EXECUTABLE_PATH"</span><br><span class="line"> mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"</span><br><span class="line">done</span><br></pre></td></tr></table></figure><h3 id="制作-XCFramework-脚本"><a href="#制作-XCFramework-脚本" class="headerlink" title="制作 XCFramework 脚本"></a>制作 XCFramework 脚本</h3><h4 id="生成-Universal-Framework的函数-1"><a href="#生成-Universal-Framework的函数-1" class="headerlink" title="生成 Universal Framework的函数"></a>生成 Universal Framework的函数</h4><p>你可以在这里直接获取<a href="https://github.com/evanxlh/MyDeveloperToolkit/blob/master/Shells/make_xcframeworks.sh" target="_blank" rel="noopener">制作 <em>xcframwork</em> 的完整脚本</a>。编译单个平台和全部平台的函数跟制作 Framework 一样,所以这里直接列出制作 <em>xcframework</em> 的函数。<font color=red>制作 XCFramewrok 的时候,需要将 Build Settings 中的 <strong>Build Libraries for Distribution</strong> 设置为 YES。不过即使不设置,脚本也会将其设置为 YES。</font></p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">function makeXCFramework {</span><br><span class="line"></span><br><span class="line">FRAMEWORK_RELATIVE_PATH="Products/Library/Frameworks"</span><br><span class="line">OUTPUT_DIR="${FREAMEWORK_OUTPUT_DIR}/DynamicFramework"</span><br><span class="line"></span><br><span class="line">mkdir -p "${OUTPUT_DIR}"</span><br><span class="line"></span><br><span class="line">xcodebuild -create-xcframework \</span><br><span class="line">-framework "${ARCHIVE_PATH_IOS_DEVICE}/${FRAMEWORK_RELATIVE_PATH}/${FREAMEWORK_NAME}.framework" \</span><br><span class="line">-framework "${ARCHIVE_PATH_IOS_SIMULATOR}/${FRAMEWORK_RELATIVE_PATH}/${FREAMEWORK_NAME}.framework" \</span><br><span class="line">-output "${OUTPUT_DIR}/${FREAMEWORK_NAME}.xcframework"</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="开始制作-1"><a href="#开始制作-1" class="headerlink" title="开始制作"></a>开始制作</h4><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">echo "#####################"</span><br><span class="line">echo "▸ Cleaning XCFramework output dir: ${FREAMEWORK_OUTPUT_DIR}"</span><br><span class="line">rm -rf $FREAMEWORK_OUTPUT_DIR</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"><span class="comment">### Make XCFramework</span></span></span><br><span class="line"></span><br><span class="line">echo "▸ Archive framework: ${FREAMEWORK_NAME}"</span><br><span class="line">archiveAllPlatforms $FREAMEWORK_NAME</span><br><span class="line"></span><br><span class="line">echo "▸ Make framework: ${FREAMEWORK_NAME}.xcframework"</span><br><span class="line">makeXCFramework</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> Clean Build</span></span><br><span class="line">rm -rf "./Build"</span><br><span class="line"></span><br><span class="line">if [ ${REVEAL_XCFRAMEWORK_IN_FINDER} = true ]; then</span><br><span class="line"> open "${FREAMEWORK_OUTPUT_DIR}/"</span><br><span class="line">fi</span><br></pre></td></tr></table></figure><p>最后生成的 <em>xcframework</em> 长这个样子:</p><p><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/make-frameworks-xcode/xcframe.png" alt="Generated xcarchives" style="zoom:67%;" /></p><h3 id="小技巧"><a href="#小技巧" class="headerlink" title="小技巧"></a>小技巧</h3><ul><li><p>查看 framework 包含的指令集</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">lipo -info /path/to/xxx.framework/xxx</span><br></pre></td></tr></table></figure></li><li><p>查看 dynamic framework 是否支持 bitcode</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">otool -arch armv7 -l /path/to/xxx.framework/xxx | grep __bundle</span><br></pre></td></tr></table></figure><p>如果包含 bitcode, 你会看到 <font color=red>sectname __bundle</font> 信息。如果不包含,Terminal 输出为空。</p></li><li><p>查看 static framework 是否支持 bitcode</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">otool -arch armv7 -l /path/to/xxx.framework/xxx | grep __bitcode</span><br></pre></td></tr></table></figure><p>如果包含 bitcode, 你会看到 <font color=red>sectname __bitcode</font> 信息。如果不包含,Terminal 输出为空。</p></li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p>非常感谢以下文章的贡献者,使我对编译原理相关知识,以及对静态库、动态库、framework 有了更深刻的认识。</p><h3 id="GCC"><a href="#GCC" class="headerlink" title="GCC"></a>GCC</h3><p><a href="https://baike.baidu.com/item/gcc/17570?fr=aladdin" target="_blank" rel="noopener">GCC 百科</a></p><p><a href="http://gcc.gnu.org" target="_blank" rel="noopener">GCC, the GNU Compiler Collection</a></p><p><a href="https://www.tutorialspoint.com/cprogramming/c_preprocessors.htm" target="_blank" rel="noopener">C Pre-Processor</a></p><p><a href="https://www.tutorialspoint.com/compiler_design/compiler_design_overview.htm" target="_blank" rel="noopener">Compiler Design Overview</a></p><h3 id="Apple-Frameworks"><a href="#Apple-Frameworks" class="headerlink" title="Apple Frameworks"></a>Apple Frameworks</h3><p><a href="https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/DynamicLibraries/000-Introduction/Introduction.html#//apple_ref/doc/uid/TP40001908-SW1" target="_blank" rel="noopener">Dynamic Library Programming Topics</a></p><p><a href="https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/MachOTopics/1-Articles/loading_code.html#//apple_ref/doc/uid/TP40001830-SW1" target="_blank" rel="noopener">Mach-O Programming Topics</a></p><p><a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingCode/LoadingCode.html#//apple_ref/doc/uid/10000052-SW1" target="_blank" rel="noopener">Code Loading Programming Topics</a></p><p><a href="https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Frameworks.html#//apple_ref/doc/uid/10000183-SW1" target="_blank" rel="noopener">Framework Programming Guide</a></p><p><a href="https://ikennd.ac/blog/2015/02/stripping-unwanted-architectures-from-dynamic-libraries-in-xcode/" target="_blank" rel="noopener">Stripping Unwanted Architectures From Dynamic Libraries In Xcode</a></p><p><a href="https://www.mokacoding.com/blog/xcodebuild-destination-options/" target="_blank" rel="noopener">XcodeBuild Destination</a></p><p><a href="https://xcodebuildsettings.com" target="_blank" rel="noopener">Xcode Build Settings</a></p>]]></content>
<categories>
<category> iOS开发 </category>
<category> Xcode </category>
</categories>
<tags>
<tag> Xcode Frameworks </tag>
</tags>
</entry>
<entry>
<title>iOS app 重签名及发布至 AppStore</title>
<link href="/2020/05/28/ios-app-resigning/"/>
<url>/2020/05/28/ios-app-resigning/</url>
<content type="html"><![CDATA[<h1 id="iOS-app-重签名及发布至-AppStore"><a href="#iOS-app-重签名及发布至-AppStore" class="headerlink" title="iOS app 重签名及发布至 AppStore"></a>iOS app 重签名及发布至 AppStore</h1><p>由于公司有个项目请了外包团队来开发,但不提供源码,只提供 xcarchive 或 ipa 包,而做为公司又不可能将证书及私钥发给外包团队,让他们来打包。所以重签名就是绕不过的坎了,只能自己来做这一工作了。刚开始直接从网上找资料,直接用现成的脚本作些修改来进行重签名,可都会出现各种问题。无耐之下,只能翻阅苹果官网文档以及 <I>Troubleshooting</I>,最终完成重签名,并上传至 AppStore。现在将重签名过程重新整理下,记录下来方便以后查阅,也希望能帮助到有需要的同学们。</p><h2 id="核心概念"><a href="#核心概念" class="headerlink" title="核心概念"></a>核心概念</h2><p>代码签名(<I>Code Signing</I>)是操作系统应用的一种安全技术,用来证明 app 是你创建的。一旦 app 被签名后,操作系统就可以检测出 App 是否被意外或恶意地修改过,是否来自一个受信任的发布机构或组织,或个人。代码签名最基础、最核心部分就是数据的加密哈希函数和非对称加密,然后在此基础上,定义一些规则与策略,来完成代码签名。接下来,我们先来了解下签名相关的一些核心概念。</p><h4 id="加密哈希函数"><a href="#加密哈希函数" class="headerlink" title="加密哈希函数"></a>加密哈希函数</h4><p>加密哈希函数可以接收任意大小的数据,然后应用特定算法处理输入数据,并输出固定大小的哈希数据(如128 / 256 位等)。一些常见的哈希算法有 <I>SHA-1, SHA-2</I> 等等。在代码签名过程中,我们用它来生成代码数据的信息摘要(<I>message digest</I>, Apple 称它为 <I>seal</I>, 即封条,用来验证数据是否被更改)。</p><h4 id="非对称加密"><a href="#非对称加密" class="headerlink" title="非对称加密"></a>非对称加密</h4><p><a href="https://en.wikipedia.org/wiki/Public-key_cryptography" target="_blank" rel="noopener">非对称加密</a>(<I>public key cryptography, 或者 asymmetric cryptography</I>) 提供两组密钥:公钥和私钥。公钥可以公开并分享给他人,而私钥只能自己拥有。公称和私钥都可以用来加解密,但在一次加密解密过程中,需要配对使用。不管是用公钥加密,还是私钥加密,最后通过解密后,都能还原出明文,正如下图所示:<br><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/ios-app-resigning/security-asymmetric.jpg" alt="Security Asymmetric"> </p><h4 id="信息摘要"><a href="#信息摘要" class="headerlink" title="信息摘要"></a>信息摘要</h4><p>信息摘要(<I>message digest</I>)是由代码签名软件创建的代码各个部分的校验和或哈希的集合,可以用来检测数据是否被更改。 拿写书信为例,写好的书信放入信封,然后进行密封。如果收信人发现密封损坏,他就会知道这封信肯定被打开过或被他人动过手脚。所以 Apple 也称它为 <I>seal</I>,意为密封或封条。通过私钥加密后,就成了代码签名中的加密信息摘要(<I>encrypted message digest</I>, 或者 <I>encrypted seal</I>)。 </p><h4 id="数字证书"><a href="#数字证书" class="headerlink" title="数字证书"></a>数字证书</h4><p>数字证书(<I>Digital Certificate</I>) 是用于验证数字证书 <I>持有者或发送者</I> 的身份的数据集合,来证明签名者公钥的真实性。比如 X.509 证书包含以下一些信息: </p><ul><li>结构信息: 版本,序列号,用于创建签名的消息摘要算法等</li><li>证书颁发机构的数字签名</li><li>有关证书持有者的信息: 名称,电子邮件地址,公司名称,所有者的公钥等</li><li>有效期: 证书在此期限之前或之后无效</li><li>证书扩展: 包含其他信息的属性,如证书的允许用途<br><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/ios-app-resigning/anatomy-of-digital-certificate.png" alt="Digital Certificate" style="zoom:70%;" /><br>下图为 <I>Apple Worldwide Developer Relations Certification Authority</I> 证书中所包含的信息:<br><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/ios-app-resigning/certificate-apple.png" alt="Apple Worldwide Developer Relations Certification Authority" style="zoom:67%;" /></li></ul><h4 id="数字身份"><a href="#数字身份" class="headerlink" title="数字身份"></a>数字身份</h4><p>数字身份(<I>Digital Identity</I>) 是公钥-私钥对以及对应的数字证书的组合。当我们向苹果申请到签名证书后,导入到 KeyChain Access 中,我们就获取到了数字身份,如下图所示:<br><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/ios-app-resigning/digital-identity.jpg" alt="Digital Identity"></p><h4 id="证书颁发机构"><a href="#证书颁发机构" class="headerlink" title="证书颁发机构"></a>证书颁发机构</h4><p>证书颁发机构(Certification Authority) 是颁发数字证书的人员或组织,它确保数字证书没有被更改,并且表明颁发数字证书者的身份。如 Apple, 我们可以从 Apple 提供的工具来创建数字身份(<I>Digital Identity</I>),并申请数字证书。</p><h4 id="数字签名"><a href="#数字签名" class="headerlink" title="数字签名"></a>数字签名</h4><p>数字签名(<I>Digital Signatures</I>) 就是加密信息摘要及数字证书的组合,是一种使用非对称加密来保证数据完整性的方法。它可以用来验证数据签名者的身份,跟我们使用传统签名(用墨水在纸上写上自己的名字)方法一样,都是用来表明及验证身份的。但数字签名除了身份验证外,还能检查出数据是否被修改过。</p><h4 id="规范要求"><a href="#规范要求" class="headerlink" title="规范要求"></a>规范要求</h4><p><I>Code Requirements</I>,我就译成规范要求吧,是操作系统用来评估代码签名的规则。进行评估的系统根据其目标决定在评估时要应用哪些要求。比如 macos 的 <I>Gatekeeper</I> 有一个规则,即在首次允许启动应用程序之前,必须由Mac App Store或开发人员ID证书签名。再比如一个应用程序可以强制执行一个规范要求,即该应用程序使用的所有插件均应由 Apple 签名。有关规范的详细描述,可以查看<a href="https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/AboutCS/AboutCS.html#//apple_ref/doc/uid/TP40005929-CH3-SW2" target="_blank" rel="noopener">苹果官方 Code Requirements</a>。</p><h2 id="数字签名原理"><a href="#数字签名原理" class="headerlink" title="数字签名原理"></a>数字签名原理</h2><h3 id="数字签名过程"><a href="#数字签名过程" class="headerlink" title="数字签名过程"></a>数字签名过程</h3><p>接下来,我们通过图例来阐述单个数据(这里指一个文件)的数字签名的过程: </p><ol><li>签名者使用加密哈希算法,生成信息摘要</li><li>使用私钥对信息摘要进行加密</li><li>将加密的信息摘要、有关签名者数字证书的信息,以及数据本身打包在一起,完成数字签名<br><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/ios-app-resigning/code-signing.png" alt="Digital Signatures Process"> </li></ol><p><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/ios-app-resigning/signing-doc.png" alt="a" style="zoom:60%;" /></p><h3 id="验证数字签名过程"><a href="#验证数字签名过程" class="headerlink" title="验证数字签名过程"></a>验证数字签名过程</h3><p>比如我们对一个文档进行了数据签名,那接收者如何去验证数字签名的正确性呢?下面我们结合图例来阐述下验证的过程: </p><ol><li>接收者从签名者的证书中获取签名者的公钥</li><li>使用公钥对加密信息摘要进行解密</li><li>接收者使用证书中指定的算法创建数据的新的信息摘要</li><li>将新的信息摘要与数字签名中传递的信息摘要的解密副本进行比较。 如果它们匹配,则接收到的数据肯定于签名者创建的原始数据相同。<br><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/ios-app-resigning/verify-signature.png" alt="Verify digital signature" style="zoom:60%;" /></li></ol><h2 id="iOS-App-代码签名"><a href="#iOS-App-代码签名" class="headerlink" title="iOS App 代码签名"></a>iOS App 代码签名</h2><p>iOS 代码签名比之前提到的<a href="###数字签名过程">数字签名过程</a>会复杂一些,在签名过程中,还需要加入 <I>Provisioning Profile</I>,并且要对各种代码文件分别进行数字签名。</p><h3 id="需要对哪些文件签名"><a href="#需要对哪些文件签名" class="headerlink" title="需要对哪些文件签名"></a>需要对哪些文件签名</h3><p>iOS app 有三类文件需要签名: </p><ul><li><p><strong>内嵌代码 (<I>Nested code</I>)</strong><br>包括所有的打包在 App 内的 libraries, frameworks, helpers, tools, 以及 app 依赖的其它组件。<font color=red>注意静态库请不要内嵌,否则重签名后的 ipa 文件上传到 iTunes Connect 会报错。</font></p></li><li><p><strong>Mach-O 可执行文件 (<I>Mach-O executables</I>)</strong><br>签名软件会将签名信息直接写入可执行文件中。可执行文件包括 App 可执行文件 和 内嵌的 Framework 中的可执行文件。</p></li><li><p><strong>资源文件 (<I>Resources</I>)</strong><br>除了代码及可执行文件以外的文件都属于资源文件,都需要进行签名。每个资源文件签完名后,签名信息会被记录 <a href="###CodeResources">CodeResoures</a> 中,存放在 <code>_CodeSignature</code> 目录下。App 中可能会有多个 <code>_CodeSignature</code> 目录,因为 App 的 <I> main bundle</I> 中可能包含有内嵌 <I>framework bundle</I>, 或者其它的 <I>resource bundle</I>。</p></li></ul><h3 id="Entitlements"><a href="#Entitlements" class="headerlink" title="Entitlements"></a>Entitlements</h3><p>权限文件是一个特殊的 <em>plist</em> 文件,可以被认为是写入应用签名中的字符串,该字符串允许特定功能或使该应用选择特定服务。 操作系统在允许应用访问某些功能之前会检查这些字符串。 比如,在允许应用程序在运行时访问 Wi-Fi 信息之前,该应用程序必须具有 <em>Wi-Fi Info</em> 权利。 关于 <em>Entitlements</em> 更多的详细信息,<a href="https://developer.apple.com/library/archive/technotes/tn2318/_index.html#//apple_ref/doc/uid/DTS40013777-CH1-TNTAG33" target="_blank" rel="noopener">请点这里</a>。</p><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="meta"><?xml version="1.0" encoding="UTF-8"?></span></span><br><span class="line"><span class="meta"><!DOCTYPE <span class="meta-keyword">plist</span> <span class="meta-keyword">PUBLIC</span> <span class="meta-string">"-//Apple//DTD PLIST 1.0//EN"</span> <span class="meta-string">"http://www.apple.com/DTDs/PropertyList-1.0.dtd"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">plist</span> <span class="attr">version</span>=<span class="string">"1.0"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">dict</span>></span></span><br><span class="line"><span class="tag"><<span class="name">key</span>></span>application-identifier<span class="tag"></<span class="name">key</span>></span></span><br><span class="line"><span class="tag"><<span class="name">string</span>></span>AA11BB22CC.com.company.appresignature.test<span class="tag"></<span class="name">string</span>></span></span><br><span class="line"><span class="tag"><<span class="name">key</span>></span>beta-reports-active<span class="tag"></<span class="name">key</span>></span></span><br><span class="line"><span class="tag"><<span class="name">true</span>/></span></span><br><span class="line"><span class="tag"><<span class="name">key</span>></span>com.apple.developer.networking.wifi-info<span class="tag"></<span class="name">key</span>></span></span><br><span class="line"><span class="tag"><<span class="name">true</span>/></span></span><br><span class="line"><span class="tag"><<span class="name">key</span>></span>com.apple.developer.team-identifier<span class="tag"></<span class="name">key</span>></span></span><br><span class="line"><span class="tag"><<span class="name">string</span>></span>AA11BB22CC<span class="tag"></<span class="name">string</span>></span></span><br><span class="line"><span class="tag"><<span class="name">key</span>></span>get-task-allow<span class="tag"></<span class="name">key</span>></span></span><br><span class="line"><span class="tag"><<span class="name">false</span>/></span></span><br><span class="line"><span class="tag"><<span class="name">key</span>></span>keychain-access-groups<span class="tag"></<span class="name">key</span>></span></span><br><span class="line"><span class="tag"><<span class="name">array</span>></span></span><br><span class="line"><span class="tag"><<span class="name">string</span>></span>AA11BB22CC.*<span class="tag"></<span class="name">string</span>></span></span><br><span class="line"><span class="tag"></<span class="name">array</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dict</span>></span></span><br><span class="line"><span class="tag"></<span class="name">plist</span>></span></span><br></pre></td></tr></table></figure><h3 id="Provisioning-Profile"><a href="#Provisioning-Profile" class="headerlink" title="Provisioning Profile"></a>Provisioning Profile</h3><p><I>Provisioning Profile</I> 是一种系统配置文件,可让您在iOS设备或Mac上安装应用程序,用于在设备上启动一个或多个应用程序并使用某些服务。</p><p><I>Provisioning Profile</I> 文件包含签名证书、app bundle ID、Entitlements 信息,或者 deivce ids,不过 <I>Provisioning Profile</I> 可以分为 <I>Development / Distribution</I> 类型,而 <I>Distribution</I> 又分为 <I>AppStore / AdHoc / Inhouse</I> / Developer ID。每种类型的 <I>Provisioning Profile</I> 包含的信息都有些不一样,下图为 AppStore <I>Provisioning Profile</I> 包含的信息:</p><p><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/ios-app-resigning/provisioning-profile.jpg" alt="AppStore Provisioning Profile" style="zoom:50%;" /></p><h3 id="CodeResources"><a href="#CodeResources" class="headerlink" title="CodeResources"></a>CodeResources</h3><p><I>CodeResources</I> 是一个特殊的 Plist 文件,<I>bundle</I> 中的每个资源文件的加密<a href="###信息摘要">信息摘要</a>都记录在这个 <I>Plist</I> 文件中。除了这些加密信息摘要以外,它还包括一些签名规则,可用于接收端进行签名验证。下图为 <I>CodeResources</I> 文件所包含的内容:</p><p><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/ios-app-resigning/code-resource.png" alt="CodeResources" style="zoom:60%;" /></p><h3 id="App-签名"><a href="#App-签名" class="headerlink" title="App 签名"></a>App 签名</h3><p>大多数时候,Xcode可以帮我们自动完成所有的签名操作。但如果是手动来签名的话,我们需要使用 Apple 提供的 <code>codesign</code> 程序来签名。<code>codesign</code> 的签名规则如下:</p><ul><li><code>codesign</code> 可以直接给一个 <em>Bundle</em> 进行签名,这里的 <em>Bundle</em> 可以是 <em>app bundle (.app)</em> ,<em>framework bundle (.framework)</em>,<em>dynamic library (.dylib)</em> 等。</li><li>它采用递归策略对 <code>Bundle</code> 下的每个文件进行签名。<code>Bundle</code> 实际上就是一个文件夹。</li><li>可执行文件的加密信息摘要会被直接写入可执行文件的本身。这里说的可执行文件可以是 executable, Mach-O 文件类型,比如 app 的可执行文件,framework 中的可执行文件。</li><li>先从最底层 <code>Bundle</code> ,再签上层 <code>Bundle</code>。比如先签 <em>app bundle</em> 中的内嵌的每个 <em>framework bundle</em> ,然后签 <em>app bundle</em>。</li></ul><p><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/ios-app-resigning/ios-app-code-signing.png" style="zoom:40%;" /></p><p>上图为 iOS app 代码签名的概览图,现在我们结合这张图来介绍 app 的签名流程。</p><ul><li><p>准备好签名要用到的所有资源:<em>ipa</em> 或 <em>xcarchive</em>,provisioning profile,签名证书。</p></li><li><p>将 App 中的各类代码文件(如库、可执行文件、脚本、资源文件,以及其他一些像代码的数据)按照<a href="###数字签名过程">数字签名过程</a>并结合签名规则进行签名,并把签名得到的 encryted seal 记录在 CodeResource 文件中。可执行文件 (Mach-O文件) 的 encryted seal 不会记录在 CodeResource, 而是直接写入可执行文件本身。</p></li><li><p>签完名后,重新打包 IPA。</p></li></ul><h2 id="App-重签名实战-★"><a href="#App-重签名实战-★" class="headerlink" title="App 重签名实战 ★"></a>App 重签名实战 <font color=red>★</font></h2><p>接下来,我们用苹果提供的命令行程序 <code>codesign</code> 来实现手动签名。如想了解 <code>codesign</code> 命令的具体用法,可以查看<a href="https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html#//apple_ref/doc/uid/TP40005929-CH4-SW2" target="_blank" rel="noopener">这篇文章</a>的 <I>Signing Code Manually</I> 小节。</p><h3 id="重签名环境"><a href="#重签名环境" class="headerlink" title="重签名环境"></a>重签名环境</h3><ul><li>MacOS: 10.15.4 </li><li>Xcode: version 11.5 </li></ul><h3 id="App-重签名参数配置文件"><a href="#App-重签名参数配置文件" class="headerlink" title="App 重签名参数配置文件"></a>App 重签名参数配置文件</h3><p>App 重签名需要提供一些参数,我们把这些参数都集中放到这个<a href="https://gitee.com/evanxlh/MyComputerToolkit/raw/master/Shells/ios-app-resigning/resigning-params-configuration.plist" target="_blank" rel="noopener">参数配置文件</a>中。以下为参数列表:</p><ul><li><p><strong>RootWorkingDirectory</strong> (必须)<br>Shell 脚本工作的根目录,<em>.ipa</em> 或 <em>.xcarchive</em>,provisioning profile 必需放在这个目录下。 <em>.ipa</em> 或者 <em>.xcarchive</em>,两者不能同时存在。</p></li><li><p><strong>SignIdentity</strong> (必须)<br>签名用的身份信息,可以在命令控制台终端输入 <code>security find-identity</code> ,列出 KeyChain Access 中的所有 <em>identities</em>,然后选择你要用的 <em>identity</em>.</p></li><li><p><strong>NewNameForIPA</strong> (可选)<br>重签名后输出的 <em>ipa</em> 文件名,不包括 <em>.ipa</em> 后缀。不提供则使用默认名。</p></li><li><p><strong>AppleID & AppleIDPassword</strong> (可选)<br>Apple ID 和 password 可选。如果提供,可以将重签名好的 <em>ipa</em> 直接上传到 iTunes Connect。</p></li></ul><p>所以在开始签名前,你的工作目录大概是这样,不过 脚本文件随便放哪都行:</p><div class="table-container"><table><thead><tr><th>重签名 ipa 的工作目录</th><th>重签名 xcarchive 的工作目录</th></tr></thead><tbody><tr><td><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/ios-app-resigning/root-working-dir-ipa.png" alt="Working Directory" style="zoom:67%;" /></td><td><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/ios-app-resigning/root-working-dir-xcarchive.png" alt="xcarchive working directory" style="zoom:67%;" /></td></tr></tbody></table></div><h3 id="Shell-脚本重签名"><a href="#Shell-脚本重签名" class="headerlink" title="Shell 脚本重签名"></a>Shell 脚本重签名</h3><p>Shell 脚本签名分为 10 个步骤,10 个步骤需要按照先后顺序执行, 你也可以在直接查看<a href="https://gitee.com/evanxlh/MyComputerToolkit/raw/master/Shells/ios-app-resigning/ios-app-resigning.sh" target="_blank" rel="noopener">完整的 shell 脚本</a>。<font color=red>脚本要求所有路径中不能有空格,所以请注意您 App 中的所有文件或文件夹名都不能带空格。</font></p><h4 id="1-读取重签名参数配置文件"><a href="#1-读取重签名参数配置文件" class="headerlink" title="1. 读取重签名参数配置文件"></a>1. 读取重签名参数配置文件</h4><p>执行脚本后,终端会提示你将重签名用的参数配置文件拖到控制台(这样就可以得到文件的绝对路径)。<a href="https://gitee.com/evanxlh/MyComputerToolkit/raw/master/Shells/ios-app-resigning/resigning-params-configuration.plist" target="_blank" rel="noopener">点我获取参数配置的样例文件</a>。</p><p><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/ios-app-resigning/resigning-params.png" alt="Read resigning params" style="zoom:67%;" /> </p><h4 id="2-准备需要重签名的-app"><a href="#2-准备需要重签名的-app" class="headerlink" title="2. 准备需要重签名的 app"></a>2. 准备需要重签名的 app</h4><p>脚本支持对 <em>ipa, xcarchive</em> 两种包类型进行重签名。如果是 <em>ipa</em>,我们需要先将其解压;如果是 <em>xcarchive</em>,我们需要自己创建 <strong>IPA</strong> 的内容目录结构,并将 <em>xcarchive</em> 里面的 <em>app bundle</em> 拷贝到创建的 <em>Payload</em> 目录中去。如果 Xcode 工程开启 <code>EMBEDDED_CONTENT_CONTAINS_SWIFT</code>,我们还需要将 <em>SwiftSupport</em> 放入 <strong>IPA</strong>。 </p><p><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/ios-app-resigning/embed-swift-standard-libraries.png" style="zoom:67%;" /> </p><p>最后 <strong>IPA</strong> 中的内容就如下图所示(SwiftSupport 需视实际情况来决定):</p><p><img src="https://gitee.com/evanxlh/Resources/raw/master/blog/ios-app-resigning/ipa-contents.png" alt="App Contents" style="zoom:67%;" /> </p><h4 id="3-删除原有的-CodeSignature-目录"><a href="#3-删除原有的-CodeSignature-目录" class="headerlink" title="3. 删除原有的 _CodeSignature 目录"></a>3. 删除原有的 _CodeSignature 目录</h4><p>先递归查找 <em>app bundle</em> 中所有的 <code>_CodeSignature</code> 目录,然后将它们逐一删除。<br><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">oldSignatures=`find $app_contents_root_path -name "_CodeSignature"`</span><br><span class="line"></span><br><span class="line">for signature in $oldSignatures; do</span><br><span class="line"> rm -rf $signature</span><br><span class="line">done</span><br></pre></td></tr></table></figure></p><h4 id="4-生成-entitlements-plist"><a href="#4-生成-entitlements-plist" class="headerlink" title="4. 生成 entitlements.plist"></a>4. 生成 entitlements.plist</h4><p><code>entitlements.plist</code> 在签名 <em>app bundle</em> 时需要使用,我们可以从提供的 <em>Provisioning Profile</em> 中提取出相关信息,并生成 <code>entitlements.plist</code>。</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 从 Provisioning Profile 中提取出来的 entitlements 信息存储路径</span></span><br><span class="line">entitlements_plist_path="${root_working_dir_path}/entitlements.plist"</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 将 *.mobileprovision 文件中的信息输出到一个临时plist</span></span><br><span class="line">security cms -D -i $new_profile_path > tempProfile.plist</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 从临时plist中提取出 entitlements 信息并写入 entitlements.plist</span></span><br><span class="line">/usr/libexec/PlistBuddy -x -c 'Print :Entitlements' tempProfile.plist > $entitlements_plist_path</span><br></pre></td></tr></table></figure><h4 id="5-替换-Provisioning-Profile"><a href="#5-替换-Provisioning-Profile" class="headerlink" title="5. 替换 Provisioning Profile"></a>5. 替换 Provisioning Profile</h4><p>将新的 <em>Provisioning Profile</em> 拷贝到 <em>app bundle</em> 中,替换原有的 <em>Provisioning Profile</em>。</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">cp $new_profile_path $app_profile_path</span><br></pre></td></tr></table></figure><h4 id="6-替换-Bundle-ID"><a href="#6-替换-Bundle-ID" class="headerlink" title="6. 替换 Bundle ID"></a>6. 替换 Bundle ID</h4><p>新的 <em>bundle id</em>,我们可以从之前生成的 <code>entitlements.plist</code> 中的 <em>application-identifier</em> 提取,但它包含了 <em>Team ID</em>, 需要将其移除。然后用新的 <em>bundle id</em> 替换 <em>app bundle</em> 中的 <code>Info.plist</code> 中的 <em>bundle id</em>。</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> E6ABDGA.com.company.appresignature.test</span></span><br><span class="line">local app_identifier=`/usr/libexec/PlistBuddy -c "Print :application-identifier" $entitlements_plist_path`</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> https://stackoverflow.com/questions/10586153/split-string-into-an-array-in-bash</span></span><br><span class="line">IFS='.' read -r -a components <<< "${app_identifier}"</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> Remove `E6ABDGA`: https://askubuntu.com/questions/435996/how-can-i-remove-an-entry-from<span class="_">-a</span>-list-in<span class="_">-a</span>-shells-script</span></span><br><span class="line">unset components[0]</span><br><span class="line">new_bundle_id=`joinStringComponents "." "${components[@]}"`</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> Replace the bundle id <span class="keyword">in</span> Info.plist with new bundle id.</span></span><br><span class="line">plutil -replace CFBundleIdentifier -string $new_bundle_id $app_infoplist_path</span><br></pre></td></tr></table></figure><h4 id="7-对内嵌-Frameworks-签名"><a href="#7-对内嵌-Frameworks-签名" class="headerlink" title="7. 对内嵌 Frameworks 签名"></a>7. 对内嵌 Frameworks 签名</h4><p>万事俱备只欠东风,我们现在正式开始签名。首先找出 <em>app bundle</em> 中的内嵌 Frameworks 和动态库,然后使用<a href="###数字身份">签名身份</a>逐一进行签名。</p><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">local frameworks=`find $app_frameworks_path -name "*.framework" -o -name "*.dylib"`</span><br><span class="line"></span><br><span class="line">for framework in $frameworks; do</span><br><span class="line"> codesign -f -s "${sign_identity}" $framework</span><br><span class="line">done</span><br></pre></td></tr></table></figure><h4 id="8-对-App-Bundle-签名"><a href="#8-对-App-Bundle-签名" class="headerlink" title="8. 对 App Bundle 签名"></a>8. 对 App Bundle 签名</h4><p>对 <em>app bundle</em> 签名除了需要<a href="###数字身份">签名身份</a>, 还需要使用 <code>entitlements.plist</code>。<br><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">codesign -f -s "${sign_identity}" --entitlements "${entitlements_plist_path}" $app_bundle_path</span><br></pre></td></tr></table></figure></p><h4 id="9-验证签名"><a href="#9-验证签名" class="headerlink" title="9. 验证签名"></a>9. 验证签名</h4><p>到目前为止,app 的签名工作已完成,现在我们来验证签名是否有效:<br><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash">https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html<span class="comment">#//apple_ref/doc/uid/TP40005929-CH4-SW9</span></span></span><br><span class="line">codesign --verify --deep --strict --verbose=2 $app_bundle_path</span><br></pre></td></tr></table></figure></p><p>如果验证失败,控制台会输出详细的错误。如果验证成功,控制台会输出:<br><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">/path/to/Test.app: valid on disk</span><br><span class="line">/path/to/Test.app: satisfies its Designated Requirement</span><br></pre></td></tr></table></figure></p><h4 id="10-制作已重签名的-IPA"><a href="#10-制作已重签名的-IPA" class="headerlink" title="10. 制作已重签名的 IPA"></a>10. 制作已重签名的 IPA</h4><p>签名验证也成功了,最后我们将所有的 App 内容重新压缩成 <em>.ipa</em> 文件,然后输出到指定目录(ResignedIPAs目录)。在压缩文件时,注意要忽略 <code>.DS_Store</code>。<br><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">local root_content_names=`ls $app_contents_root_path`</span><br><span class="line">local contents_will_zipped=""</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 列出 app contents 根目录下的所有内容</span></span><br><span class="line">for name in $root_content_names; do</span><br><span class="line"> contents_will_zipped+="$name "</span><br><span class="line">done</span><br><span class="line"></span><br><span class="line">cd $app_contents_root_path</span><br><span class="line">zip -qr $new_ipa_name $contents_will_zipped -x "*.DS_Store"</span><br><span class="line">mv $new_ipa_name $ipa_output_directory</span><br></pre></td></tr></table></figure></p><h4 id="11-上传至-iTunes-Connect-可选"><a href="#11-上传至-iTunes-Connect-可选" class="headerlink" title="11. 上传至 iTunes Connect (可选)"></a>11. 上传至 iTunes Connect (可选)</h4><p>这一步为可选的,我们也可以将重签名的 <em>ipa</em> 直接通过命令行上传至 iTunes Connect。如果在重签名参数配置文件中有填写 <em>Apple ID</em> 和 <em>Apple ID Password</em>,Shell 脚本将执行这一步。<br><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">xcrun altool --upload-app -f $reigned_ipa_path -t iOS -u $apple_id -p $apple_id_password</span><br></pre></td></tr></table></figure></p><h3 id="常见错误解决"><a href="#常见错误解决" class="headerlink" title="常见错误解决"></a>常见错误解决</h3><p>常见的签名错误你都可以在这两个地方找到 <a href="https://developer.apple.com/library/archive/technotes/tn2318/_index.html" target="_blank" rel="noopener">Troubleshooting Failed Signature Verification</a> ,<a href="https://developer.apple.com/library/archive/technotes/tn2415/_index.html" target="_blank" rel="noopener">Entitlements Troubleshooting</a></p><h3 id="脚本及参数配置文件获取"><a href="#脚本及参数配置文件获取" class="headerlink" title="脚本及参数配置文件获取"></a>脚本及参数配置文件获取</h3><p>以下是完整的重签名 shell 脚本和参数配置文件:<br><a href="https://gitee.com/evanxlh/MyComputerToolkit/raw/master/Shells/ios-app-resigning/ios-app-resigning.sh" target="_blank" rel="noopener">ios-app-resigning.sh</a><br><a href="https://gitee.com/evanxlh/MyComputerToolkit/raw/master/Shells/ios-app-resigning/resigning-params-configuration.plist" target="_blank" rel="noopener">resigning-params-configuration.plist</a><br>写了这么久,总算完成了。可以好好休息下了!🍹</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="https://en.wikipedia.org/wiki/Public-key_cryptography" target="_blank" rel="noopener">Asymmetric Cryptography</a><br><a href="https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Introduction/Introduction.html" target="_blank" rel="noopener">Code Signing Guide</a><br><a href="https://developer.apple.com/library/archive/documentation/Security/Conceptual/cryptoservices/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011172-CH1-SW1" target="_blank" rel="noopener">Cryptographic Services Guide</a><br><a href="https://developer.apple.com/library/archive/documentation/Security/Conceptual/Security_Overview/Introduction/Introduction.html#//apple_ref/doc/uid/TP30000976-CH1-SW1" target="_blank" rel="noopener">Software Security Overview</a><br><a href="https://developer.apple.com/library/archive/technotes/tn2318/_index.html" target="_blank" rel="noopener">Technical Note TN2318: Troubleshooting Failed Signature Verification</a><br><a href="https://developer.apple.com/library/archive/technotes/tn2415/_index.html" target="_blank" rel="noopener">Technical Note TN2415: Entitlements Troubleshooting</a></p>]]></content>
<categories>
<category> iOS开发 </category>
<category> Xcode </category>
</categories>
<tags>
<tag> Build & Release </tag>
</tags>
</entry>
</search>