前言
本文是对于静态库和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.h和TestA.m,新建文件夹TestB并创建文件TestB.h和TestB.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.h和TestB.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.h和TestB.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.a和libTestB.a进行合并,链接一个静态库就可以了,在testDemo目录中新建文件夹TestAB,将TestA和TestB中的头文件拷贝一份到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.framework和TestB.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
这时我们已经生成了静态库TestA和TestB,基于此执行一下命令
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来进入某个路径中...