编译、汇编、链接和包管理(依赖分析)
一. 编译、汇编、链接
众所周知,C++代码一般要经历预处理、编译、汇编、链接四个过程,才能转化为可执行的文件
通常使用g++,例如执行g++ main.cpp -o program生成可执行文件,在window上开发使用MinGW工具链。其它编译器还有Clang,MSVC。
之所以有这些阶段,是出于以下几点考虑
- 提高编译效率:每个文件单独编译为目标文件,方便模块化处理,如果某个文件发生变化,只需要重新编译该文件,而不需要重新编译整个项目,同时可以增量编译
- 多阶段优化:其实就是解耦,可以在不同阶段分别进行针对优化,同时快速定位问题所在。比如编译阶段指令优化,汇编阶段硬件优化
- 兼容性考虑:C++编译生成的汇编代码可以跨平台支持,然后再生成目标架构相关二进制文件
Pyhton,java,go的逻辑也大差不差
Python是解释型语言,(.py)通过解释器动态编译为字节码(.pyc)然后通过PVM逐条解释运行,可以在不同平台解释器上运行
Java 通过 javac 编译为字节码(.class),字节码由 JVM 加载并通过 JIT 编译为机器码,字节码同样可以在不同平台上的JVM上运行
Go 代码(.go)直接编译为二进制文件
二. Go 开发和包管理
1. 开发流程
-
初始化项目:
使用go mod init初始化项目模块:go mod init example.com/myproject- 创建
go.mod文件,记录模块路径和依赖。
- 创建
-
开发代码:
- 编写
main.go或其他模块化代码文件。 - 遵循 Go 的模块化开发原则,确保代码可读性和可维护性。
- 编写
-
测试和调试:
- 编写测试代码文件(
*_test.go),使用go test运行单元测试:go test ./... - 使用调试工具(如
delve)进行代码调试。
- 编写测试代码文件(
-
构建和运行:
- 使用
go build构建二进制文件:go build -o myapp - 使用
go run运行程序:go run main.go
- 使用
-
部署:
- 如果需要将程序打包到容器中,可以使用 Docker:
FROM golang:1.20 WORKDIR /app COPY . . RUN go build -o myapp CMD ["./myapp"]
- 如果需要将程序打包到容器中,可以使用 Docker:
-
公共库开发:
- 创建公共库模块:
go mod init example.com/mylib - 开发代码后,编写
*_test.go文件对库进行全面测试。 - 打包并发布:将库代码推送到远程 Git 仓库。
git push origin main - 在需要使用的项目中通过
go get引入库:go get example.com/mylib@v1.0.0
- 创建公共库模块:
-
更新与维护:
- 在库代码中更新功能或修复问题后,更新版本号(如
v1.1.0)。 - 使用语义化版本控制(SemVer)管理版本。
- 提供详细的变更日志(changelog),便于用户了解新版本改动。
- 在库代码中更新功能或修复问题后,更新版本号(如
2. 制品
-
二进制文件:
Go 的主要制品是独立的二进制文件,无需依赖运行时环境,直接执行。go build -o myapp -
跨平台支持:
通过交叉编译生成适用于不同平台的二进制文件。GOOS=linux GOARCH=amd64 go build -o myapp
3. 公共库
-
创建公共库:
公共库是标准模块,直接通过go mod init初始化,并发布到公共或私有的 Git 仓库。go mod init example.com/mylib -
发布与使用:
- 发布:将库推送到远程仓库。
- 使用:其他项目通过
go get下载并引入模块。
go get example.com/mylib@v1.0.0 -
版本升级:
- 在
go.mod中更新版本号。 - 使用
go get -u更新到最新版本。
- 在
4. 包管理
-
依赖管理工具:
Go 使用go mod自动管理依赖。- 通过
go.mod记录模块信息。 - 通过
go.sum记录依赖的校验信息。
- 通过
-
命令使用:
go mod tidy # 清理不必要依赖 go get <pkg> # 添加或更新依赖 -
依赖审查:
使用go list -m all查看所有依赖模块。
三. Java 开发和包管理
1. 开发流程
-
初始化项目:
使用 Maven 或 Gradle 初始化项目,生成标准目录结构。mvn archetype:generate # Maven 初始化 gradle init # Gradle 初始化 -
开发代码:
- 编写 Java 源文件,通常放置在
src/main/java目录中。 - 遵循分层架构原则,合理划分模块。
- 编写 Java 源文件,通常放置在
-
编译与测试:
- 使用构建工具编译:
mvn compile gradle build - 编写测试代码,放置在
src/test/java目录,运行测试:mvn test gradle test
- 使用构建工具编译:
-
打包与部署:
- 打包成 JAR 文件:
mvn package gradle jar - 如果需要容器化部署,可编写 Dockerfile:
FROM openjdk:17 WORKDIR /app COPY target/myapp.jar myapp.jar CMD ["java", "-jar", "myapp.jar"]
- 打包成 JAR 文件:
-
公共库开发:
- 创建公共模块,编写代码后将其打包为 JAR 文件。
- 发布到中央仓库(如 Maven Central)或公司内部私有仓库。
- 示例(Maven):
<dependency> <groupId>com.example</groupId> <artifactId>mylib</artifactId> <version>1.0.0</version> </dependency>
-
更新与维护:
- 定期检查库的兼容性。
- 发布新版本时,遵循语义化版本管理,提供清晰的变更日志。
2. 制品
-
JAR 文件:
- Java 项目的主要制品是 JAR 文件,包含编译后的字节码和资源文件。
- 打包命令:
mvn package # Maven gradle jar # Gradle
-
WAR 文件(可选):
- Web 项目可打包为 WAR 文件,用于部署到应用服务器(如 Tomcat)。
3. 公共库
-
创建公共库:
创建公共模块,编写代码后将其打包为 JAR 文件并发布。 -
发布与使用:
- 发布到中央仓库(如 Maven Central)或公司内部私有仓库。
- 在其他项目中通过
pom.xml(Maven)或build.gradle(Gradle)添加依赖。 - 示例(Maven):
<dependency> <groupId>com.example</groupId> <artifactId>mylib</artifactId> <version>1.0.0</version> </dependency>
4. 包管理
-
依赖管理工具:
- Maven:通过
pom.xml管理依赖,自动下载并解析依赖树。 - Gradle:通过
build.gradle定义依赖,灵活性更强。
- Maven:通过
-
版本控制与升级:
- 使用语义化版本(
x.y.z)管理依赖版本。 - 定期检查更新,通过
mvn versions:display-dependency-updates或 Gradle 插件实现。
- 使用语义化版本(
-
依赖冲突解决:
使用mvn dependency:tree或gradle dependencies检查依赖冲突。
四. C++ 开发和包管理
1. 开发流程
-
初始化项目:
使用 CMake 初始化项目,生成CMakeLists.txt文件。cmake -S . -B build -
开发代码:
- 编写 C++ 源文件,通常分为头文件(
.h)和实现文件(.cpp)。 - 按模块划分代码,确保代码复用性和易维护性。
- 编写 C++ 源文件,通常分为头文件(
-
构建与测试:
- 使用 CMake 生成构建系统并执行编译:
cmake --build build - 使用测试框架(如 Google Test)编写单元测试:
enable_testing() add_executable(tests tests.cpp) target_link_libraries(tests gtest gtest_main) add_test(NAME MyTest COMMAND tests)
- 使用 CMake 生成构建系统并执行编译:
-
部署与容器化:
如果需要将程序打包到容器中:FROM ubuntu:22.04 RUN apt-get update && apt-get install -y g++ cmake WORKDIR /app COPY . . RUN cmake -S . -B build && cmake --build build CMD ["./build/myapp"]
2. 制品
- 静态库:
将目标文件打包为.a或.lib文件,直接链接到可执行文件中。 - 动态库:
将目标文件打包为.so、.dll或.dylib文件,运行时动态加载。
3. 公共库
-
创建公共库:
编写头文件和实现文件,使用 CMake 配置导出。add_library(MyLib STATIC mylib.cpp) -
发布与使用:
- 发布到私有仓库(如 Artifactory 或手动分发)。
- 使用工具(如 Conan)管理依赖:
conan upload mylib/1.0@user/channel
4. 包管理
-
手动管理:
通过find_package或硬编码路径引入库,维护成本较高。 -
工具化管理:
使用 Conan 或 vcpkg 管理依赖:conan install . --build=missing -
版本升级:
- 定期更新依赖版本,并通过 CI 测试验证兼容性。
- 配置版本锁定策略,确保可复现性。
五. 依赖分析
1. Go
-
依赖管理机制:
- 使用
go.mod明确记录模块依赖和版本。 - 使用私有仓库或代理(如
GOPROXY)提升依赖管理的可控性。
- 使用
-
分析方法:
- 查看依赖树:
go list -m all - 清理冗余依赖:
go mod tidy
- 查看依赖树:
2. Java
-
依赖管理机制:
- Maven 和 Gradle 自动解析依赖树,并提供冲突解决策略(如最近声明优先)。
-
分析方法:
- Maven:
mvn dependency:tree - Gradle:
gradle dependencies
- Maven:
3. C++
-
依赖管理机制:
- 手动管理依赖:通过
find_package或硬编码路径引入。 - 使用工具管理:通过 Conan 或 vcpkg 自动解析依赖。
- 手动管理依赖:通过
-
分析方法:
- 查看目标文件的符号依赖:
nm mylib.a - 检查动态库依赖:
ldd myapp
- 查看目标文件的符号依赖:

浙公网安备 33010602011771号