编程基础(1):总结编译基本过程


前言

笔者曾经是个编程小白(其实现在也是),别人说使用库、编译、链接的时候,完全不知所云,只好口中答应着;曾经是个Linux小白,命令行一报错就完全不知道咋办,上网一顿乱找也不知道怎么做,一度失去了学习的信心。后来看了书、换了个好的电脑,不至于挑灯熬油的等待反馈之后才有所好转。本文不谈编译原理等问题,只从实用的角度出发,来记录编译程序过程的一些想法。大家也可以参考高翔博士的《视觉SLAM十四讲》,里面有更加详细的教学。


提示:以下是本篇文章正文内容,下面案例可供参考

一、编译是什么?

先上一张图帮助大家理解,可能在细节有一些不到位的地方,但是不影响总体。

在这里插入图片描述
简而言之,编译就是把程序变成可执行文件/库文件的过程。可执行文件是我们直接通过文件名就可以运行的,库文件是我们可以链接上去的。笔者最早学习编程是在Windows下,IDE是Visual Studio。Windows固然是当下最流行的操作系统,VS固然是最厉害的IDE,但是它隐藏了很多的过程。一键运行程序,我们只知道报没报错,但是中间的流程并不清楚。在配置库的时候,要进入各种各样的界面中进行配置,还要反复检查(笔者曾经对这些问题相当的搞不清楚)。程序在Debug状态下运行不会产生我们可见到的可执行文件(.exe)(但是在后台确实生成了),我们需要编译的话,要采用cmake,把代码编译成dll和exe。
——————————
而在Linux下,情况大为改观。可执行程序是.out文件,命令行中可以直接输入文件名执行。库文件是.so文件。最初,完成这一任务编译器叫g++(对应C和C++)。我们可以在命令行中手动输入g++命令来生成可执行文件。随着代码工程越来越大,文件之间的关系越来越复杂,我们希望能够在定义好文件关系的基础上让程序自己编译,这就有了CMake工具。为了使用CMake,我们需要编写CMakeList.txt文件。从实用的角度,本文对“如何编译自己的程序”,“如何使用别人的程序”进行分析(均基于Linux)。

二、如何编译并执行自己的程序

我们使用一个光束法平差的程序来示例。其中,工程名叫bundle_adjustment,包含两个准备编译为可执行文件的cpp(bundle_adjustment_g2o.cpp & bundle_adjustment_ceres.cpp)以及一个准备编译为库文件的common.cpp。另外使用的库有,优化库g2o、矩阵库Eigen、优化库Ceres、李代数Sophus和CSparse库。给出其CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)

project(bundle_adjustment)
set(CMAKE_BUILD_TYPE "Release")
set(CMAKE_CXX_FLAGS "-O3 -std=c++11")

LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

Find_Package(g2o REQUIRED)
Find_Package(Eigen3 REQUIRED)
Find_Package(Ceres REQUIRED)
Find_Package(Sophus REQUIRED)
Find_Package(CSparse REQUIRED)

SET(G2O_LIBS g2o_csparse_extension g2o_stuff g2o_core cxsparse)

include_directories(${PROJECT_SOURCE_DIR} ${EIGEN3_INCLUDE_DIR} ${CSPARSE_INCLUDE_DIR})

add_library(bal_common common.cpp)
add_executable(bundle_adjustment_g2o bundle_adjustment_g2o.cpp)
add_executable(bundle_adjustment_ceres bundle_adjustment_ceres.cpp)

target_link_libraries(bundle_adjustment_ceres ${CERES_LIBRARIES} bal_common)
target_link_libraries(bundle_adjustment_g2o ${G2O_LIBS} bal_common)

1.基础配置

(1)指定CMake需求版本:cmake_minimum_required(VERSION 2.8)
(2)指定工程名称:project(bundle_adjustment)
(3)指定代码模式:发布版本还是调试版本:set(CMAKE_BUILD_TYPE “Release”)
注意,指定为Release版本之后,程序运行速度会比Debug版本大大加快,但是我们使用编译器build之后,是无法进行断点调试的。(社区作者们似乎更喜欢叫“build”而非“compile”)

2.寻找并链接所需要的包

Find_Package(g2o REQUIRED)
Find_Package(Eigen3 REQUIRED)
Find_Package(Ceres REQUIRED)
Find_Package(Sophus REQUIRED)
Find_Package(CSparse REQUIRED)
include_directories(${PROJECT_SOURCE_DIR} ${EIGEN3_INCLUDE_DIR} ${CSPARSE_INCLUDE_DIR})

(1)Find_package(库名,REQUIRED)是一个很好的命令,它避免了我们手动去寻找这些库的具体分类和位置(事实上有些库非常庞大,不交给计算机自己找的话自己配置成功的概率几乎为0,除非你是这个库的作者)。读者可能会感到困惑,计算机为什么可以这么找呢?这是因为Linux系统(例如ubuntu)可以将所有编译好的库安装到机器中一个固定的地方,例如/usr/local/include,但是我们需要手动安装(会在第三部分讲到),这样就可以找到了。回忆我们在Windows下的配置,也是倡导把自己所使用的库全部放在一个文件夹里(例如3rdParty)。
(2)include_directories() 类似于VS中配置“附加库目录”(可能不太正确),就是将库的目录和当前工程目录连接起来,当然有的时候也会省略 ${PROJECT_SOURCE_DIR} ,重点是将包含这些库的文件夹连接起来。我们看到各个库的格式为“name_INCLUDE_DIR”,至于为什么能够找到,和(1)原因相同。

3.指定要编译的库和可执行文件

终于要进入编译环节了,我们需要指定哪些文件编译为库,哪些文件编译为可执行文件:

add_library(bal_common common.cpp)
add_executable(bundle_adjustment_g2o bundle_adjustment_g2o.cpp)
add_executable(bundle_adjustment_ceres bundle_adjustment_ceres.cpp)

这里尤其要说明的一点是,为什么我们需要将common.cpp编译成库。这是因为,我们希望在程序中通过只包含“common.h”来实现调用。本程序还使用了好几个头文件,但是这些头文件都是把声明和实现写到一起的,所以在程序中include即可,不需要再编译成库。

4.指定库和可执行文件的关系

最后就是把可执行程序名、库都链接上了(link)。

target_link_libraries(bundle_adjustment_ceres ${CERES_LIBRARIES} bal_common)
target_link_libraries(bundle_adjustment_g2o ${G2O_LIBS} bal_common)

我们希望构建的是两个工程,分别使用了ceres库g2o库。读者可能会问其他的库在哪里,都被编译进了common.cpp里,这也是作者很高明的地方。这是隐式的,另外的库都是这些程序的附加依赖项。

5.执行程序

我们进入build文件夹,只需要文件名即可执行:

./bundle_adjustment_ceres 
./bundle_adjustment_g2o

三、如何编译并使用他人的程序

1.阅读README,寻找并安装依赖项

回顾(二)中的问题,如何编译他人的程序呢?一般而言,我们在git上下载下来的,如果支持CMake工程的话,基本都是可以直接编译的。但是有一条非常重要,就是依赖项。这也造成了为什么在Windows下的编译从入门到放弃的原因:如果这些依赖项本身就是在Linux下写成的,也只能通过Linux的命令安装,那么这个库就无法CMake成功。
——————————
以g2o库(一个非常流行的图优化库为例),他的依赖项在README.md里:
在这里插入图片描述
可以看到,他的基础依赖是cmake和Eigen,非常的亲民,并且给出了解决方法。并且下面的依赖也给出了解析包的名称。我们只需要调用sudo apt-get install 即可(这个算是初学者最常使用的linux命令了,正是由于如此方便的安装,节约了我们大量的时间,不必像windows中那样到处寻找可以使用的安装包)。

2.执行编译即可

接下来,我们只需要在当前文件夹下,新建一个build文件夹,cmake即可。电脑会将程序之间的关系根据CMakeList.txt构建完毕,生成Makefile文件,而后直接使用make命令即可进入编译环节。像opencv库,需要编译一个上午,可以使用make -j4等命令开始多线程编译。编译完成之后,为了让电脑能够发现他,我们需要安装:make install,这样这些编译好的文件就可以被复制进我们的/usr/include文件夹了。顺便说一句,我们在编程时,最前面的#include,就是从这个文件夹起步的(读者在编程时如果能惊喜看到的代码补全,就说明安装成功了)。

mkdir build
cd build
cmake ..
make
sudo make install

总结

本文总结了比较初级的编译和运行程序基本用法,希望能帮助大家少走弯路。本文会持续维护,但一般来说,网上的库cmake一下,自己的就能用了,对初学者已经足够,可能我们会花更多的时间在如何使用库并写出高质量的代码上。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值