要解决的问题
- 复用其他人的工作
- 不需要运行时支持动态链接库
- 不共享源代码的前提下复用
解决方案
静态链接库 linker 在构建的时期,把 object 文件和 static library 预先合并成一个完整的 executable。 object 与 static library 彼此知道自己的链接关系。
构成
构成 | 解释 |
---|---|
object | 第一方开发的可执行代码 |
static library | 第三方开发的可执行代码 |
static library linker | 把 object 和 static library 链接成 executable |
executable | 给 executor 执行用的可执行文件 |
衍生的问题
解决方案案例
browserify
browserify 把多个 CJS 格式的静态链接库链接成一个完整自包含的executable。
main.js
// 引用的 uniq 包会被链接到最终的 executable 里
var unique = require('uniq');
var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];
console.log(unique(data));
build.sh
browserify main.js -o bundle.js
构成 | 解释 |
---|---|
object | main.js |
static library | uniq |
static library linker | browserify |
executable | bundle.js |
webpack
webpack 是一个更全面的静态链接库的链接器,是 browserify 的改进。
src/main.js
// 引用的 uniq 包会被链接到最终的 executable 里
var unique = require('uniq');
var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];
console.log(unique(data));
build.sh
webpack --mode development --entry ./src/main.js --output ./dist/bundle.js
构成 | 解释 |
---|---|
object | src/main.js |
static library | uniq |
static library linker | webpack |
executable | dist/bundle.js |
rollup
rollup 的出发点是改进静态链接库的格式。 采用 ES6 Module 的导入导出语法,从而实现 tree-shaking 来减少静态链接后的文件尺寸。
src/main.js
// 引用的 uniq 包会被链接到最终的 executable 里
import unique from 'uniq'
var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];
console.log(unique(data));
rollup.config.js
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
export default {
input: 'src/main.js',
output: {
file: 'dist/bundle.js',
format: 'iife', // immediately-invoked function expression — suitable for <script> tags
sourcemap: true
},
plugins: [
resolve(), // tells Rollup how to find date-fns in node_modules
commonjs(), // converts date-fns to ES modules
]
};
build.sh
rollup -c
构成 | 解释 |
---|---|
object | src/main.js |
static library | uniq |
static library linker | rollup |
executable | dist/bundle.js |
gcc
ar 把多个 linux 下用 c 编译出来的 .o 文件,合并成 .a 静态链接文件
gcc 把 .o 文件和 .a 文件静态链接成 executable
file1.c
int hello() {
return 1;
}
file2.c
int world() {
return 2;
}
build1.sh
gcc -c file1.c # 创建 file1.o 提供 hello
gcc -c file2.c # 创建 file2.o 提供 world
ar -r lib12.a *.o # 把两个object文件合并成一个静态链接库
演示静态链接库如何产生出来
main.c
int hello();
int world();
int main() {
return hello() + world();
}
build2.sh
gcc -c main.c # 创建 main.o,引用 hello / world 这两个符号
gcc -o main main.o lib12.a # 把静态链接库链接成 executable
./main # 返回值应该是 1 + 2 = 3
echo $?
演示静态链接库如何被使用
构成 | 解释 |
---|---|
object | file1.o file2.o main.o |
static library | lib12.a |
static library linker | gcc |
executable | ./main |
rustc
rustc 可以编译 .a 文件,也可以把多个 .a 文件链接成可执行文件
file1.rs
#![crate_type = "staticlib"]
#[no_mangle]
pub fn hello() -> i32 {
return 1;
}
file2.rs
#![crate_type = "staticlib"]
#[no_mangle]
pub fn world() -> i32 {
return 2;
}
build1.sh
rustc file1.rs # 创建 libfile1.a
rustc file2.rs # 创建 libfile2.a
演示静态链接库如何产生出来
main.rs
#[link(name = "file1")]
extern "C" {
fn hello() -> i32;
}
#[link(name = "file2")]
extern "C" {
fn world() -> i32;
}
fn main() {
let r = unsafe { hello() + world() };
::std::process::exit(r)
}
build2.sh
rustc main.rs -L . # 链接当前目录下的 libfile1.a libfile2.a
./main # 返回值应该是 1 + 2 = 3
echo $?
演示静态链接库如何被使用
构成 | 解释 |
---|---|
object | 没有 |
static library | libfile1.a libfile2.a |
static library linker | rustc |
executable | ./main |