• 0

  • 478

  • 收藏

iOS强化3.1-静态库&shell初探

4星期前

前言

本文是对于静态库和shell脚本的初探,使用最简单的例子一步步演示从.m代码到目标文件.o再到静态库.a,其中还涉及到framework的简单介绍和静态库合并相关内容

一、单文件的编译、链接、运行

本节将演示.m文件从编译、链接到运行的完整流程,熟悉整体过程

1.1 编译

桌面新建文件夹testDemo,创建Test.m文件

#import <Foundation/Foundation.h>

int main(){
    NSLog(@"testApp----");
    return 0;
}
复制代码

终端进入文件夹testDemo并执行命令将Test.m文件编译为目标文件Test.o

clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-c Test.m -o Test.o
复制代码

生成了目标文件

clang是什么???进入终端执行命令man clang

  • -x指定语言为objective-c
  • -target指定架构为x86_64-apple-macos11.1,这是我的电脑系统
  • isysroot代码用到了Foundation库的NSLog,我使用了XCode中的Foundation

1.2 链接

终端进入文件夹testDemo并执行命令将目标文件Test.o链接为可执行文件Test

clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
Test.o -o Test
复制代码

可执行文件生成成功

1.3 运行

终端进入文件夹testDemo并执行命令运行可执行文件Test

./Test
复制代码

运行成功

1.4 shell脚本实现

我们完成这个简单的过程需要写如此之多的代码,能不能搞个脚本实现呢?? 在testDemo文件夹中新建文件build.sh

echo "---开始编译Test.m---"
clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-c Test.m -o Test.o
echo "---编译结束Test.m---"

echo "---开始链接Test.o---"
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
Test.o -o Test
echo "---链接结束生成可执行文件Test---"

echo "---执行可执行文件Test---"
./Test
echo "---执行结束---"
复制代码

执行脚本build.sh,报无权限zsh: permission denied: ./build.sh,通过命令chmod +x ./build.sh授权,再次执行脚本

二、文件依赖

在上一节中我们演示了单个.m文件从编译到链接再到执行的整个流程,本节演示有文件依赖的情况。在文件夹testDemo新建文件夹TestA并创建文件TestA.hTestA.m,新建文件夹TestB并创建文件TestB.hTestB.m

TestA.h文件内容为

#import <Foundation/Foundation.h>

@interface TestA: NSObject
- (void)lg_testA:(_Nullable id)e;
@end
复制代码

TestA.m文件内容为

#import "TestA.h"

@implementation TestA
- (void)lg_testA:(_Nullable id)e{
    NSLog(@"testA----");
}
@end
复制代码

TestB.h文件内容为

#import <Foundation/Foundation.h>

@interface TestB: NSObject
- (void)lg_testB:(_Nullable id)e;
@end
复制代码

TestB.m文件内容为

#import "TestB.h"

@implementation TestB
- (void)lg_testB:(_Nullable id)e{
    NSLog(@"testB----");
}
@end
复制代码

文件Test.m依赖TestA.hTestB.h文件

#import <Foundation/Foundation.h>
#import "TestA.h"
#import "TestB.h"

int main(){
    NSLog(@"testApp----");
    TestA *managerA = [TestA new];
    [managerA lg_testA: nil];
    
    TestB *managerB = [TestB new];
    [managerB lg_testB: nil];
    return 0;
}
复制代码

2.1 编译

这时候编译需要指定依赖文件TestA.hTestB.h的路径

clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-I./TestA \
-I./TestB \
-c Test.m -o Test.o
复制代码

执行成功

  • -I参数执行依赖头文件的位置./TestA,如果依赖多个文件那么-I可以写多个

我们还需要编译TestA.m文件,进入文件夹路径TestA执行命令

clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-c TestA.m -o TestA.o
复制代码

成功生成了目标文件TestA.o

编译TestB.m文件,进入文件夹路径TestB执行命令

clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-c TestB.m -o TestB.o
复制代码

成功生成了目标文件TestB.o

2.2 生成静态库

链接的时候不能直接使用目标文件.o,我们这里先打包成静态库,进入目录TestA执行命令

ar -rc libTestA.a TestA.o
复制代码

成功生成静态库libTestA.a

进入目录TestB执行命令

ar -rc libTestB.a TestB.o
复制代码

成功生成静态库libTestB.a

  • ar压缩目标文件,对其进行编号和索引,形成静态库。同时也可以解压缩静态库,查看有哪些目标文件。
  • -r添加or替换文件
  • -c不输出任何信息
  • -t列出包含的目标文件

例如我想查看libTestB.a中包含哪些目标文件

2.3 链接

现在Test.m的目标文件和其依赖的静态库都已经生成了,现在要链接生成可执行文件,进入TestB.o所在目录执行命令

clang \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-L./TestA \
-lTestA \
-L./TestB \
-lTestB \
Test.o -o Test
复制代码

生成了可执行文件Test

  • -L执行静态库所在路径
  • -l指定依赖库,寻找规则先寻找lib{NAME}.a静态库,找不到再寻找lib{NAME}.dylib动态库

2.4 执行

./Test
复制代码

执行成功

2.5 静态库合并

我们将静态库libTestA.alibTestB.a进行合并,链接一个静态库就可以了,在testDemo目录中新建文件夹TestAB,将TestATestB中的头文件拷贝一份到TestAB

进入TestAB目录执行命令

libtool \
-static \
-o \
libTestAB.a \
../TestA/libTestA.a \
../TestB/libTestB.a
复制代码

合并成功

  • libtool可以创建动态库和静态库
  • static生成静态库
  • libTestAB.a合并后生成静态库的名字

编译Test.m生成Test.o

clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-I./TestAB \
-c Test.m -o Test.o
复制代码

编译成功

链接Test.o生成可执行文件Test

clang \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-L./TestAB \
-lTestAB \
Test.o -o Test
复制代码

可执行文件生成成功

运行

./Test
复制代码

2.6 shell脚本实现

脚本实现从编译->静态库合并->链接->运行一条龙服务

echo "---开始编译Test.m---"
clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-I./TestAB \
-c Test.m -o Test.o
echo "---编译结束Test.m---"

echo "---进目录TestA---"
pushd ./TestA
echo "---开始编译TestA.m---"
clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-c TestA.m -o TestA.o
ar -rc libTestA.a TestA.o
echo "---编译结束TestA.m---"
popd
echo "---出目录TestA---"


echo "---进目录TestB---"
pushd ./TestB
echo "---开始编译TestB.m---"
clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-c TestB.m -o TestB.o
ar -rc libTestB.a TestB.o
echo "---编译结束TestB.m---"
popd
echo "---出目录TestB---"

echo "---进目录TestAB---"
pushd ./TestAB
echo "---开始合并动态库---"
libtool \
-static \
-o \
libTestAB.a \
../TestA/libTestA.a \
../TestB/libTestB.a
echo "---动态库合并成功---"
popd
echo "---出目录TestAB---"


echo "---开始链接Test.o---"
clang \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-L./TestAB \
-lTestAB \
Test.o -o Test
echo "---链接成功---"

echo "---开始运行---"
./Test
echo "---运行结束---"

复制代码

运行成功!!!

三、framework

3.1 编译

本节主要演示framework包的生成和依赖,framework包含头文件Headers和静态库或者动态库,这里我们只演示静态库

TestDemo目录中新建文件夹Frameworks,在Frameworks中分别新建TestA.frameworkTestB.framework,然后分别新建Headers放头文件

先编译TestA.m生成目标文件TestA.o,然后打包成静态库TestA

进入目录TestA.framework执行编译命令

clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-I./Headers \
-c TestA.m -o TestA.o
复制代码

执行命令生成静态库

ar -rc TestA TestA.o
复制代码

执行结果

同样编译TestB.m生成目标文件TestB.o,然后打包成静态库TestB

进入目录TestB.framework执行编译命令

clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-I./Headers \
-c TestB.m -o TestB.o
复制代码

执行命令生成静态库

ar -rc TestB TestB.o
复制代码

执行结果

编译Test.m文件为目标文件Test.o

进入testDemo文件路径执行命令

$ clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-I./Frameworks/TestA.framework/Headers \
-I./Frameworks/TestB.framework/Headers \
-c Test.m -o Test.o
复制代码

3.2 链接

目标文件Test.o链接生成可执行文件Test

clang \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-F./Frameworks \
-framework TestA \
-framework TestB \
Test.o -o Test
复制代码

链接成功,生成了可执行文件

  • -F指定依赖的framework所在目录
  • -framework指定framework

3.3 运行

./Test
复制代码

运行成功了

3.4 静态库合并

我们可以合并静态库只生成一个framework,只链接一个库就可以了

进入testDemo文件夹新建TestAB.framework,并创建Headers

这时我们已经生成了静态库TestATestB,基于此执行一下命令

libtool \
-static \
-o \
TestAB \
../TestA.framework/TestA \
../TestB.framework/TestB
复制代码

静态库合并完成

使用合并之后的静态库完成链接和运行,这个时候我们只链接了一个framework

$ clang \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-F./Frameworks \
-framework TestAB \
Test.o -o Test
复制代码

运行

./Test
复制代码

3.4 脚本实现

echo "---开始编译Test.m---"
clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-I./Frameworks/TestAB.framework/Headers \
-c Test.m -o Test.o
echo "---编译结束Test.m---"

echo "---进目录TestA---"
pushd ./Frameworks/TestA.framework
echo "---开始编译TestA.m---"
clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-I ../TestAB.framework/Headers \
-c TestA.m -o TestA.o
ar -rc TestA TestA.o
echo "---编译结束TestA.m---"
popd
echo "---出目录TestA---"


echo "---进目录TestB---"
pushd ./Frameworks/TestB.framework
echo "---开始编译TestB.m---"
clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-I ../TestAB.framework/Headers \
-c TestB.m -o TestB.o
ar -rc TestB TestB.o
echo "---编译结束TestB.m---"
popd
echo "---出目录TestB---"

echo "---进目录TestAB---"
pushd ./Frameworks/TestAB.framework
echo "---开始合并动态库---"
libtool \
-static \
-o \
TestAB \
../TestA.framework/TestA \
../TestB.framework/TestB
echo "---动态库合并成功---"
popd
echo "---出目录TestAB---"


echo "---开始链接Test.o---"
clang \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-F./Frameworks \
-framework TestAB \
Test.o -o Test
echo "---链接成功---"

echo "---开始运行---"
./Test
echo "---运行结束---"

复制代码

运行成功

  • pushd进入目录栈
  • pushd退出目录栈

脚本里边尽量不要使用cd命令进入某目录,因为cd命令是直接修改了导航栈,修改之后就不能退出当前目录了,只能继续使用cd来进入某个路径中...

免责声明:文章版权归原作者所有,其内容与观点不代表Unitimes立场,亦不构成任何投资意见或建议。

ios

478

相关文章推荐

未登录头像

暂无评论