购物网站开发的管理可行性,襄阳住房和城乡建设局网站首页,做网站赚钱不,网站开发公司规章制度在Linux中开发C
本文档为本人在学习慕课网课程——[重学C #xff0c;重构你的C知识体系]时的一些记录与思考#xff0c;侵删。学习课程请支持正版#xff01;
1. 搭建C/C编译环境
1.1 gcc 和 g 的区别
本质上没有太大区别#xff0c;gcc 默认使用 c 编译器#xf…在Linux中开发C
本文档为本人在学习慕课网课程——[重学C 重构你的C知识体系]时的一些记录与思考侵删。学习课程请支持正版
1. 搭建C/C编译环境
1.1 gcc 和 g 的区别
本质上没有太大区别gcc 默认使用 c 编译器g 默认使用C 编译器
如果是 .c 文件gcc 会使用 c 编译器来编译但 g 会使用 c 编译器但如果是 .cpp 文件两者是一样的。
1.2 一个C/C 小例子
具体代码如下
#includeiostream
using namespace std;
int main(int argc,char** argv){coutHello Ubnutuendl;return 0;
} 可通过以下指令编译
g helloworld.cc -o helloworld
即可生成名为 helloworld 的可执行程序其中 -o 表示我们指定生成的 可执行文件名称
但如果我们需要用 gcc 来编译其会报如下错误
/usr/bin/ld: /tmp/ccO1YpSf.o: in function main:
hello.cc:(.text0x1d): undefined reference to std::cout
/usr/bin/ld: hello.cc:(.text0x22): undefined reference to std::basic_ostreamchar, std::char_traitschar std::operator std::char_traitschar (std::basic_ostreamchar, std::char_traitschar , char const*)
/usr/bin/ld: hello.cc:(.text0x2c): undefined reference to std::basic_ostreamchar, std::char_traitschar std::endlchar, std::char_traitschar (std::basic_ostreamchar, std::char_traitschar )
/usr/bin/ld: hello.cc:(.text0x37): undefined reference to std::ostream::operator(std::ostream (*)(std::ostream))
/usr/bin/ld: /tmp/ccO1YpSf.o: in function __static_initialization_and_destruction_0(int, int):
hello.cc:(.text0x6b): undefined reference to std::ios_base::Init::Init()
/usr/bin/ld: hello.cc:(.text0x80): undefined reference to std::ios_base::Init::~Init()
collect2: error: ld returned 1 exit status 很显然它没有连接到 std 库所以例如 std::cout 等指令都没有找到。我们可以改为用如下指令编译
gcc hello.cc -o helloworld -lstdc
我们发现没有问题了这是因为 gcc 可以进行 C 文件的预处理编译汇编但不会主动连接 iostream等 C 库而如果我们手动指定需要连接 -lstdc它就会去主动连接该库
参考文档[C OpenCV常见链接error及解决方案/usr/bin/ld:][https://blog.csdn.net/qq_45983373/article/details/136361499] 中有一幅挺好的图呈现了C编译的过程 除此之外我们还可以尝试让他显示警告信息例如对于一下代码
#includeiostream
using namespace std;
int main(int argc,char** argv){coutHello Ubnutuendl;int a0;return 0;
} 如果我们使用以下指令进行编译
gcc hello.cc -o helloworld -lstdc -Wall
我们会发现它就会有如下提示信息
hello.cc: In function ‘int main(int, char**)’:
hello.cc:5:6: warning: unused variable ‘a’ [-Wunused-variable]5 | int a0;| ^ 也就是说我们使用 -Wall 选项就可以让他输出一些可以优化的提示信息。
1.3 更多编译指令
除此之外还有如下选项
-ansi 只支持 ANSI 标准的 C 语法。这一选项将禁止 GNU C 的某些特色例如 asm 或 typeof 关键字。-S 知己或预处理和编译就是把文件编译成为汇编代码(.s 文件)-c 质变仪并生成目标文件即把文件编译为二进制文件(.o 文件)-g 生成调试信息GNU 调试器可以利用该信息。-o FILE 生成指定的输出文件用在生成可执行文件时。-O0 不进行优化处理-O 或 -O1 优化生成代码-O2 或 -O3 进一步优化-shared 生成共享目标文件。通常用在建立共享库时。-static 禁止使用共享链接。-w 不生成任何警告信息-Wall 生成所有警告信息-IDIRECTORY 指定额外的头文件搜索路径 DICTIONARY-LDIRECTORY 指定额外的函数库搜索路径 DICTIONARY-ILIBRARY 连接时搜索指定的函数库 LIBRARY-m486 针对 486 进行代码优化。-E 只运行 C 预编译器即把头文件展开等生成预编译文件(.i 文件)
最终编译过程描述为图 2. Makefile 文件的编写
2.1 Makefile 简单介绍
makefile 主要在工程实践中帮助我们完成C工程配置问题。make 字面意思就是制作文件制作一个当前平台可以运行的文件像我们之前使用 g / gcc 生成可执行文件就是一个简单的 makefile 的过程。
可执行程序产生过程
配置环境系统环境确定标准库和头文件位置确定依赖关系源代码之间编译的依赖关系头文件预编译预处理编译链接安装和操作系统建立联系生成安装包
在大型工程中有很多头文件和 .cpp 文件它们在编译的时候会存在以来关系同时很多头文件会被很多文件 include我们希望只被编译一次汇编可转化为机器代码然后通过链接引入目标库等。
当依赖关系复杂的时候make 命令工具诞生了而 Makefile 文件正式为 make 工具所使用的。Makefile 描述了整个工程所有文件的编译顺序、编译规则属于可执行程序生成过程中很重要的部分帮助我们编译器更好的生成可执行代码。
2.2 多个文件编译简单示例
写一个例子在reply.h 中定义 Reply 类
#includeiostream
class Reply{public:Reply();~Reply();void printHello();
}; 然后再 reply.cc 中具体定义这三个方法
#includereply.h
using namespace std;
Reply::Reply(){}
Reply::~Reply(){}
void Reply::printHello(){coutHelloworld!endl;
} 最后我们使用 main 函数调用 Reply
#includereply.h
int main(){Reply reply;reply.printHello();return 0;
} 如果我们按照老方法来编译 使用 gcc main.cc -o main 报错如下 /usr/bin/ld: /tmp/cc4EXok8.o: in function main:
main.cc:(.text0x24): undefined reference to Reply::Reply()
/usr/bin/ld: main.cc:(.text0x30): undefined reference to Reply::printHello()
/usr/bin/ld: main.cc:(.text0x41): undefined reference to Reply::~Reply()
/usr/bin/ld: main.cc:(.text0x67): undefined reference to Reply::~Reply()
/usr/bin/ld: /tmp/cc4EXok8.o: in function __static_initialization_and_destruction_0(int, int):
main.cc:(.text0xab): undefined reference to std::ios_base::Init::Init()
/usr/bin/ld: main.cc:(.text0xc0): undefined reference to std::ios_base::Init::~Init()
/usr/bin/ld: /tmp/cc4EXok8.o:(.data.rel.local.DW.ref.__gxx_personality_v0[DW.ref.__gxx_personality_v0]0x0): undefined reference to __gxx_personality_v0
collect2: error: ld returned 1 exit status使用 gcc mian.cc -o mian -lstdc 报错如下 /usr/bin/ld: /tmp/cchzdnO3.o: in function main:
main.cc:(.text0x24): undefined reference to Reply::Reply()
/usr/bin/ld: main.cc:(.text0x30): undefined reference to Reply::printHello()
/usr/bin/ld: main.cc:(.text0x41): undefined reference to Reply::~Reply()
/usr/bin/ld: main.cc:(.text0x67): undefined reference to Reply::~Reply()
collect2: error: ld returned 1 exit status 我们发现按照原来的方式来编译是不能成功的但如果我们使用这一句话编译
使用gcc reply.cc main.cc -o main -lstdc
是可以成功编译的调用 main 也可以成功输出结果
那我们尝试使用 make 命令来生成一次内容如下
main: reply.o main.o //main 这个文件依赖于 reply.o 和 main.o 两个文件
gcc reply.o main.o -o main -lstdc //生成 main 的指令
reply.o: reply.cc //上一行是对下一行有依赖关系的
gcc -c reply.cc -o reply.o -lstdc
main.o: main.cc
gcc -c mian.cc -o main.o -lstdc 然后我们直接执行一个 make 指令然后就可以一样的生成 main 可执行文件。
2.3 make 和 Makefile 的调用
make究竟是什么
make 是操作系统中的一个批处理工具它可以帮我们把很多命令融合在一起一次性把这些命令执行下去即可以一次性完成很多命令。
可以这样一个比喻make 是一个指挥家而 Makefile 则是乐谱指挥着所有工具完成任务但为什么 windows 不用那么麻烦呢
因为 Linux 是相对开放的系统很多东西需要自己来搭积木但 windows 是商业化的它的用户性是做的比较好的在 IDE 中很多 maker 的事都帮我们做完了因此我们需要了解更多细节。
当然在Linux 中也有其他 IDE 工具例如 CMake它就是帮助我们编写 Makefile 的CMake 我们只需要编写一个 CMakeList.txt它内部就会帮助我们转换为 Makefile 文件
还有 QT 中的 QMake也是实现类似的功能
2.4 Makefile 的格式
基本语法原则
target: prerequisites ...command ... 注意每个命令行前面必须是一个 Tab 字符即命令行第一个字符是 Tab。
简化规则
变量定义 varstring
变量使用 $(var) 此时 Makefile 可以写为
TARGET main
OBJS reply.o main.o
$(TARGET):$(OBJS)gcc $(OBJS) -o $(TARGET) -lstdc
reply.o: reply.cc
main.o: main.cc 此时生成指令如下
g -c -o reply.o reply.cc
g -c -o main.o main.cc
gcc reply.o main.o -o main -lstdc 显然生成 main.o 和 reply.o 的生成都是自动生成的调用的是 g。
如果我们希望若已存在则删除则使用如下指令
TARGET main
OBJS reply.o main.o
$(TARGET):$(OBJS)gcc $(OBJS) -o $(TARGET) -lstdc
reply.o: reply.cc
main.o: main.ccclean:rm $(TARGET) $(OBJS) 此时我们可以调用 make clean即可删除掉这些文件
rm main reply.o main.o 但值得注意的是这里的 clean 实际上是我们生成的 Target只不过我们生成的指令是 rm 指令而已我们可以再优化一下
TARGET main
OBJS reply.o main.o.PHONY: clean$(TARGET):$(OBJS)gcc $(OBJS) -o $(TARGET) -lstdc
reply.o: reply.cc
main.o: main.ccclean:rm $(TARGET) $(OBJS) 这里的 .PHONY 是一个关键字代表这个文件并不是实体存在的因此不会受到已存在 clean 文件的影响
2.5 用 Makefile 实现程序安装卸载
如何安装卸载
TARGET main
OBJS reply.o main.o.PHONY: clean$(TARGET):$(OBJS)gcc $(OBJS) -o $(TARGET) -lstdc
reply.o: reply.cc
main.o: main.ccclean:rm $(TARGET) $(OBJS)install:cp ./main /usr/local/bin/mainTest
uninstall:rm /usr/local/bin/mainTest 这样我们就可以通过 make install 把我们的程序安装到系统中了可以在任意地方通过 mainTest 指令调用而同样的可以通过 make uninstall 来卸载。
注意我们实际上就是把当前可执行文件放到了当前 $PATH 环境变量中所以我们直接在控制台输入即可访问当前我的机器的环境变量
/home/xuzhenge/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin 在大型工程中可能还需要依赖一些第三方库还需要把一些信息写到注册表中
2.6 Makefile 的变量问题
在Makefile 中有以下几种变量 用户自定义变量在 Makefile 中自定义变量很想C中的宏但会有一些特殊的符号表示一些特殊的含义后续会提到且注意大小写是敏感的 变量中的变量 在 Makefile 中支持如下定义方式 foo $(bar)
bar $(ugh)
ugh Huh 也就是我们可以把变量的真实值往后放把真实的变量延迟到后面会非常灵活此时的 bar 就是变量中的变量 当我们使用以下句子时 test1:echo $(foo), foo
test2:echo $(bar), bar 当我们调用 make test1 时输出语句为 Huh, foo 也就是前面会输出 foo 变量的最终值后面会将 foo 作为字符串处理输出 foo 字符。 这种方法好处是很灵活但坏处时会让变量的编写过程变得非常繁杂。 我们看另一段代码 foo $(bar)
bar $(ugh)
ugh Huhtest1:echo $(foo), foo
test2:echo $(bar), bary: $(x)bar
z$(x)bar
x:foo
test3:echo $(x),xecho $(y),yecho $(z),z 在这一段代码中最终输出为 foo,x
bar,y
foobar,z 在这里我们可以看到这里 y 通过 : 赋值但没有获取到 x 的真实值这里的 y其实是一个空值但 z 通过 赋值这里就获取到了 x 的目标值。 这里 : 实际上就是避免依赖后面定义的变量的赋值方法 再进一步 foo $(bar)
bar $(ugh)
ugh Huhtest1:echo $(foo), foo
test2:echo $(bar), bary: $(x)bar
z$(x)bar
x:foo
xxzg
test3:echo $(x),xecho $(y),yecho $(z),z 其输出为 foo xzg,x
bar,y
foo xzgbar,z 可以看出来x 在 foo 的基础上还追加了 xzg 字符 多行变量 以下代码 foo $(bar)
bar $(ugh)
ugh Huhtest1:echo $(foo), foo
test2:echo $(bar), bary: $(x)bar
z$(x)bar
x:foo
xxzg
test3:echo $(x),xecho $(y),yecho $(z),z
define two-lines
foo
echo $(bar)
endef
test4:echo $(two-lines) 其中执行 make test4 结果如下 foo
Huh 即对于一个变量会同时打出两行第一行即字符 foo第二行即变量 bar 的最终值即 Huh。这就是多行变量。 环境变量 类似于 PATH 这种变量例如常见的环境变量 $PATH$LANG 等 C 的环境路径为 $LD_LIBRARY_PATH即动态和静态库的搜索路径 对环境变量赋值可以通过 export 赋值调用环境变量 testEnv:echo $(HOME),$(SHELL),$(LD_LIBRARY_PATH)输出结果为 /home/xuzhenge,/bin/sh,同样的编译器也有自己的环境变量 testMakefileEnv:echo $(CXX), $(RM)其输出结果如下 g, rm -f2.7 实现共享库
在 windows 平台中共享库一般是 .lib 或 .dll 文件在 Linux 平台中共享库一般是 .so 文件。这里我们就尝试把刚刚写的 reply.o 文件做成一个共享库 reply.so
代码如下
TARGET main
OBJS reply.o
LIB libreply.so
CXXFLAGS -c -fPIC.PHONY: clean install uninstall$(TARGET):$(LIB) main.o$(CXX) main.o -o $(TARGET) -L. -lreply
$(LIB):$(OBJS)$(CXX) -shared $(OBJS) -o $(LIB)
reply.o: reply.cc$(CXX) $(CXXFLAGS) reply.cc -o $(OBJS)
main.o: main.cc$(CXX) $(CXXFLAGS) main.cc -o main.oclean:rm $(TARGET) $(OBJS)install:cp ./main /usr/local/bin/mainTest
uninstall:rm /usr/local/bin/mainTest 首先我们定义需要生成的文件为 LIB libreply.so即一个动态链接库。同时我们需要使用一个 CXXFLAGS 变量来保存 C 的编译指令。这里包括两个编译选项生成目标文件 (-c) 和 生成共享动态链接库 (-fPIC)。
动态链接库可以只生成一份备份多份代码可共享一份代码这一份二进制程序可以被其他二进制程序所共享
这里生成过程其实还挺复杂的其中生成 main 的语句为
g main.o -o main -L. -lreply 其中这里的 -L. 表示从本地寻找动态链接库-lreply 表示寻找名为 libreply.so 的动态链接库。
生成 main.o 的语句为
g -c -fPIC main.cc -o main.o -fPIC选项应用于静态库编译时编译器会生成额外的位置无关代码和数据布局以确保生成的静态库可以在不同的内存地址上加载。
生成 libreply.so 的语句如下
g -shared reply.o -o libreply.so 这里 -shared 表示生成动态链接库它是通过 .o 文件生成的
生成 reply.o 文件的语句如下
g -c -fPIC reply.cc -o reply.o 这个语句就没啥好说的了。
但如果直接这样生成运行 ./main 会出现以下报错
./main: error while loading shared libraries: libreply.so: cannot open shared object file: No such file or directory 即我们无法打开共享链接库说明还有一些参数有问题我们继续修改生成 TARGET 的指令为
g main.o -o main -L. -lreply -Wl,-rpath ./ 这里的 -Wl 表示编译器将后面的参数传递个连接器 ld-rpath 选项添加了一个链接库的定位路径在运行连接时会优先搜索 -rpath 的路径再去搜索 LD_RUN_PATH 的路径。
如果不加 -fPIC就会有如下报错
/usr/bin/ld: reply.o: relocation R_X86_64_PC32 against symbol _ZSt4coutGLIBCXX_3.4 can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: bad value 其中可以顺利产生 reply.o但不能产生动态链接库 libreply.so说明这个 -fPIC 实际上是维持 .o 到 .so 之间的稳定的
2.8 继续讲变量 自动变量目标变量 把前面的语句写作如下形式 $(LIB):$(OBJS)$(CXX) -shared $^ -o $原本为 $(LIB):$(OBJS)$(CXX) -shared $(OBJS) -o $(LIB)这两个变量 $^ 和 $ 并不是原本就有的而是依据某些规则生成的变量公式如下 因此这里的 $^ 明显就是 $(OBJS) 而 $ 就是 $(LIB) 。注意这里这两个变量是局部变量只有在当前规则中有效。因此当变量名冲突的时候则会优先以局部变量为主 最后我们修改完毕的 Makefile 文件长这样 TARGET main
OBJS reply.o
LIB libreply.so
CXXFLAGS -c -fPIC
LDFLAGS -L. -lreply -Wl,-rpath $(D).PHONY: clean install uninstall$(TARGET):main.o $(LIB)$(CXX) $ -o $(TARGET) $(LDFLAGS)
$(LIB):$(OBJS)$(CXX) -shared $^ -o $
reply.o: reply.cc$(CXX) $(CXXFLAGS) $ -o $
main.o: main.cc$(CXX) $(CXXFLAGS) $ -o $clean:rm $(TARGET) $(OBJS) $(LIB) main.oinstall:cp ./main /usr/local/bin/mainTest
uninstall:rm /usr/local/bin/mainTest模式变量 不用再依赖项中把每个文件都清晰写出来可以通过扩展名的方式找到任意字符为文件名这样的文件因此有了通配符 %。 最终简化下来的代码如下 TARGET main
OBJS reply.o
TESTOBJ main.o
LIB libreply.so
CXXFLAGS -c -fPIC
LDFLAGS -L. -lreply -Wl,-rpath $(D).PHONY: clean install uninstall$(TARGET):$(TESTOBJ) $(LIB)$(CXX) $ -o $(TARGET) $(LDFLAGS)
$(LIB):$(OBJS)$(CXX) -shared $^ -o $
%.o:%.cc$(CXX) $(CXXFLAGS) $ -o $clean:$(RM) $(TARGET) $(OBJS) $(LIB) $(TESTOBJ)install:cp ./main /usr/local/bin/mainTest
uninstall:rm /usr/local/bin/mainTest 这里就是把 .o 和 .cc 的关系通过一句话都编完了
3. Makefile 自动生成与部署
在项目中我们一般有如下文件夹
src: 源代码源程序include: 头文件build: 生成临时文件test: 做测试用例example: 一些 demoLib: 用于保存第三方库的文件夹bin: 可运行文件……
但如果是大型项目直接写 Makefile 文件很累因此我们有自动化工具
automake/autoconfigCMake
这里就演示一下 CMake 的用法
先下载安装包然后用 tar -zxvf 进行解压缩
然后在解压缩后的文件夹中执行 ./bootstrap 命令即可安装。
然后我们就可以编写 CMakeList.txt这个是有固定模板的
#CMakeLists.txt
# 设置 cmake 最低版本
cmake_minimum_required(VERSION 2.8.0)
# 设置C标准
set(CMAKE_CXX_STANDARD 11)
# 项目名称
project(cmake_test)
# 包含的头文件目录
include_directories(./include)
set(SRC_DIR ./src)
# 指定生成链接库
add_library(XXX ${SRC_DIR}/XXX.cc)
add_library(YYY ${SRC_DIR}/YYY.cc)
# 设置变量
set(LIBRARIES XXX YYY)
set(OBJECT XXX_test)
# 生成可执行文件
add_executable(${OBJECT} ${SRC_DIR}/main.cc)
# 为可执行文件链接目标库
target_link_libraries(${OBJECT} ${LIBRARIES}) 生成的 Makefile 文件如下
# CMAKE generated file: DO NOT EDIT!
# Generated by Unix Makefiles Generator, CMake Version 3.16# Default target executed when no arguments are given to make.
default_target: all.PHONY : default_target# Allow only one make -f Makefile2 at a time, but pass parallelism.
.NOTPARALLEL:#
# Special targets provided by cmake.# Disable implicit rules so canonical targets will work.
.SUFFIXES:# Remove some rules from gmake that .SUFFIXES does not remove.
SUFFIXES .SUFFIXES: .hpux_make_needs_suffix_list# Suppress display of executed commands.
$(VERBOSE).SILENT:# A target that is always out of date.
cmake_force:.PHONY : cmake_force#
# Set environment variables for the build.# The shell in which to execute make rules.
SHELL /bin/sh# The CMake executable.
CMAKE_COMMAND /usr/bin/cmake# The command to remove a file.
RM /usr/bin/cmake -E remove -f# Escaping for special characters.
EQUALS # The top-level source directory on which CMake was run.
CMAKE_SOURCE_DIR /home/xuzhenge/Desktop/testCPP/TestMake# The top-level build directory on which CMake was run.
CMAKE_BINARY_DIR /home/xuzhenge/Desktop/testCPP/TestMake/build#
# Targets provided globally by CMake.# Special rule for the target rebuild_cache
rebuild_cache:$(CMAKE_COMMAND) -E cmake_echo_color --switch$(COLOR) --cyan Running CMake to regenerate build system.../usr/bin/cmake -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)
.PHONY : rebuild_cache# Special rule for the target rebuild_cache
rebuild_cache/fast: rebuild_cache.PHONY : rebuild_cache/fast# Special rule for the target edit_cache
edit_cache:$(CMAKE_COMMAND) -E cmake_echo_color --switch$(COLOR) --cyan No interactive CMake dialog available.../usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available.
.PHONY : edit_cache# Special rule for the target edit_cache
edit_cache/fast: edit_cache.PHONY : edit_cache/fast# The main all target
all: cmake_check_build_system$(CMAKE_COMMAND) -E cmake_progress_start /home/xuzhenge/Desktop/testCPP/TestMake/build/CMakeFiles /home/xuzhenge/Desktop/testCPP/TestMake/build/CMakeFiles/progress.marks$(MAKE) -f CMakeFiles/Makefile2 all$(CMAKE_COMMAND) -E cmake_progress_start /home/xuzhenge/Desktop/testCPP/TestMake/build/CMakeFiles 0
.PHONY : all# The main clean target
clean:$(MAKE) -f CMakeFiles/Makefile2 clean
.PHONY : clean# The main clean target
clean/fast: clean.PHONY : clean/fast# Prepare targets for installation.
preinstall: all$(MAKE) -f CMakeFiles/Makefile2 preinstall
.PHONY : preinstall# Prepare targets for installation.
preinstall/fast:$(MAKE) -f CMakeFiles/Makefile2 preinstall
.PHONY : preinstall/fast# clear depends
depend:$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1
.PHONY : depend#
# Target rules for targets named main_test# Build rule for target.
main_test: cmake_check_build_system$(MAKE) -f CMakeFiles/Makefile2 main_test
.PHONY : main_test# fast build rule for target.
main_test/fast:$(MAKE) -f CMakeFiles/main_test.dir/build.make CMakeFiles/main_test.dir/build
.PHONY : main_test/fast#
# Target rules for targets named main# Build rule for target.
main: cmake_check_build_system$(MAKE) -f CMakeFiles/Makefile2 main
.PHONY : main# fast build rule for target.
main/fast:$(MAKE) -f CMakeFiles/main.dir/build.make CMakeFiles/main.dir/build
.PHONY : main/fast#
# Target rules for targets named reply# Build rule for target.
reply: cmake_check_build_system$(MAKE) -f CMakeFiles/Makefile2 reply
.PHONY : reply# fast build rule for target.
reply/fast:$(MAKE) -f CMakeFiles/reply.dir/build.make CMakeFiles/reply.dir/build
.PHONY : reply/fastsrc/main.o: src/main.cc.o.PHONY : src/main.o# target to build an object file
src/main.cc.o:$(MAKE) -f CMakeFiles/main_test.dir/build.make CMakeFiles/main_test.dir/src/main.cc.o$(MAKE) -f CMakeFiles/main.dir/build.make CMakeFiles/main.dir/src/main.cc.o
.PHONY : src/main.cc.osrc/main.i: src/main.cc.i.PHONY : src/main.i# target to preprocess a source file
src/main.cc.i:$(MAKE) -f CMakeFiles/main_test.dir/build.make CMakeFiles/main_test.dir/src/main.cc.i$(MAKE) -f CMakeFiles/main.dir/build.make CMakeFiles/main.dir/src/main.cc.i
.PHONY : src/main.cc.isrc/main.s: src/main.cc.s.PHONY : src/main.s# target to generate assembly for a file
src/main.cc.s:$(MAKE) -f CMakeFiles/main_test.dir/build.make CMakeFiles/main_test.dir/src/main.cc.s$(MAKE) -f CMakeFiles/main.dir/build.make CMakeFiles/main.dir/src/main.cc.s
.PHONY : src/main.cc.ssrc/reply.o: src/reply.cc.o.PHONY : src/reply.o# target to build an object file
src/reply.cc.o:$(MAKE) -f CMakeFiles/reply.dir/build.make CMakeFiles/reply.dir/src/reply.cc.o
.PHONY : src/reply.cc.osrc/reply.i: src/reply.cc.i.PHONY : src/reply.i# target to preprocess a source file
src/reply.cc.i:$(MAKE) -f CMakeFiles/reply.dir/build.make CMakeFiles/reply.dir/src/reply.cc.i
.PHONY : src/reply.cc.isrc/reply.s: src/reply.cc.s.PHONY : src/reply.s# target to generate assembly for a file
src/reply.cc.s:$(MAKE) -f CMakeFiles/reply.dir/build.make CMakeFiles/reply.dir/src/reply.cc.s
.PHONY : src/reply.cc.s# Help Target
help:echo The following are some of the valid targets for this Makefile:echo ... all (the default if no target is provided)echo ... cleanecho ... dependecho ... rebuild_cacheecho ... main_testecho ... mainecho ... edit_cacheecho ... replyecho ... src/main.oecho ... src/main.iecho ... src/main.secho ... src/reply.oecho ... src/reply.iecho ... src/reply.s
.PHONY : help#
# Special targets to cleanup operation of make.# Special rule to run CMake to check the build system integrity.
# No rule that depends on this can have commands that come from listfiles
# because they might be regenerated.
cmake_check_build_system:$(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0
.PHONY : cmake_check_build_system4. 如何调用他人写的库
4.1 通过 -L. 指定库目录编译
我们以 openfec 为例这是一个由 www.openfec.org 提供的用于方便进行 AL-FEC 编解码的方法经过 make 后它会生成一个 libopenfec.so 文件下面我们将要尝试如果在其他 .cc 代码中引入该文件的相关方法应该如何编译
我们编译的目标是 openfec 自带的一个例子具体在 ~/applis/how_to_examples/simple_client_server 下包含头文件 simple_client_server.h 和两个代码文件 simple_server.c 、 simple_client.c 。显然我们需要编译的文件就是后两个代码文件。当前文件结构如下图所示 当然我们看到这个目录下其实也是有 CMakeLists.txt 的说明我们同样可以通过 cmake 的方式生成 Makefile 文件然后使用 make 进行编译。打开 CMakeLists.txt 就可以看到它在里面也声明了 openfec 的库文件target_link_libraries 然而在这里我们偏不用 make 的方式来编译而是直接使用 gcc 来编译体会一下 C 代码编译的过程。显然我们需要声明的库文件即 libopenfec.so那么这个库在哪呢
在 openfec 官网中曾提到可以使用 make 指令来编译 openfec 的源代码编译结果将会放到 ~/bin/Debug 文件夹下这个文件夹的内容如下图所示 可以看到这里的 libopenfec.so 就是我们想要的文件。当然除了这个文件亦以外还有一个 libopenfec.so.1这个文件实际上是运行时的连接文件也就是 -Wl,-rpath 指定的目标文件后续可以通过报错看到他们之间的关系。
首先我们把 libopenfec.so 文件挪到 openfec 提供的例子目录~/applis/how_to_examples/ simple_client_server下此时该目录内容如下 此时调用我们的编译语句
gcc simple_server.c -o simple_server -L. -lopenfec
可以看到代码顺利生成了 simple_server 的可执行文件但是我们实际运行时它会报如下错误 显然它没有找到库 libopenfec.so.1 那么我们把该目录也放到当前文件夹下并通过如下指令进行编译
gcc simple_server.c -o simple_server -L. -lopenfec -Wl,-rpath ./
此时生成的可执行文件不再出错可以正常运行
4.2 放到默认库目录文件夹下编译
在前面的例子中我们通过 -L. 指定了库文件的目录但这似乎不太符合我们一般的使用习惯一般我们指定了 -lopenfec 之后就可以直接编译了在本小节中我们进一步尝试直接把 .so 文件放到默认库目录文件夹下编译
在 C 编译中它会按照以下顺序来找库文件位置 注意这里说的仅仅是 编译过程中的库文件而不包含运行过程中的库文件。在C中编译过程中要求的库文件叫静态库例如上面的 libopenfec.so而在运行过程中要求的库文件叫动态库例如上面的 libopenfec.so.1。在编译时指定库文件静态库通过 -L 指定而动态库则通过 -Wl,-rpath 指定同样的对于环境变量静态库通过 $LIBRARY_PATH 指定而动态库则通过 $LD_LIBRARY_PATH 指定下面我们希望把我们需要的 openfec.so 和 openfec.so.1 放到桌面的目录 ~/Desktop/openFECLib 中然后我们通过环境变量指定这个目录作为库目录之一使我们编译时不再需要额外指定库目录路径
此时~/Desktop/openFECLib 的内容如下 实际上就是把库文件静态库和动态库都放到了该目录下然后就很简单就向环境变量设置该目录即可
export LIBRARY_PATH~/Desktop/openFECLib/
export LD_LIBRARY_PATH~/Desktop/openFECLib/
需要注意的是我这里是直接重写了该变量如果只是希望往里面添加新的路径则通过如下指令
export LIBRARY_PATH~/Desktop/openFECLib/:$LIBRARY_PATH
export LD_LIBRARY_PATH~/Desktop/openFECLib/:$LD_LIBRARY_PATH
此时我们重新对目标文件进行编译不过我们执行如下指令
gcc simple_server.c -o simple_server -lopenfec
很好没有报错直接就编译成功了而且生成的 simple_server 是可以直接运行的
但是这还有一个缺陷我们设置的环境变量是临时变量我们把当前终端关掉了该变量就丢失了… 那么我们有三种选择
每次 gcc 之前先设置 LIBRARY_PATH 和 LD_LIBRARY_PATH 的路径。设置一个永久的变量把库文件放到默认目录中/usr/local/lib就像我们安装应用那样需要sudo。
第一种和第三种方法就不多说了我们试试第二种方法设置全局环境变量首先执行指令
vim ~/.bashrc
打开文件后在末尾处直接添加
export LIBRARY_PATH~/Desktop/openFECLib/
export LD_LIBRARY_PATH~/Desktop/openFECLib/
保存退出后执行 source .bashrc 令其生效即可
这样我们就成功设置了一个持久的变量
本文档完~ 到桌面的目录 ~/Desktop/openFECLib 中然后我们通过环境变量指定这个目录作为库目录之一使我们编译时不再需要额外指定库目录路径
此时~/Desktop/openFECLib 的内容如下
[外链图片转存中…(img-rUhDcfVG-1710836785819)]
实际上就是把库文件静态库和动态库都放到了该目录下然后就很简单就向环境变量设置该目录即可
export LIBRARY_PATH~/Desktop/openFECLib/
export LD_LIBRARY_PATH~/Desktop/openFECLib/
需要注意的是我这里是直接重写了该变量如果只是希望往里面添加新的路径则通过如下指令
export LIBRARY_PATH~/Desktop/openFECLib/:$LIBRARY_PATH
export LD_LIBRARY_PATH~/Desktop/openFECLib/:$LD_LIBRARY_PATH
此时我们重新对目标文件进行编译不过我们执行如下指令
gcc simple_server.c -o simple_server -lopenfec
很好没有报错直接就编译成功了而且生成的 simple_server 是可以直接运行的
但是这还有一个缺陷我们设置的环境变量是临时变量我们把当前终端关掉了该变量就丢失了… 那么我们有三种选择
每次 gcc 之前先设置 LIBRARY_PATH 和 LD_LIBRARY_PATH 的路径。设置一个永久的变量把库文件放到默认目录中/usr/local/lib就像我们安装应用那样需要sudo。
第一种和第三种方法就不多说了我们试试第二种方法设置全局环境变量首先执行指令
vim ~/.bashrc
打开文件后在末尾处直接添加
export LIBRARY_PATH~/Desktop/openFECLib/
export LD_LIBRARY_PATH~/Desktop/openFECLib/
保存退出后执行 source .bashrc 令其生效即可
这样我们就成功设置了一个持久的变量
本文档完~