如何用 Makefile 反向生成 CMakeLists 文件

如何用 Makefile 反向生成 CMakeLists 文件,第1张

CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性。只是 CMake 的组态档取名为 CmakeLists.txt。Cmake 并不直接建构出最终的软件,而是产生标准的建构档(如 linux 的 Makefile 或 Windows Visual C++ 的 projects/workspaces),然后再依一般的建构方式使用。

在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:

编写 CmakeLists.txt。

执行命令 “cmake PATH” 或者 “ccmake PATH” 生成 Makefile ( PATH 是 CMakeLists.txt 所在的目录 )。

使用 make 命令进行编译

工程实例:

一. 编写各层CMakeLists.txt

主目录的主程序main.cpp

#include "hello.h"

extern Hello hello

int main()

{

hello.Print()

return 0

}

主目录的CMakeLists.txt

# to the root binary directory of the project as ${MAIN_BINARY_DIR}.

project (MAIN)

#version support

cmake_minimum_required(VERSION 2.8)

# Recurse into the "Hello" and "Demo" subdirectories. This does not actually

# cause another cmake executable to run. The same process will walk through

# the project's entire directory structure.

add_subdirectory (Hello)

add_subdirectory (Demo)

# Make sure the compiler can find include files from our Hello library.

include_directories (${MAIN_SOURCE_DIR}/Hello)

# Make sure the linker can find the Hello Demo library once it is built.

link_directories (${HELLO_BINARY_DIR}/Hello)

link_directories (${HELLO_BINARY_DIR}/Demo)

#define the source coedes of current directory as DIR_SRCS

AUX_SOURCE_DIRECTORY(. DIR_SRCS)

# Add executable called "MAIN" that is built from the source files

add_executable (Main ${DIR_SRCS})

# Link the executable to the Hello Demo library.

target_link_libraries (Main Hello Demo)

定义项目名project(MAIN),使得当前目录可以用${MAIN_SOURCE_DIR},由于有2个子目录,所以需要add_subdirectory它们。由于主程序会使用到其他库,因而也需要指定连接库所在目录。

主目录下的作用是利用add_executable将当前目录下的源文件编译成Main程序,然后通过target_link_libraries链接Hello和Demo库。由于主程序文件使用了hello.h文件,所以要include_directories该目录。

---------------------------------------------------------------------------------------------------

子目录Demo的子程序demo.c

#include "hello.h"

Hello hello

子目录Demo的CMakeLists.txt

# Make sure the compiler can find include files from our Hello library.

include_directories (${MAIN_SOURCE_DIR}/Hello)

#define the source coedes of current directory as DIR_DEMO_SRCS

AUX_SOURCE_DIRECTORY(. DIR_DEMO_SRCS)

# Add library called "Demo" that is built from the source files

add_library (Demo ${DIR_DEMO_SRCS})

Demo目录下的CMakeLists主要作用是利用add_library将当前目录源码编译成Demo库,由于该库使用到hello.h文件,所以要include_directories该目录。

---------------------------------------------------------------------------------------------------

子目录Hello的子程序hello.h

#ifndef _hello_h

#define _hello_h

class Hello

{

public:

void Print()

}

#endif

子目录Hello的子程序hello.c

#include "hello.h"

#include <stdio.h>

void Hello::Print()

{

printf("Hello, World!\n")

}

子目录Hello的CMakeLists.txt

#define the source coedes of current directory as DIR_HELLO_SRCS

AUX_SOURCE_DIRECTORY(. DIR_HELLO_SRCS)

# Add library called "hello" that is built from the source files

add_library (Hello ${DIR_HELLO_SRCS})

Hello目录下的CMakeLists主要作用是利用add_library将当前目录源码编译成Hello库。

---------------------------------------------------------------------------------------------------

二. 执行cmake命令

至此我们完成了项目中所有 CMakeLists.txt 文件的编写,进入目录 step2 中依次执行命令

#cmake .

默认当前目录,生产makefile

#make

最后编译程序

运行可执行文件hello

./hello

移除文件 rm hello

编译文件得到可执行文件的同时,保留产生的中间文件

g++ -save-temps hello_world.cpp -o hello

单个文件编译过程:

实际的编译过程:预处理,对应临时文件hello_world.ii

g++ -E hello_world.cpp -o preprocessed.ii

cat preprocessed.ii

预处理的工作主要包含去掉注释,然后将我们include的库tack上,以上过程使我们自己主动调用预处理器的过程

cat hello_world.ii

则展示了我们在第5)中编译时保留的中间文件,两者是一样的

实际的编译过程:这是真正的编译过程compilation step,对应临时文件hello_world.s(assembly code)

我们自己把.ii文件进行assembly code得到.s的方法。

g++ -S preprocessed.ii -o complied.s

.s文件是高级语言和机器语言之间的中间环节

实际的编译过程:Assembly,将.s对应的assembly code转换成.o对应的机器语言的过程,也叫machine-readable code或者object code

让编译器将.s文件assembly起来

g++ -c complied.s -o assembled.o

实际的编译过程:最后一步Linking,产生最终的可执行文件。

"Undefined reference" errors are pretty much always linking errors, and you will probably have them. Remember this.

我们通过连接一堆.o文件,得到.exe文件的过程,命令:

g++ assembled.o -o hello_manual

多个文件编译过程:

举例,如果我们有定义一个class,然后我们的文件中包含dog.hpp,dog.cpp和main.cpp三个文件,然后我们只使用以下两个命令:

g++ -c main.cpp -o main.o

g++ main.o dog_program

的话就会出错,告诉我们undefined reference of dog::bark()

因为对于不同的.cpp文件,都要生成一个object file,也就是.o文件。所以如果我们用以下命令:

g++ -c main.cpp -o main.o

g++ -c dog.cpp

g++ dog.o main.o -o dog_program

的话,就不会出错。

我们如果修改main.cpp中的内容的话,我们只需要重新用最后一个连接命令。但是,如果我们修改了dog class本身的内容的话,例如添加一个函数,我们就需要重新产生object file of dog.cpp,然后重新连接。

关于Make的介绍

用自己常用的记事本创建一个Makefile,并注意大小写。在program对应的目录下面。

gedit Makefile

Makefile里面语句的书写规则是

Target: tgt_dependency1 tgt_dependency2 ……

Command

所以dog.o和main.o对应的语句分别是:

dog.o: dog.hpp dog.cpp

g++ -c dog.cpp

main.o: main.cpp

g++ -c main.cpp

在Makefile中Tab是很重要的,所以不要忘记在command对应的第二行加Tab

Makefile的编译顺序

如果Makefile中有如下语句

animal_assembly : moose goose cat

command

moose : antlers hooves fur

command

goose : beak wings webbed_feet interest_in_bread

command

cat : whiskers evil_personality

command

我们可以看到animal_assembly的dependency是 moose goose cat。如果文件夹中存在moose goose cat的话,make命令只需要执行第一句就可以了。如果文件夹中缺少moose goose cat中的一个或者几个,make命令执行的时候,需要先找到moose goose cat的生成方法,然后执行对应的命令,最后执行animal_assembly生成的命令。

moose : antlers hooves fur

command

animal_assembly : moose goose cat

command

goose : beak wings webbed_feet interest_in_bread

command

cat : whiskers evil_personality

command

如果Makefille是以上形式的话,我们只运行make的话,Makefile会只执行第一句,因为第一句的dependency都存在了,所以只把moose生成的命令执行完就好了。如果我们要生成animal_assembly,就要运行命令make animal_assembly。所以,我们需要把最重要的命令,我们最重要生成的object file对应的命令放在第一行。

所以我们的dog_program的完整的Makefile文件应该是:

dog_program: dog.o main.o

g++ dog.o main.o -o dog_program

dog.o: dog.hpp dog.cpp

g++ -c dog.cpp

main.o: main.cpp

g++ -c main.cpp

在Makefile的最后写clean语句。

clean:

rm dog_program *.o

然后我们在命令窗口运行make clean的话,就会删除文件夹中生成的可执行文件,和所有过程中产生的副产品。

对于Makefile中的dependency list,我们需要将每个object file的dependency list都写好。因为make不负责检查文件中的具体的语句,只负责执行Makefile中的语句。

dog_program:

g++ dog.o main.o -o dog_program

dog.o: dog.hpp dog.cpp

g++ -c dog.cpp

main.o: main.cpp

g++ -c main.cpp

如果像上面所示去掉dog_program的dependency list的话,运行make就会出错,因为main是依赖于dog.cpp的。

如果文件中本身就存在dog.o和main.o的话,运行make不会出错,因为make就是先check dog.o main.o是否存在,存在的话就直接运行。

所以,我们如果修改了main.cpp或者dog.cpp的话,我们需要重新生成dog.o和main.o。因为make是不管这个问题的。

make这个命令的功能就是执行Makefile中我们要求执行的语句,对结果没有任何的预期,也不会检查命令有没有问题。所以,我们必须遵守Makefile书写中的一些规则。

all : fill_file_with_nonsense

echo "I have mostly created a lot of junk today!"

fill_file_with_nonsense : create_file

echo "Hello, there is nothing important here" >silly_file

create_file :

touch silly_file touch是Unix中的典型命令,用于生成空的文件

move_file :

mv silly_file silly_file_new_name

delete_file :

rm _file

open_file :

gedit another_silly_file

clean :

touch junk1 junk2 junk3 junk4 junk5

really_clean :

rm junk*

如果想体验的更加清楚,就可以运行这个文件中的内容,然后就知道make完全不会管结果是什么,只是没有脑子的执行命令。

解释上面的内容:

Makefile的书写规则。all: 放在第一句,把所以我们要生成executable依赖的Targets列出来,这样我们需要的所有的文件都可以生成。我们看到all对应的dependency是file_file_with_nonsense,就去找file_file_with_nonsense的生成语句,发现它的dependency是create_file,然后去找create_file的生成语句,就到touch silly_file,touch是Unix中的典型命令,用于生成空的文件。create_file的语句执行完之后,回到file_file_with_nonsense,执行echo "Hello, there is nothing important here" >silly_file,就把"Hello, there is nothing important here" 写入silly_file中,file_file_with_nonsense的语句也执行完之后,我们就返回到all,然后在命令行输出"I have mostly created a lot of junk today!"。

因为其他的target,不在all的dependency list中,也不在all的dependency的dependency当中,所以只能通过make target的形式来调用对应的命令。

Marvelous macros(宏)

一个宏的示例,宏就是Makefile中的variables,用来定义我们需要的 *** 作,一些变量之类的

CXX = clang++

FLAGS = -O

hello : hello_world.cpp

$(CXX) $(FLAGS) $? -o $@

clean :

rm hello

CXX,这是一个预定义的宏,default value是g++。这里把CXX定义成clang++了。

FLAGS,这里定义的是-O。FLAGS也不一定非得定义成-o,也可以是some moose have large antlers,但是这样定义的话,就会导致调用的时候出错。

对于上面的文件,我们在命令行输入make的时候,实际运行的是clang++ -O hello_world.cpp -o hello。

如果我们把CXX=clang++这一行删掉的话,在命令行输入make,实际运行的就是g++ -O hello_world.cpp -o hello。

定义好macro宏,我们使用的时候,就要用$(MACRO)这样的形式,这是makefile语言的一种语法。我们注意到MACRO全部用的大写,虽然不是明确规定的,但是通常情况下用大写。

$?和$@是makefile language里面特别的预定义的宏。$?是指的"names of the dependencies(newer than the target)",$@是指的"name of the target"。

Complier and liner flags in CS 225

CXX = clang++ LD = clang++

CXXFLAGS = -std=c++1y -stdlib=libc++ -c -g -O0 -Wall -Wextra -Werror -pedantic

LDFLAGS = -std=c++1y -stdlib=libc++ -lpng -lc++abi

写法正确,只是你的LD_LIBRARY_PATH设置不对,找不到连接库的路径

export $LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib:/usr/local/lib

类似这样写


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/bake/11495809.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-16
下一篇 2023-05-16

发表评论

登录后才能评论

评论列表(0条)

保存