Makefile

gcc编译过程

-E:预处理、只关联主函数cpp文件,若有其他cpp文件会报错

g++: fatal error: cannot specify -o with -c, -S or -E with multiple files

-S:编译、只关联预处理文件产生的 .ii文件,若有其他.ii文件会报错

-c:汇编成目标代码(.o)只关联编译产生的 .s文件,若有其他.s文件会报错

链接:多文件编译时,需要main cpp文件中头文件对应的cpp文件(一般也将该文件编译成.o文件),若不包含会产生头文件中函数找不到的错误

gcc     -E     源文件     -o     *.ii          预处理/预编译, 使用 c 源程序文件,生成 *.ii 文件,.ii文件就是生成的预处理文件

gcc     -S     *.ii             -o    *.s         编译,使用 *.ii 文件,生成 *.s 文件,.s文件就是生成的汇编文件

gcc     -c     *.s            -o    *.o          汇编,使用 *.s 文件,生成 *.o 文件,.o就是字节码文件

gcc            *.o            -o     *.exe      链接,将 *.o 文件与标准库链接,生成可执行文件exe

注意:一般直接使用 gcc/g++  *.c源程序文件,会自动执行 预编译、编译、汇编操作。

使用GCC直接编译源程序文件(不使用Makefile),并且源程序中包含自定义头文件,使用GCC编译时可通过 -I头文件路径 指定头文件的路径,存在多个头文件路径时使用      ,    分隔,每个头文件路径前都加   -I    即可。

单个头文件:

$ gcc test.c I../inc -o test

包含多个目录: 

$ gcc test.c -I../inc, -I../inc2 -o test

使用GCC编译时有一下选项可用:

gcc -g              生成调试信息。GNU 调试器可利用该信息,不加 -g 不可以调试

gcc -Wall         生成所有警告信息

gcc -w             不生成任何警告信息

gcc -c              只编译并生成目标文件

-shared :指定生成动态链接库。
-fPIC :产生位置无关的代码,当产生共享库的时候(动态库),应该创建位置无关的代码,这会让共享库使用任意的地址而不是固定的地址。
-static :指定生成静态链接库。
-L:指定要连接的库所在的目录。
-l:指定链接时需要的库名(动态/静态),编译器查找连接库时有隐含的命名规则:即在给出的名字前面加上lib,后面加上.a/.so来确定库的名称。
-I:(大写i)指定头文件的文件夹。
-Wall :生成所有警告信息。
-ggdb :此选项将尽可能的生成gdb的可以使用的调试信息。
-g :编译器在编译的时候产生调试信息。
-o:指定执行文件的名字;

 

参考: https://blog.csdn.net/Ethan_Novice/article/details/7192076

            https://blog.csdn.net/liuxiao723846/article/details/97617548

源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。 而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码 (Linker Error),在VC下,这种错误一般是: Link 2001错误 ,意思说是说,链接器未能找到 函数的实现。你需要指定函数的Object File。

将结果保存到指定文件/从指定文件中读取输入

运行编译后的exe文件,可以指定输入从ain.txt中读取,输出结果保存到aout.txt文件中.

xxx.exe <infile> outfile

Makefile文件编写

参考:https://seisman.github.io/how-to-write-makefile/overview.html

注意:

  • all:    clean:    等所有标签行要顶格写

  • 另外所有标签下的命令前必须使用一个Tab键

make是如何工作的

在默认的方式下,也就是我们只输入 make 命令。那么,

  1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。

  2. 如果找到,它会找文件中的第一个目标文件(target),写在第一行 前的标记文件,并把这个文件作为最终的目标文件。

  3. 如果 目标文件 不存在,或是目标文件 后面的 依赖文件1 的修改时间要比  目标文件 这个文件新,那么,他就会执行后面所定义的命令。

  4. 如果 目标文件 的 依赖文件1 也不存在,那么make会在当前文件中找目标为 依赖文件1目标文件2,如果找到则再根据那一个规则生成 对应文件

  5. 如果目标文件和其依赖文件都存在,则就按照规则生成对应的最终 目标文件

在Makefile中,规则的顺序是很重要的,因为,Makefile中应该只有一个最终目标,其它的目标都是被这 个目标所连带出来的,所以一定要让make知道你的最终目标是什么。一般来说,定义在Makefile中的目标 可能会有很多,但是第一条规则中的目标将被确立为最终的目标。如果第一条规则中的目标有很多个,那么 ,第一条规则中的第一个目标会成为最终的目标。make所完成的也就是这个目标。

伪目标

Makefile中的第一个目标会被作为其默认目标,对于伪目标 all,clean等,他们都有一个显式写法就是 .PHONY:clean

但是一般都直接使用 clean:(这是隐式写法,不推荐)。

下面是一个伪目标的实例。

all : prog1 prog2 prog3
.PHONY : all

prog1 : prog1.o utils.o
    cc -o prog1 prog1.o utils.o

prog2 : prog2.o
    cc -o prog2 prog2.o

prog3 : prog3.o sort.o utils.o
    cc -o prog3 prog3.o sort.o utils.o

我们声明了一个“all”的伪目标,all依赖于其它三个目标(prog1 prog2 prog3)。由于默认目标的特性是,总是被执行的,但由于“all”又是一个伪目标,伪目标只是一个标签不会生成文件,所以不会有“all”文件产生。于是,其它三个目标的规则总是会被执行。也就达到了我们一口气生成多个目标的目的

静态模式

<targets ...> : <target-pattern> : <prereq-patterns ...>
    <commands>
    ...

targets定义了一系列的目标文件,可以有通配符。targets是目标的一个集合定义一个/多个目标文件,提前定义好多个文件。

target-parrtern是指明了targets的类型,也就是的targets目标集的类型。目标文件的集合,生成的目标文件是什么后缀/类型。

prereq-parrterns是目标的依赖模式,即生成目标需要依赖的文件类型。生成目标文件的依赖项的类型。

例如:目标文件A :%.o : %.c

意思是 目标文件A 依赖于 后面的文件,其中%.o代表 后缀为.o的目标文件A的模式集合,%.c代表后缀为.c的文件。

%.o : %.c 代表 目标文件A后缀模式是 .o 的集合,生成 .o 文件的依赖文件就是 %.c

生成目标文件时,使用 % 自动替换为同名文件。

使用静态模式时使用前就需要指定 生成目标文件是那个文件

例如下面使用静态模式,首先定义了生成目标是 foo.o和bar.o,在下面说明了目标文件的集合模式是 .o,而生成.o文件所依赖的文件.c文件。

其中 $< 代表第一个依赖文件,生成目标文件的第一依赖文件,$@ 代表目标文件就是 objects

objects = foo.o bar.o

all: $(objects)

$(objects): %.o: %.c
    $(CC) -c $(CFLAGS) $< -o $@
 

使用静态模式实例

# 先指定生成exe目标文件
SRCS = ./src/class.exe ./src/Test.exe
SRCSC = ./src

BIN = ./bin

all : $(SRCS)
# 显式目标写法,伪目标一般建议都用显式写法
.PHONY : all
# $@ 目标文件就是$(SRCS)
$(SRCS) : %.exe: %.cpp
	g++ $< -o $@

# 伪目标
.PHONY : clean
clean :
	-rm -rf $(SRCSC)/*.o
	-rm -rf $(SRCSC)/*.exe

	-rm -rf $(BIN)/*.o
	echo Clean Done

自动生成依赖项文件

gcc/g++ -MM 源程序文件.c

使用GNU的C/C++编译器,你得用 -MM 参数,不然, -M 参数会把一些标准库的头文件也包含进来。

Makefile的综合实例

# 源程序目录
PSRC = ./src

# 静态模式目标文件
SRCS = ./src/class.exe ./src/Test.exe ./src/class.o ./src/Test.o

# exe文件存储目录,一般不需要
BIN = ./bin

# 编译all标签
all : $(SRCS) temp
	echo Complie Begin
# 显式目标写法,伪目标一般建议都用显式写法
.PHONY : all
# $@ 目标文件,一般就是指冒号前的标签,如果标签是一个目标文件则自动替换,
# 若标签是一个伪目标文件,则直接使用标签名为目标文件名
# $< 当前目标文件的第一个依赖文件
# $^ 当前目标文件的所有依赖文件

# 这里的$@就是$(SRCS)
# 使用filter只产生exe目标文件
$(filter %.exe,$(SRCS)) : %.exe: %.cpp
	g++ $< -o $@
	
# 使用filter只产生.o目标文件
$(filter %.o,$(SRCS)) : %.o: %.cpp
	g++ -c $< -o $@

# 不使用静态模式,直接生成目标文件
# 这里的$@就是temp 伪目标,会直接生成temp.exe目标文件
temp: $(PSRC)/*.c
# 编译指定源程序文件,生成目标文件到BIN目录下
	gcc $^ -o $(BIN)/$@
# 编译所有依赖文件时只能有一个main()函数,否则编译无法通过

# 伪目标
.PHONY : clean
clean :
	-rm -rf $(PSRC)/*.o
	-rm -rf $(PSRC)/*.exe
	echo CLean PSRC Done
	-rm -rf $(BIN)/*.*
	echo Clean BIN Done

Make Error 总结 

1.使用make出现下述错误

$ make all
make: Nothing to be done for 'all'.

解决办法:这是因为源程序文件没有修改,之前已经编译过。若要重新编译先使用 make clean命令

2.预处理、编译、汇编是,使用多个文件

g++: fatal error: cannot specify -o with -c, -S or -E with multiple files

 

使用Makefile编译学生管理系统实例

1.Makefile

# 源码Path
PSRC =  ./ManageStuSys.cpp ./ManageLink.cpp
# 依赖头文件目录
INC = ../inc/
# 生成的目标文件,
SRC =  ./ManageStuSys.ii ./ManageStuSys.s  ./ManageStuSys.o ./ManageStuSys.exe  
BIN = ./StuSys

all : $(SRC) temp clean

# 预处理
# 生成.i 预处理,预处理将源码中的 宏展开、头文件复制进来
# $(filter 指定生成的文件, 生成的文件集合),从文件集合中筛选出指定的文件
# %.o: %.i    : 前面是生成的文件,后面是依赖的文件
$(filter %.ii, $(SRC)) : %.ii: %.cpp
	pwd
	echo Preprocessing Begin
	g++ -E $< -o $(BIN)/$@
	echo Preprocessing product .ii


# 编译
# 生成.i
$(filter %.s, $(SRC)) : %.s: $(BIN)/%.ii
	echo Compile Begin
	g++ -S $<  -o $(BIN)/$@
	echo Compile product .s


# 生成.o
$(filter %.o, $(SRC)) : %.o: $(BIN)/%.s
	echo Assembly Begin
	g++ -c $<  -o $(BIN)/$@
	g++ -c ./ManageLink.cpp -o $(BIN)/ManageLink.o
	echo Assembly product .o


# 生成exe
$(filter %.exe, $(SRC)) : %.exe: $(BIN)/*.o 
	echo Link Begin
	g++  $^ -I $(INC) -o $(BIN)/$@
	echo Link product .exe

# 直接生成exe
temp : $(PSRC)
	g++ $(PSRC) -I $(INC) -o $(BIN)/$@
	echo Compile End


clean :
	-rm -rf $(BIN)/*.*
	echo allfile Done


2. ManageStuSys.h

#ifndef _HManageStuSys
#define _HManageStuSys

// define student struct
typedef struct STUNODE
{
	char stuName[20];
	char stuID[20];
	char stuGrade[20];
	char stuClass[20];
	char *stuInfo[4] = {stuName,stuID,stuGrade,stuClass};
	double stuScore;
	STUNODE *next;
}STU;

void* NodeInit();
void printNode(STU *);
void exitPrecent();//显示退出系统百分比
void addStuInfo(STU *, char *[] , double);
void saveStudInfo(STU *);//存储学生信息
void modifyStuSocre(STU *,char [],double);//修改学生信息
void serachStuInfo(STU *);//查询学生信息
void deleteStuInfo(STU *);//删除学生信息	

void getStuQueue(STU *);//将学生排序

#endif

 3.ManageStuSys.cpp

#include <iostream>
#include "../inc/ManageStuSys.h"

using namespace std;
// g++ -g ManageStuSys.cpp ManageLink.cpp -I ../inc -o ms
int main(){
	// static int i = 1;
	char name[20],id[20],Grade[20],Class[20];
	double socer = 0.0;
	STU *head = (STU*)NodeInit();
	STU *NewNode = (STU*)NodeInit();
	NewNode->stuInfo[0] = NewNode->stuName;
	NewNode->stuInfo[1] = NewNode->stuID;
	NewNode->stuInfo[2] = NewNode->stuGrade;
	NewNode->stuInfo[3] = NewNode->stuClass;

	cout <<"*******************欢迎使用GitW学生管理系统****************"<<endl;
	cout <<"*******************  系统版本号:V0.0.1   ****************"<<endl;
	cout <<"*******************    操作指南           ****************"<<endl;

	cout <<"*******************1:添加学生信息*************************"<<endl;
	cout <<"*******************2:修改成绩    *************************"<<endl;
	cout <<"*******************3:查询学生信息*************************"<<endl;
	cout <<"*******************4:查看学生成绩排名**********************"<<endl;
	cout <<"*******************5:删除学生信息*************************"<<endl;


	char MenuNumber;
	while(true){
		cout <<"请输入指令:";
		cin >> MenuNumber;
		switch (MenuNumber)
		{
		case 'q':
			cout <<"*****即将退出系统*****"<< endl;
			exitPrecent();
			free(head);
			free(NewNode);
			exit(0);
		case '1':
			cout <<"*****请添加添加学生信息*****"<< endl;
			cout <<"*****格式:姓名,学号,年级,班级,总成绩*****"<< endl;
			cin >> NewNode->stuName >> NewNode->stuID >> NewNode->stuGrade
				>> NewNode->stuClass >> NewNode->stuScore;
			// if(NewNode->stuScore){
			// 	cout <<"成绩输入有误请重新输入"<<endl;
			// 	break;
			// }
			// cin >> NewNode->stuID;
			// cin >> NewNode->stuGrade;
			// cin >> NewNode->stuClass;
			// cin >> NewNode->stuScore;
		// g++ -g -Wall ManageStuSys.cpp ManageLink.cpp -I ../inc -o ms

			addStuInfo(head, NewNode->stuInfo, NewNode->stuScore);
			// printNode(NewNode);
			saveStudInfo(NewNode);
			break;
		case '2':
			cout <<"*****请输入修改的学生的学号、总成绩*****"<< endl;
			cout <<"*****格式:学号,总成绩     *****"<< endl;
			cin >> id >> socer;
			modifyStuSocre(head,id,socer);
			break;
		case '3':
			cout <<"*****请输入查询的学生姓名和学号*****"<< endl;
			cout <<"*****格式:姓名,学号         *****"<< endl;
			// cin >>>>>>;
			serachStuInfo(head);		
			break;			
		case '4':
			cout <<"*****你现在正在查看学生成绩排名*****"<< endl;
			getStuQueue(head);			
			break;
		case '5':
			cout <<"*****请输入删除的学生姓名和学号*****"<< endl;
			cout <<"*****格式:姓名,学号         *****"<< endl;
			deleteStuInfo(head);		
			break;
		case '6':
			cout <<"*****功能开发中*****"<< endl;
		
			break;
		case '7':
			cout <<"*****功能开发中*****"<< endl;
		
			break;
		case '8':
			cout <<"*****功能开发中*****"<< endl;
		
			break;
		default:
			cout <<"您的输入有误,请重新输入"<< endl;
			break;
		}
	}

	return 0;

}

4. ManageLink.cpp

#include <iostream>
#include <string>
#include <Windows.h>
#include <fstream>
#include "../inc/ManageStuSys.h"


// #include "ManageStuSys.h"

void* NodeInit(){
	STU *head;
	head = (STU*)malloc(sizeof(STU));
	head->next = NULL;
	return head;
}
//显示所有成绩
void printNode(STU *head){
	while(head->next){
		std::cout <<"姓名:"<< head->next->stuName 
		<< "\t学号:"<< head->next->stuID
		<< "\t年级:"<<head->next->stuGrade
		<< "\t班级:"<<head->next->stuClass
		<<"\t总成绩:"<< head->next->stuScore << std::endl;
		head = head->next;
	}
}
void exitPrecent(){
	int i = 0;
	printf("正在退出系统...\n");
	while(1){

	// for(int i =0 ;i<101;i+=10){
		for(int j = 0 ;j <i;j+=10){
			printf("*");
		}
		for(int n = 0; n<10-i/10;n++){
			printf("=");
		}
		if(i<101){
			Sleep(50);
			printf("\r%d%%",i);
		}
		else
		{
			printf("\n已退出系统");
			break;
		}
		i+=10;

	}

}
void addStuInfo(STU *head, char *stuInfo[],  double stuScore){ 
	STU *Temp = (STU*)NodeInit(); 
	strcpy(Temp->stuName, stuInfo[0]);
	strcpy(Temp->stuID, stuInfo[1]);
	strcpy(Temp->stuGrade, stuInfo[2]);
	strcpy(Temp->stuClass, stuInfo[3]);
	Temp->stuScore = stuScore;

	Temp->next = head->next;
	head->next = Temp;
	// free(Temp);
}
void saveStudInfo(STU *New){
	std::ofstream out;
	char str[100];
	out.open("./stuinfo.txt",std::ofstream::app);
	if(out.is_open()){
		for(int i = 0;i < 4;i++){
			strcpy(str, New->stuInfo[i]);
			// std::cout << New->stuInfo[0] <<std::endl;
				out << str;
				if(i < 4)out << ",";
			}
		out << New->stuScore;
		out << "\n";
	}else{
		std::cout <<"File Error"<<std::endl;
	}
	out.close();
	
}
void modifyStuSocre(STU *New, char id[], double socer){
	while(New->next){
		if(New->stuID == id){
			New->stuScore = socer;
		}
		New = New->next;
	}
	std::cout <<"成绩已修改"<< std::endl; 
	std::cout <<"姓名:"<< New->stuName 
	<< "\t学号:"<< New->stuID
	<< "\t年级:"<< New->stuGrade
	<< "\t班级:"<< New->stuClass
	<<"\t总成绩:"<< New->stuScore << std::endl;
}

void serachStuInfo(STU *New){
	// printNode(New);
}

void deleteStuInfo(STU *New){
	;
}

void getStuQueue(STU *New){
	;
}

 5. make结果

$ make
pwd
/h/CWorkSpace/GCCBuild/src
echo Preprocessing Begin
Preprocessing Begin
g++ -E ManageStuSys.cpp -o ./StuSys/ManageStuSys.ii
echo Preprocessing product .ii
Preprocessing product .ii
echo Compile Begin
Compile Begin
g++ -S StuSys/ManageStuSys.ii  -o ./StuSys/ManageStuSys.s
echo Compile product .s
Compile product .s
echo Assembly Begin
Assembly Begin
g++ -c StuSys/ManageStuSys.s  -o ./StuSys/ManageStuSys.o
g++ -c ./ManageLink.cpp -o ./StuSys/ManageLink.o
echo Assembly product .o
Assembly product .o
echo Link Begin
Link Begin
g++  StuSys/ManageStuSys.o StuSys/ManageLink.o -I ../inc/ -o ./StuSys/ManageStuSys.exe
echo Link product .exe
Link product .exe
g++ ./ManageStuSys.cpp ./ManageLink.cpp -I ../inc/ -o ./StuSys/temp
echo Compile End
Compile End
rm -rf ./StuSys/*.*
echo allfile Done
allfile Done

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值