《ARM Linux嵌入式系统开发基础》课件第3章_第1页
《ARM Linux嵌入式系统开发基础》课件第3章_第2页
《ARM Linux嵌入式系统开发基础》课件第3章_第3页
《ARM Linux嵌入式系统开发基础》课件第3章_第4页
《ARM Linux嵌入式系统开发基础》课件第3章_第5页
已阅读5页,还剩211页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第3章LinuxC编译调试基础

3.1Linux下C语言编程概述3.2Vi编辑器的使用

3.3Emacs使用简介

3.4使用GNUCC编程

3.5使用GNUmake

3.6使用autoconf

3.7使用automake

3.8GDB调试工具

3.9在GDB下运行程序

3.10调试程序

3.11设置断点、观测点和异常

3.1Linux下C语言编程概述

C语言最早是由贝尔实验室的DennisRitchie为了UNIX的辅助开发而编写的,C语言是在B语言的基础上开发出来的。尽管C语言不是专门针对UNIX操作系统或机器编写的,但它与UNIX系统的关系十分紧密。由于它的硬件无关性和可移植性,C语言逐渐成为世界上使用最广泛的计算机语言。为了进一步规范C语言的硬件无关性,1987年美国国家标准协会(ANSI)根据C语言问世以来的各种版本,对C语言的发展和扩充制定了新的标准,称为ANSI C。目前流行的C语言编译系统都是以ANSI C为基础。C语言具有强大的功能和各种硬件平台上良好的可移植性。

C语言可移植性强,适用于多种操作系统,如DOS、Windows、Linux,也适合于多种CPU硬件体系结构,因此特别适合在嵌入式领域进行开发。

Linux下的C语言程序设计与在其他环境中的C程序设计一样,主要涉及到编辑器、编译链接器、调试器及项目管理工具等。

1.编辑器

Linux下的编辑器主要完成对所录入文字的编辑功能。Linux中最常用的编辑器有Vi(Vim)和Emacs,其功能强大,使用方便,广受编程爱好者的好评。在本章中着重介绍Vi和Emacs的使用。

2.编译器

编译过程是指源代码转化生成可执行代码,它所完成的工作主要如图3-1所示。在Linux中,最常用的编译器是GCC编译器。它是GNU推出的功能强大、性能优越的多平台编译器,其执行效率与一般的编译器相比平均效率要高20%~30%,为Linux环境下使用最广泛的编译器。图3-1编译过程

3.调试器

编程的过程中,往往调试所消耗的时间远远大于编写代码的时间,调试器是专为程序员方便调试程序而用的。因此一个功能强大、使用方便的调试器是必不可少的。GDB是绝大多数Linux开发人员所使用的调试器,它可以方便地设置断点、单步跟踪等,足以满足开发人员的需要。

4.项目管理器

Linux中的项目管理器“make”有些类似于Windows中VisualC++里的“工程”,它是用来控制编译或者重复编译软件的工具,另外,在Linux环境下,“make”还能自动管理软件编译的内容、方式和时机,使程序员能够把精力集中在代码的编写上而不是在源代码的组织上。3.2Vi编辑器的使用

Linux系统编辑器包括行编辑器(Ed、Ex)和全屏幕编辑器(Vi、Emacs)。行编辑器每次只能对一行进行操作,使用起来很不方便;而全屏幕编辑器可以对整个屏幕进行编辑,直观地在屏幕上直接显示用户编辑的文件,便于用户学习和使用,具有强大的功能。

Vi是Linux系统的第一个全屏幕交互式编辑程序,它从诞生至今一直得到广大用户的青睐,历经数十年仍然是人们主要使用的文本编辑工具,足以见其生命力之强,而强大的生命力是其强大的功能带来的。由于大多数读者在此之前都已经用惯了Windows的Word等编辑器,因此,在刚刚接触Vi编辑器时总会或多或少不适应,但只要习惯之后,就能感受到它的方便与快捷。

1.Vi的工作模式

Vi有三种工作模式,分别为命令行模式、插入模式及底行模式。

(1)命令行模式。用户在用Vi编辑文件时,最初进入的为一般模式。在该模式中可以通过上下移动光标进行删除字符或整行删除等操作,也可以进行复制、粘贴等操作,但无法编辑文字。

(2)插入模式。只有在该模式下,用户才能进行文字编辑输入,用户可按Esc键回到命令行模式。

(3)底行模式。在该模式下,光标位于屏幕的底行。用户可以进行文件保存或退出操作,也可以设置编辑环境,如寻找字符串、列出行号等操作。

2.Vi的基本编辑流程

(1)进入Vi,即在命令行下键入“Vihello”(文件名)。此时进入的是命令行模式,光标位于屏幕的上方。

(2)在命令行模式下键入“i”进入到插入模式,在该模式下可以输入文字信息,屏幕底部显示有“插入”表示插入模式。

(3)插入模式中,按Esc键即可转入命令行模式,并在底行行中输入“:wq”(存盘退出)进入底行模式。

这样,就完成了一个简单的Vi操作流程:命令行模式→插入模式→底行模式。由于Vi在不同的模式下有不同的操作功能,可根据屏幕最下方的提示分清所在的模式。

3.Vi的各模式功能键

Vi的命令行模式常见功能键如表3-1所示。表3-1Vi的命令行模式功能键

录内

容I切换到插入模式,此时光标在开始输入文件处A切换到插入模式,从目前光标所在位置的下一位置输入文字O切换到插入模式,从行首开始输入新行[ctrl]+[b]后翻屏幕[ctrl]+[f]前翻屏幕[ctrl]+[u]后翻半页[ctrl]+[d]前翻半页0光标移到本行开头G光标移到文章最后nG光标移到第n行n<Enter>光标下移n行/name光标后查找名为name的字符串?name光标前查找名为name的字符串X删除光标所在位置后面的一个字符dd删除光标所在行ndd删除光标所在行下n行yy复制所在行p将缓冲区内容粘贴光标处U恢复前一个动作3.3Emacs使用简介如上节所述,Vi是一款功能非常强大的编辑器,它能够方便、快捷、高效地完成用户的代码编辑。而Emacs不仅仅是一款功能强大的编译器,而且是一款融合编辑、编译、调试于一体的开发环境,它在没有图形显示的终端环境下可出色地工作。

Emacs的使用方法和Vi不同。在Emacs里,没有类似于Vi的三种“模式”,只有一种模式,也就是编辑模式,而且它的命令全靠功能键完成。但Emacs有各种辅助环境。比如,当编辑普通文本时,使用的是“文本模式”(TxtMode),而当编写程序时,使用的则是如“C模式”、“shell模式”等。3.3.1Emacs的基本操作

1.Emacs的安装

现在较新版本的Linux的安装光盘中一般都自带有Emacs的安装包,用户可以通过安装光盘选择安装。

2.Emacs的启动

安装完Emacs之后,可以在命令行键入“emacs[文件名]”(若缺省文件名,则可在Emacs编辑文件后另存时指定)启动,也可从“编程”→“emacs”打开Emacs欢迎界面。接下来可单击任意键进入Emacs的工作窗口。

Emacs的工作窗口分为两个部分,上部为编辑窗口,底部为命令显示窗口,用户执行功能键的功能都会在底部有相应的显示,有时也需要用户在底部窗口输入相应的命令,如查找字符串等。

3.Emacs的进入

在进入Emacs后,即可进行文件的编辑。首先说明一下,以“C”表示Ctrl键,“M”表示META键(一般为Alt键)。Emacs中基本编辑功能键包括以下内容。

1)移动光标

在Emacs中可以使用“上”、“下”、“左”、“右”方向键来移动单个字符,也可以按Ctrl键+字符。表3-2列举了Emacs中光标移动的常见功能键。表3-2Emacs中光标移动的常见功能键

键定义功能C+f向前移动一个字符C+b向后移动一个字符C+p移动到上一行C+n移动到下一行M+b向后移动一个单词C+a移动到行首C+e移动到行尾M+<移动到文本开头

2)剪切和粘贴

在Emacs中可以使用Delete键和BackSpace键删除光标前后的字符,以词和行为单位的剪切和粘贴功能键如表3-3所示。表3-3以词和行为单位的剪切和粘贴功能键

键定义功能M+Delete剪切光标前的字符M+d剪切光标前的字符C+k剪切光标前到行尾的字符M+k剪切光标前到句尾的字符C+y粘贴缓冲区内容到光标位置C+xu撤销操作

3)复制文本

在Emacs中复制文本包括两步:选择复制区域和粘贴文本。

选择复制区域的方法是:首先在复制起始点(A)按C+Space键或C+@(或C+Shift+2)键使它成为一个表示点,再将光标移至复制结束点(B),再按M+w键,就可将A与B之间的文本复制到系统的缓冲区中。然后使用功能键C+y将其粘贴到指定位置。

4)查找文本

查找文本的功能键如表3-4所示。表3-4查找文本的功能键键定义功能C+s在光标后内容里查找相应字符串C+r在光标前内容里查找相应字符串

5)保存文档

在Emacs中保存文档的功能键为C+xC+s(即先操作C+x,再操作C+s)。这时,屏幕底部的对话框会出现如“Wrote/root/workplace/editor/why”字样。

另外,Emacs在编辑时会为每个文件提供“自动保存”(Autosave)的功能,而且自动保存的文件名前后都有一个“#”,例如,编辑名为“hello.c”的文件,其自动保存的文件名就叫“#hello.c#”。当用户正常地保存了文件后,Emacs就会删除这个自动保存的文件。此功能当系统发生异常时非常有用。

6)退出文档

在Emacs中退出文档的功能键为C+xC+c。

3.3.2Emacs编译概述

Emacs不仅仅是个强大的编译器,还是一个集编译、调试等于一体的工作环境。

1.Emacs中的模式

正如前面提到的,在Emacs中并没有像Vi中那样的“命令行”、“编辑”模式,只有一种编辑模式。这里所说的“模式”是指Emacs里的各种辅助环境。这里着重介绍C模式,即当启动某一文件时,Emacs会判断文件的类型,从而自动选择相应的模式。当然,用户也可以手动启动各种模式,用功能键M+x,然后再输入模式的名称,即可启动C模式。在强大的C模式下,用户拥有“自动缩进”、“注释”、“预处理扩展”、“自动状态”等功能。在C模式下编辑代码时,可以用Tab键自动将当前行的代码产生适当的缩进,使代码结构清晰、美观,它也可以用于指定缩进的规则。

源代码要有良好的可读性,就必须有清晰的注释。在Emacs中,用M+可以产生一条右缩进的注释。C模式下是“/*comments*/”形式的注释,C++模式下是“//comments”形式的注释。用户高亮选定某段文本,然后操作“C+cC+c”就可以注释该段文字。Emacs还可以使用C模式来预处理其运行代码的一部分,以便让程序员检测宏、条件编译以及include语句的效果。

2.Emacs编译调试程序

Emacs可以在Emacs环境下编译软件。编辑器把编译器的输出和程序代码连接起来。可以如同在Windows的其他开发工具中一样,将出错位置和代码定位联系起来。Emacs默认的编辑命令是对一个make的调用,用户可以打开“tool”菜单下的“Compile”进行查看。Emacs可以支持大量的工程项目,以方便开发。

另外,Emacs为GDB调试器提供了一个功能齐全的接口。在Emacs中使用GDB的时候,程序员不仅能够获得GDB用其他任何方式运行时所具有的全部标准特性,还可以通过接口增强所获得的其他性能。3.4使用GNUCC编程3.4.1LinuxC源程序的编译

大多数的Linux用户对Linux开发还处于比较低级的层次,但是当遇到一些需要编译安装的软件时,面对一些简单的出错信息,就会不知所措。要想真正跨跃这些初级层次,就需要了解底层的知识,下面将介绍Linux下的C语言开发环境以及其他操作系统的知识。

Linux的操作系统内核主要是用C语言编写的,Linux下的很多软件也是用C语言编写的,特别是一些著名的服务软件,比如MySQL、Apache等。初学者可能在编译MySQL这样的软件时遇到过各式各样的错误,其实只要了解了Linux的C语言开发环境,就能解决安装过程中的一些错误。

Linux的C语言开发环境与Windows的有所不同,在Linux下,一个完整的C语言开发环境包括以下三个部分。

1.函数库(glibc)

要构架一个完整的C语言开发环境,glibc是必不可少的,它是Linux下C语言的主要函数库。glibc有如下两种安装方式:

1)安装为测试用的函数库,在编译程序时用不同的选项来试用新的函数库。

(2)安装为主要的C语言函数库,所有新编译程序均使用的函数库。

Glibc中包含三个附加包:LinuxThreads、locale和crypt,通常它们的文件名随版本不同而不同,类似于下列文件名:

glibc-2.06.tar.gzglibc-linuxthreads-2.0.6.tar.gz

glibc-localedate-2.0.6.tar.gz

glibc-crypt-2.0.6.tar.gz

2.编译器(GCC)

GCC(GNUCCompiler)是GNU推出的功能强大、性能优越的多平台编译器,GCC编译器能将C、C++语言源程序、汇编源化序和目标程序编译、链接成可执行文件,以下是GCC支持编译的一些源文件的后缀及其解释:

以 .c为后缀的文件是C语言源代码文件;以 .a为后缀的文件是由目标文件构成的档案库文件;以 .C、.cc或 .cxx为后缀的文件是C++ 源代码文件;以 .h为后缀的文件是程序所包含的头文件;以 .i为后缀的文件是已经预处理过的C源代码文件;以 .ii为后缀的文件是已经预处理过的C++ 源代码文件;以 .m为后缀的文件是Objective -C源代码文件;以 .o为后缀的文件是编译后的目标文件;以 .s为后缀的文件是汇编语言源代码文件;以 .S为后缀的文件是经过预编译的汇编语言源代码文件。

3.系统头文件(glibc_header)

缺少了系统头文件的话,很多用到系统功能的C程序将无法编译。

假如用户在安装过程中少装了这些文件包,就会无法编译C源程序。初学者有时候选择自己定制软件包来安装,结果遗漏了这些文件包,导致无法编译源程序,不少人就只好重新安装一遍Linux。其实并不需要这样做,虽然从压缩包中解压缩来安装Linux的C语言开发环境对于初学者来说比较难,但是可以通过rpm包来迅速安装Linux的C语言开发环境的。下面介绍如何安装Linux的C语言开发环境,要注意软件版本的不同。

由于GCC包需要依赖binutils和cpp包,另外make包也在编译中常用,因此一共需要8个包来完成安装,分别为:

cpp-2.96-110.i386.rpm

binutils-.2-11.i386.rpm

glibc-2.2.5-34.i386.rpm

glibc-kernheaders-2.4-7.14.i386.rpm

glibc-common-2.2.5-34

glibc-devel-2.2.5-34.i386.rpm

gcc-2.96-110.i386.rpm

make-3.79.1-8.i386.rpm如果能获得Internet接入的话,就可以直接从Internet上安装,命令依次如下:

rpm-ivh

8/linux/redhat/7.3/en/os/i386

/RedHat/RPMS/cpp-2.96-110.i386.rpm

rpm-ivh

8/linux/redhat/7.3/en/os/i386

/RedHat/RPMS/binutils-.2-11.i386.rpm

rpm-ivh

8/linux/redhat/7.3/en/os/i386

/RedHat/RPMS/glibc-kernheaders-2.4-7.14.i386.rpm

rpm-ivh

8/linux/redhat/7.3/en/os/i386

/RedHat/RPMS/glibc-2.2.5-34.i386.rpm

rpm-ivh

8/linux/redhat/7.3/en/os/i386

/RedHat/RPMS/glibc-devel-2.2.5-34.i386.rpm

rpm-ivh

8/linux/redhat/7.3/en/os/i386

/RedHat/RPMS/glibc-common-2.2.5-34.i386.rpm

rpm-ivh

8/linux/redhat/7.3/en/os/i386

/RedHat/RPMS/gcc-2.96-110.i386.rpm

rpm-ivh

8/linux/redhat/7.3/en/os/i386

/RedHat/RPMS/make-3.79.1-8.i386.rpm如果不能获得Internet链接的话,需要将上述8个包下载安装。安装完成后,就构成了最基本的C语言开发环境,在这个C语言开发环境中,可以编译多数的C语言应用程序。而对于C语言程序来说,安装完成后通常可以分成三个组成:①可执行文件;②包含文件;③库文件。

可执行文件就是最终运行的程序;包含文件是该C程序include的一些定义文件;库文件则是该C程序自定义的库。比如对于用RPM安装的MySQL,可执行文件放在/usr/bin下,包含文件放在/usr/include/mysql下,库文件在/usr/lib/mysql下。只有系统运行时正确找到程序对应的包含文件和库文件,程序可执行文件才能正常运行。

GCC是GNU项目的编译器套件,可以编译用C、C++、Java、FORTRAN、Objc、Adsa等语言编写的程序。本书的测试环境均使用2.95.3版本的GCC。读者可以从网上免费下载GCC的新版本并安装。GCC的官方网站。

GCC典型的配置如下所示:

../gcc-x.x.x/configure--prefix=/usr/local/gcc-x.x.x\

--enable-threads=posix--disable-checking\

--enable--long-long\

--host=i386-redhat-linux\

--with-system-alib\

--enable-languages=c,c++,java上述配置指明了新安装的GCC编译器可以编译C、C++和Java程序。由此可见,GCC是完全可以定制的。安装新版本的GCC需要系统有旧版本的CC或GCC编译器。如果系统上没有编译器,则不能安装源代码形式的GCC,那么可以在网上找一个与系统相适应的二进制形式的GCC软件包来安装使用。通常这类软件包是RPM格式的。查看系统GCC版本的命令为

gcc-v

察看GCC所在路径的命令为

whichgcc

在确认了系统中存在GCC编译器及其版本号后,就可以开始软件开发工作。3.4.2“Hello,World!”

软件开始的第一个程序是很简单的,但也是很经典的,它只是在屏幕上打印一行文字“Hello,World!”,程序如下:

/*

hello.c-Averysimpleprogram

*/

#include<stdio.h>

intmain(void)

{

printf(“Hello,World!\n”);

return0;

}在命令行中键入下面命令编译上述程序:

$gcchello.c-ohello

$./hello

Hello,World!

第一行命令使用GCC对源文件hello.c进行编译和链接,-o选项的功能是指定创建的可执行文件的名称。当然也可以不使用 -o选项,这样可以得到关于GCC最为简单的用法:

$gcchello.c

$./a.out

HelloWorld!上面是不指定可执行文件名称的用法,此时GCC使用默认的名字a.out,执行的结果是一样的。上述过程看起来非常简单,但是GCC的工作远比这复杂得多:预处理、编译、汇编、链接都是GCC在后台完成的。首先,GCC运行预处理程序cpp,展开hell.c中的宏,并在其中插入#include文件所包含的内容,然后把预处理后的源代码编译成目标代码,最后通过链接程序ld创建可执行文件。为了清楚地了解执行过程,可以使用GCC的-E选项:

$gcc-Ehello.c-ohello.cpp

查看hello.cpp文件可以看到stdio.h文件的内容已经被插入到文件中,其他一些内容也得到了处理,具体如下:#29“/usr/include/bits/types.h”23

typedefunsignedchar_u_char;

typedefunsignedshort_u_short;

typedefunsignedint_u_int;

typedefunsignedlong_u_long;

_extension_typedefunsignedlonglongint_u_quad_t;

_extension_typedeflonglongint_u_quad_t;

#48“/usr/include/bits/types.h”3typedefsignedchar_int8_t;

typedefunsignedchar_uint8_t;

typedefsignedshortint_int16_t;

typedefunsignedshortint_int16_t;

typedefsignedint_int32_t;

typedefunsignedint_uint32_t;

_extension_typedefsignedlonglongint_int64_t;

_extension_typedefunsignedlonglongint_uint64_t;经过预处理后,GCC会把hello.cpp编译成目标代码,此过程可采用GCC的-c选项来实现。

上面命令行中的-x选项使GCC从指定的步骤开始编译,链接目标文件,最终生成可执行文件。

在实际开发过程中很少有如此简单的程序,大多数程序是由多个源代码组成的。GCC可以很容易地把多个源文件编译成目标代码并进行链接。假设项目中有很多源文件需要编译,并且每个源文件中都包含1000行代码,如果像上面那样仅用一条GCC命令完成编译工作,那么GCC需要将每个源文件都重新编译一遍,然后再全部链接起来。显然,这样浪费的时间相当多,尤其是当用户只是修改了其中某一个文件的时候,完全没有必要将每个文件都重新编译一遍,因为很多已经生成的目标文件是不会改变的。要解决这个问题,熟练使用GCC还是不够的,同时必须借助make工具。

3.4.3GCC的主要选项

GCC提供了非常多的命令行选项,熟练地使用这些选项能更有效使用GCC。表3-5列出了GCC中常用的命令行选项。表3-5GCC中常用的命令行选项

选项说明-ansi只支持ANSI标准的C语法。该选项将禁止GNUC的某些特色,例如asm或typeof关键字-c只编译并生成目标文件-DMACRO以字符串1定义MACRO宏-DMACRO=DEFN以字符串DEFN定义MACRO宏-E只运行C预编译器-g生成调试信息,GNU调试器可利用该信息-IDIRECTORY指定额外的头文件搜索路径DIRECTORY-LDIRECTORY指定额外的函数库搜索路径DIRECTORY-ILIBRARY链接时搜索指定的函数库LIBRARY-m486针对486进行代码优化-oFILE生成指定的输出文件,用于生成可执行文件时-O0不进行优化处理-O或-O1优化生成代码-O2进一步优化-O3比-O2更进一步优化,包含inline函数-shared生成共享目标文件,通常用在建立共享库时-static禁止使用共享连接-UMACRO取消对MACRO宏的定义-w不生成任何告警信息-Wall生成所有告警信息3.5使用GNUmake编写小型Linux应用程序时,一般情况下只会有很少数量的源文件,程序员很容易理清它们之间的包含和引用关系。但随着软件项目逐渐变大,对源文件的处理也将变得越来越复杂。此时单纯依赖手工方式进行管理的做法就显得有些力不从心。为此,Linux专门为软件开发提供了一个自动化管理工具GNUmake。通过它可以很方便地管理软件编译的内容、方式和时机,从而能够把主要精力集中在代码的编写上。

make工具将整个软件项目的代码分开放在几个小的源文件里,在改动其中一个文件的时候,可以只对该文件重新进行编译,然后重新链接所有的目标文件。针对由许多源文件组成的大型软件项目,全部重新进行编译需要花费很长的时间,而采用这种项目管理方法则可以极大地提高工作效率,让原本复杂繁琐的开发工作变得简单。make工具最主要也是最基本的功能就是通过Makefile文件来描述源程序之间的相互关系并自动维护编译工作。而Makefile文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并链接生成可执行文件,并要求定义源文件之间的依赖关系。3.5.1初识Makefile

Makefile里主要包含了显式规则、隐晦规则、变量定义、文件指示和注释。

(1)显式规则:说明了如何生成一个或多个目标文件。这是由Makefile的书写者明显指出的,包括要生成的文件、文件的依赖文件和生成的命令。

(2)隐晦规则:由于make工具有自动推导的功能,因此隐晦的规则可以让用户比较简略地书写Makefile,这是由make工具所支持的。

(3)变量的定义:在Makefile中要定义一系列的变量,变量一般都是字符串,如C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。

(4)文件指示:包括三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;可以定义一个多行的命令,有关这一部分的内容将会在后文中讲述。

(5)注释:Makefile中只有行注释,和UNIX的shell脚本一样,其注释是用“#”字符,这个就像C/C++ 中的“//”一样。如果要在Mallefile中使用“#”字符,可以用反斜框进行转义,如“\#”。在Makefile中的命令必须要以Tab键开始。

Make能完成复杂的任务,得益于Makefile文件。Makefile是以文本形式存在的数据库文件,在Makefile文件中包含了一系列的规则,告诉make工具应编译哪些文件、如何编译以及在什么情况下进行编译。每个规则都包含下面一些内容。

目标(Target):make工具最终要创建的文件。

依赖关系列表(Dependency):通过这张列表了解编译目标时需要用到的文件。命令列表(Command):从指定的依赖关系创建出目标文件而需要执行的命令。这些命令包括编译命令,也可以是shell命令。

简单地说,Makefile规则有下列通用形式:

target:dependency[dependency[…]]

command

command

[…]对于前面小节编译的文件:hello.c、printf2.c、print2.h,使用Makefile重新编译。以下程序是编译这三个文件的Makefile内容。

#ThisisasimpleMakefile

hello:hello.oprintf2.oprintf2.h

gcchello.oprintf2.o-ohello

printf2.o:printf2.cprintf2.h

gcc-cprintf2.c

hello.o:hello.c

gcchello.c-ohello

all:printf2hello

clean:

rmhello*.o通过上面的例子可以看到,第一个字符为“#”的行为注释行。第一个非注释行指定要生成的hello文件是由3个目标文件hello.o、printf2.o和printf2.h链接生成。第3行描述了如何从hello所依赖的文件建立可执行文件,接下来的第4、6、8行分别指定3个目标文件及其所依赖的 .c和 .h文件。而第5行和第7行则指定了如何从目标所依赖的文件建立目标。当filea.c或a.h文件在编译之后又被修改,则make工具可自动重新编译filea.o,如果在前后两次编译之间,filea.c和a.h均没有被修改,而且test.o还存在的话,就没有必要重新编译。这种依赖关系在多源文件的程序编译中尤其重要。通过这种依赖关系的定义,make工具可避免许多不必要的编译工作。当然,利用shell脚本也可以达到自动编译的效果,但是shell脚本将全部编译源文件,包括那些不必要重新编译的源文件,make工具则可根据目标文件上一次编译的时间和目标文件所依赖的源文件的更新时间自动判断应当编译哪些源文件。3.5.2Makefile规则

一个简单的Makefile文件包含一系列的规则,其形式如下:

目标(target)…:依赖(rerequiries)…

<tab>命令(cornmand)

说明:

(1)目标通常是要产生的文件的名称。目标可以是可执行文件或OBJ文件,也可是一个执行的动作名称,比如clean。

(2)依赖是用来输入从而产生目标的文件,一个目标经常有几个依赖。

(3)命令是make工具执行的动作,一个规则可以含有几个命令,每个命令占一行。

Makefile中还有一些规则被称为隐晦规则。允许make工具对一个目标文件寻找传统的更新方法,而避免指定任何命令。可以编写没有命令行的规则或根本不编写任何规则,这样,make工具将根据存在的源文件类型或要生成的文件类型决定使用何种隐含规则。例如,假设Makefile文件是下面的格式:

foo:foo.obar.o

cc-ofoofoo.obar.o$(CFLAGS)$(LDFLAGS)因为这里提及了文件foo.o,但是没有给出规则,make工具将自动寻找一条隐含规则,该规则能够告诉make工具怎样更新该文件。无论文件foo.o存在与否,make工具都会这样执行。make工具会找到如下规则生成foo.o:

CompilingCprograms(编译C程序)。

n.o自动由n.c使用下面的命令生成:

‘$(CC)-c$(CPPFLAGS)$(CFLAGS)’

因此当使用隐含规则时,关注编译器的额外标识变量,表3-6列出了比较常用的标识变量。表3-6编译器的额外标识变量

选项编译器的额外标识变量ASFLAGS用于汇编编译器的额外标识CFLAGS用于C编译器的额外标识CXXFLAGS用于C++编译器的额外标识CPPFLAGS用于C预处理以及使用它的程序的额外标识LDFLAGS用于调用linker(ld)的编译器的额外标识3.5.3Makefile中的变量

Makefile里的变量如同环境变量。环境变量在make过程中被解释成make的变量。这些变量是大小写敏感的,一般使用大写字母。它们几乎可以从任何地方被引用,也可以执行其它功能,例如:

(1)存储文件名列表。在前面的代码中,生成可执行文件的规则包含一些目标文件名作为依赖。在规则的命令行里同样的那些文件被输送给GCC作为命令参数。如果在这里使用变数来存储所有的目标文件名,加入新的目标文件会变得简单而且不易出错。

(2)存储可执行文件名。如果项目被用在非GCC的系统里,或者想使用不同的编译器,就必须将所有使用编译器的地方改成新编译器名。如果使用变量来代替编译器名,那么只需改变其中一个地方,所有的命令名就都得到改变。

(3)存储编译器标志。如要给所有的编译命令传递一组相同的选项,将这组选项存入一个变量,可以把这个变量放在所有呼叫编译器的地方。当要改变选项时,只需在一个地方改变这个变量的内容。当设定一个变量时,只要在一行的开始写下变量名,后面跟一个等号(=)和所要设定的变量值。以后再引用该变量时,可直接写美元符号($),后面为括号内的变量名即可。语句如下:

OBJS=foo.obar.o

CC=gcc

CFLAGS=_Wall-O-g

Myprog:$(OBJS)

$(CC)-cfoo.c-ofoo.o

bar.o:bar.cbar.h

$(CC)$(CFLAGS)-cbar.c-obar.o

Makefile中有几个默认变量,也就是make工具不需定义就可以使用的变量。表3-7列出了这些默认变量。表3-7Makefile默认变量

选项说明AR档案管理程序:默认为:arAS汇编编译程序:默认为:asCCC语言编译程序:默认为:ccCXXC++编译程序:默认为:c++CPP带有标准输出的C语言与处理程序:默认为:$(CC)-ERM删除文件的命令:默认为rm-f自动变量在编写规则中的命令时使用,比如:

目标(target)…:依赖(prerequiries)…

<tab>命令(command)

以下符号在命令中出现时具有特殊含义,说明如下:

$@:规则的目标文件名。对于有多个目标的格式规则,为规则命令运行的目标文件名。

$<:第一个依赖的文件名。如果目标更新命令来源于隐含规则,则该变量的值是隐含规则添加的第一个依赖。

$^:所有依赖的名字,名字之间用空格隔开。如果在依赖列表中同一个文件名出现多次,则仅包含该文件名一次。

$*:和隐含规则匹配的stem。3.5.4简单的Makefile文件内容

简单的Makefile如下:

OBJS=foo.obar.o

CC=gcc

CFLAGS=-Wall-O-g

myprog:$(OBJS)

$(CC)$(OBJS)-omyprog

foo.o:foo.cfoo.hbar.h

$(CC)$(CFLAGS)-cfoo.c-ofoo.o

bar.o:bar.cbar.h

$(CC)$(CFLAGS)-c$<-o$@说明:

(1)第1~3行定义变量:OBJS、CC、CFLAGS。

(2)有3条规则,而第一条一般作为默认规则,也就是说如果在shell命令行中只输入make的话从这里开始。

(3)第1条规则是编译foo.o和bar.o,编译完后再执行gccfoo.obar.o-omyprog,从而生成myprog文件。

(4)第2条规则是关于如何编译foo.o,执行命令gcc-wall-O-g-cfoo.c-o。

(5)第3条规则比第2条复杂一些,$<是指bar.c,$@是指bar.o,命令如下:

gcc- Wall-O-g-cbar.cbar.o3.5.5假想目标

假想目标(PhonyTargets)并不是真正的文件名,仅仅是制定的一个具体规则所执行的命令名称。常用的假想目标有all、clean等。下面分别以all和clean为例说明假想目标用法。

(1) all的用法如下:

all:exec1exec2

其中,exec1和exec2作为目的的两个可执行文件。make把all作为主要目的,每次执行时都会尝试更新all。规则里没有命令作用在名称为all的实际文件(事实上all并不会在磁盘上实际产生),因此规则并不改变all的状态。既然文件并不存在,make尝试更新all规则,就将检查它的依靠exec1、exec2是否需要更新,从而达到编译目的。

(2) clean的用法如下:

clean:

rm*.otemp

在shell下输入makeclean会执行rm*.otemp,但如果磁盘上存在一个名为clean文件,会因为在规则里没有任何依赖文件,这个目的文件一定是最新的(所有的依赖文件都已经是最新的),即使用户明确命令make工具重新产生,也不会有任何事情发生。解决方法是用 .PHONY标明假想目标,告诉make工具不用检查是否存在磁盘上,也不用查找任何隐含规则,直接假设指定的目的需要被更新。在Makefile里加入以包含上面规则的规则:

.PHONY:clean3.5.6条件语句

条件语句可以导致根据变量的值执行或忽略Makefile文件中的一部分脚本。条件语句可以将变量与其他变量的值相比较,或将变量与字符串常量相比较。条件语句用于控制make工具实际看见的Makefile文件部分,不能用于在执行时控制shell命令。下面的例子中,条件语句告诉make工具如果变量CC的值是gcc时使用一个数据库,若不是则使用其他数据库。该语句通过控制选择两命令行之一作为该规则的命令来工作。CC=gcc作为make工具改变参数的结果,不仅用于决定使用编译器的类型,而且决定连接的数据库。

Libs_for_gcc=– lgnu

normal_libs=

foo:$(objects)

ifeq($(cc),gcc)

$(CC)-ofoo$(objects)$(libs_for_gcc)

else

$(CC)-ofoo$(objects)$(normal_libs)

endif

以上条件语句使用了三个指令:ifeq、else和endif。

ifeq指令是条件语句的开始,它指明了条件。它包含两个参数,被逗号分开并被括在圆括号内。运行时首先对两个参数变量进行替换,进行比较。在Makefile中位于ifeq之后的是符合条件时执行的命令;不符合条件时,它们将被忽略。

else指令指出:如果前面的条件失败将导致跟在其后面的命令被执行。在上述例子中,意味着当第一个选项不执行时,和第二个选项连在一起的命令将被执行。在条件语句中,else指令是可选择使用的。

endif指令用于结束条件语句。任何条件语句必须以endif指令结束,后跟Makefile文件中的正常内容。当变量CC的值是gcc时,上例的效果为

foo:$(objects)

$(CC)-ofoo$(objects)$(libs_for_gcc)

当变量CC的值不是gcc而是其他值的时候,上例的效果为

foo:$(objects)

$(CC)-ofoo$(objects)$(normal_libs)

上例表明条件语句工作在原文水平:条件语句的行根据条件要么被处理成Makefile文件的一部分,要么被忽略。这是Mallefile文件中重大的语法单位可以跨越条件语句的开始或结束的原因。3.5.7依赖关系

(1)变量VPATH(注意为大写)的值指定了make工具搜寻的目录。经常用到的是包含依赖的目录,并不是当前目录;但VPATH指定了make工具对所有文件都适用的目录搜寻序列,包括了规则目标所需要的文件。在VPATH变量定义中,目录名字由冒号或空格分开。目录列举的次序也是make工具搜寻的次序。在MS–DOS、MS-Windows系统中,VPATH变量定义中目录的名字由分号隔开,因为在这些系统中,冒号成为路径名的一部分(通常在驱动器字母后面)。例如:make也按照这个次序进行搜寻。

(2) VPath指令和VPATH变量类似,但却更具灵活性。VPath指令允许对符合一定格式类型的文件名指定一个搜寻路径,这样可以对一种格式类型的文件名指定某个搜寻路径,对另外格式类型的文件名指定另外的搜寻路径。有如下三种形式的VPath指令。

(3) VPathPatterndirectories对一定格式类型的文件名指定一个搜寻路径,搜寻的路径与VPATH变量定义要搜寻的路径格式相同。

(4) VPathPattern清除和一定类型格式相联系的搜寻路径。

(5) VPath清除所有由VPath指令指定的搜寻路径。3.5.8函数

函数调用和变量引用类似,格式为:$(functionargs)或${functionargs}。其中function是函数名,是make内建函数列表中的一个。用户也可以使用创建函数call创建自己的函数。args是该函数的参数。参数和函数名之间用空格或按Tab键隔开,如果有多个参数,它们之间用逗号隔开。

(1) $(substfrom,to,text):在文本text中使用“to”替换每一处“from”。例如:

$(substee,EE,feetonthestreet)

结果为

fEEtonthestreet

(2) $(patsubstpattern,replacement,text):寻找text中符合格式pattern的字符,用replacement替换它们。这里pattern中包含通配符%,它和一个字中任意个数的字符相匹配。如果replacement中也含有通配符%,则这个%被和pattern中源配符%匹配的文本代替。

例如:

$(patsubst%.c,%.o,x.c.obar.c)

结果为

x.c.obar.o替换引用是实现函数patsubst功能的一个简单方法,例如:

$(var:pattern=replacement)

等同于:

$(patsubstpattern,replacement,$(var))

下例说明怎样使用函数subst和patsubst通知C编译器在相同路径列表中搜寻头文件:

$(subst:,,$(VAPTH))

产生值src../headers。然后,函数patsubst为每一个路径名加入– I标志,即

$(patsubst%,– I%,$(subst:,,$(VAPTH)))产生文本为– Isrc–I../headers。最后将这些路径加到变量CFLAGS中:

overrideCFLAGS+=$(patsubst%,– I%,$(subst:,,$(VPATH)))

结果在以前给定的变量CFLAGS的值后追加文本– Isrc– 1../headers。override指令的作用为即使以前使用命令参数指定了变量CFLAGS值,新值也能起作用。

(3) $(filterpattern…,text):返回在text中由空格隔开且匹配格式pattern…的字,对于不符合格式pattern…的字将其移出。格式用%写出,和前面论述过的函数patsubst的格式相同。函数filter用来分离类型不同的字符串。例如:

sources:=foo.cbar.cbaz.sugh.h

foo:$(sources)

cc$(filter%.c%.s,$(sources))– ofoo

表明foo依靠foo.c、bar.c、baz.s和ugh.h;但仅有foo.c、bar.c和baz.s指明用命令编译。

(4) $(filter-outpattern…,text):返回在text中由空格隔开且不匹配格式pattern…的字,对于符合格式pattern…的字将其移出,即

objects=main1.ofoo.omain2.obar.o

mains=main1.omian2.o

产生不包含在变量mains中的OBJ文件的文件列表:

$(filter-out$(mains),$(objects))

(5) $(wildcardpattern):参数pattern为文件名格式,典型的用法包含通配符(和shell中的文件名相同)。函数wildcard的结果是一列和格式匹配且文件存在的文件名,文件名之间用一个空格隔开。如果没有与指定格式一致的文件,则函数wildcard的输出将会省略。注意这和在规则中通配符扩展的方式不同,在规则中使用逐字扩展方式,而不是省略方式。使用函数wildcard得到指定目录下所有的C语言源程序文件名:

$(wildcard*.c)可以把所获得的C语言源程序文件名的字符串,转换为一个OBJ文件名的字符串,其格式为

$(patsubst%.c,%.o,$(wildcard*.c))

这样,编译特定目录下所有C语言源程序,把源程序链接在一起的Makefile文件可以写成如下格式:

objects:=$(patsubst%.c,%.o,$(wildcard*.c))

foo:$(objects)

cc– ofoo$(objects)

这里使用了编译C语言源程序的隐含规则,因此没有必要为每个文件编写具体编译规则“:=”是“=”的变异。

(6)  $(addprefixprefix,names…):参数names作为一系列的文件名,文件名之间用空格隔开;prefix作为一个单位,将prefix(前缀)的值附加在每一个独立文件名前面,完成后将文件名串联起来,文件名之间用单个空格隔开。例如:

$(addprefixsrc/,foobar)

结果为

Src/foosrc/bar3.5.9Makefile的扩展变量

使用“:=”的作用是立即把文件定义中参考到的函数和变量展开。如果使用“=”,函数和变量参考会保留,也就是说改变一个变量的值会导致其他变量的值也被改变。例如:

A=foo

B=$(A)

现在B是$(A),而$(A)是foo。

A=bar

现在B仍然是$(A),但A的值已改变为bar。

B:=$(A)

现在B的值是bar。

A=foo

现在B的值仍然是bar。3.5.10Makefile中的替换

发布版本中每个包含了需要被编译或者被安装文件的目录都应该含有一个文件Makefile.in,configure将利用它在那个目录中创建一个Makefile。为了创建Makefile,configure进行了一个简单的变量替换:用configure为@variable@选取的值,在Makefile.in中对它们进行替换。按照这种方式被替换到输出文件中的变量称为输出变量。在configure中,它们是普通的shell变量。为了让configure把特殊的变量替换到输出文件中,必须把那个变量的名字作为调用AC_SUBST的参数。其他变量的任何@variable@都保持不变。使用configure脚本的软件应该发布文件Makefile.in,而不是Mallefile;这样,用户就可以在编译它之前正确地为本地系统进行配置。下面是每个预定义变量所包含的内容。

(1)变量bindir:用于安装由用户运行的可执行文件的目录。

(2)变量configure_input:用于说明文件是由configure自动生成的,并且给出了输入文件名的注释。AC_OUTPUT在它创建的每个Mallefile文件的开头添加一个包括了这个变量的注释行。对于其他文件,应该在每个输入文件开头处的注释中引用这个变量。例如,一个输入shell脚本应该以如下行作为开始:

#!/bin/sh

#@configure_input@

这一行的存在也提醒程序员在编辑这个文件之后需要用configure进行处理。

(3)变量datadir:用于安装只读的与结构无关的数据的目录。

(4)变量exec_prefix:与结构有关的文件的安装前缀。

(5)变量includedir:用于安装C头文件的目录。

(6)变量infodir:用于安装Info格式文档的目录。

(7)变量libdir:用于安装目标代码库的目录。

(8)变量libexecdir:用于安装由其他程序运行的可执行文件的目录。

(9)变量localstatedir:用于安装可以被修改的单机数据的目录。

(10)变量mandir:用于安装man格式的文档的顶层目录。

(11)变量oldincludedir:用于安装由非GCC编译器使用的C头文件的目录。

(12)变量prefix:与结构无关的文件的安装前缀。

(13)变量sbindir:用于安装主系统管理员运行的可执行文件的目录。

(14)变量sharedstatedir:用于安装可以修改的与结构无关的数据的目录。

(15)变量srcdir:包含了由Makefile使用的源代码的目录。

(16)变量sysconfdir:用于安装只读的单机数据的目录。

(17)变量top_srcdir:包的顶层源代码目录。在目录的顶层,它与srcdir相同。

(18)变量CFLAGS:为C编译器提供的调试和优化选项。如果在运行configure时,没有在环境中设置CFLAGS,可在调用AC_PROG_CC时设置。configure在编译程序以测试C的特征时,使用本变量。

(19)变量CPPFLAGS:为C预处理器和编译器提供头文件搜索目录选项(-Idir)以及其他各种选项。如果在运行configure时,在环境中没有设置本变量,默认值为空。configure在编译或者预处理程序以测试C的特征时,使用本变量。

(20)变量CXXFLAGS:为C++编译器提供的调试和优化选项。如果在运行configure时,没有在环境中设置本变量,那么就在调用AC_PROG_CXX时设置它的值。configure在编译程序以测试C++的特征时,使用本变量。

(21)变量FFLAGS:为FORTRAN77编译器提供的调试和优化选项。如果在运行configure时,在环境中没有设置本变量,那么它的值就在调用AC_ROG_F77时被设置(如果没有调用AC_ROG_F77,它就为空)。configure在编译程序以测试FORTRAN77的特征时,使用本变量。

(22)变量DEFS:传递给C编译器的-D选项。如果调用了AC_CONFIG_HEADER,configure用-DHAVE_CONFIG

_H代替@DEFS@。configure测试时,本变量没有被定义,只有在创建输出文件时候才定义。

(23)变量LDFLAGS:为连接器提供的Stripping(-s)的选项和其他各种选项。如果运行configure时,在环境中没有设置本变量,它的值就是空。configure在连接程序以测试C的特征时使用本变量。

(24)变量LIBS:传递给连接器的-I和-L选项。为了支持从一个软件包的源代码复制件中同时为多种结构进行编译,make用变量VPATH寻找存储在源代码目录中的文件,并把每种结构生成的目标文件都在它们的目录中存储。GNUmake和其他大部分新版本的make程序都可以这样操作,老版本的make程序不支持VPATH。在使用它们的时候,源代码必须与目标代码处于同一个目录。

为了支持VPATH,每个Makefile.in文件都应该包括下列两行:

srcdir=@srcdir@

VPATH=@srcdir@不要把VPATH设置成其他变量的值,比如说VPATH=$(srcdir),这是因为某些版本的make并不对VPATH的值进行变量替换。在configure生成Makefile时,用正确的值对srcdir进行替换。除非在隐含规则中,不使用make变量$<,它将被展开成到源代码目录的文件的路径(通过VPATH确定)。有些版本的make在隐含规则中不设置$<,它们被展开成空值。

Makefile命令行总是应该通过使用前缀$(srcdir)/来引用源代码文件,例如:

Time,info:time.textinfo

$(MAKEINFO)$(srcdir)/time.textinfo可以在包的顶层目录的Makefile.in文件中添加规则,以便在更新配置文件之后可以自动地更新配置信息。下例包括了所有可选的文件,例如“aclocal.m4”和那些与配置头文件有关的文件。从Makefile.in规则中可忽略所有不需要的文件。

由于VPATH机制的限制,因此应该包含${srcdir}/前缀。在重新创建不改变config.h.in和config.h的内容的情况下,不会改变这两个文件的时间标记,因此需要stamp-文件。该特征避免了不必要的重新编译工作。一般地,应该把文件成stamp-h.in包含在包的发布中,以便make能够把config.h.in看做是更新了的文件。在一些旧的BSD系统中,touch或者任何可能导致空文件的命令不会更改时间标记,因此应使用诸如echo之类的命令。

${srcdir}/configure:confihure.inaclocal.m4

cd${srcdir}&&autoconf

#autoheadermightnotchangeconfig.h.in,sotouchastampfile.

${srcdir}/config.in.h:stamp-h.in

${srcdir}/stamp-h.in:configure.inaclocal.m4acconfig.hconfig.h.topconfig.h.bot

cd${srcdir}&&autoheader

echotimestamp>${srcdir}/stamp-h.in

config.h:stamp-h

stamp-h:config.h.inconfig.status

./config.status

Makefile:Makefile.inconfig.status

./config.status

config.status:configure

./config.status–recheck

此外,还应该把echotimestamp>stamp上作为extra_cmds参数传递给AC_OUTPUT,以便config.status能够确认config.h是否更新。3.6使用autoconf

autoconf是用于生成可以自动配置软件源代码包以适应多种UNIX类系统的shell脚本工具。由autoconf生成的配置脚本在运行的时候与autoconf无关,就是说配置脚本的用户并不需要拥有autoconf。由autoconf生成的配置脚本在运行的时候不需要用户手工干预;甚至不需要通过给出参数以确定系统的类型。相反,它们需对软件包可能需要的各种特征进行独立的测试(在每个测试之前,打印一个单行的消息以说明正在进行的检测。因此,在混合系统或者从各种常见的UNIX变种定制而成的系统中工作得很好,没有必要维护文件以储存由各个UNIX变种及各个发行版本所支持的特征列表。

对于每个使用了autoconf的软件包,autoconf从列举了该软件包需要的或者可以使用的系统特征的列表的模板文件中生成配置脚本。在shell代码识别并响应被列出的系统特征之后,autoconf允许多个可能使用该特征的软件包共享该特征。如果调整shell代码,只在一个地方进行修改即可,所有的配置脚本都将被自动重新生成,以使用更新了的代码。

autoconf需要GNUm4以便于生成脚本。autoconf使用了某些UNIX版本的m4所不支持的特征,会超出包括GNUm41.0在内的某些m4版本的内部限制。3.6.1创建configure脚本

由autoconf生成的配置脚本通常被称为configure。在运行的时候,configure会创建一些文件,在这些文件中以适当的值替换配置参数。由configure创建的文件主要如下:

(1)一个或者多个Makefile文件,在包的每个子目录中都有一个。

(2)可创建一个C头文件,名字可以被配置,该头文件包含一些#define命令。

(3)可创建名为config.status的shell脚本,在运行时,重新创建上述文件。

(4)可创建名为config.cache的shell脚本,储存测试的运行结果。

(5)可创建名为config.log的文件,包含由编译器生成的许多消息,以便于在configure出现错误时进行调试。

为了使用autoconf创建configure脚本,需要编写autoconf的输入文件configufe.in并运行auoconf。如果自行编写了特征测试以补充auoconf所提供的测试,还要编写名为aclocal.m4的文件和名为acsite.m4的文件。如果使用了包含#define指令的C头文件,还需要编写aconfig.h,并且需要与软件包一同发布由autoconf生成的文件config.h.in。3.6.2编写configure.in文件

为软件包创建configure脚本,需要编写configure.in文件,该文件包含软件包需要或者可以使用的系统特征测试的autoconf宏调用。现有的autoconf宏可以检测许多特征。对于大部分其他特征,可以使用autoconf模板宏创建定制测试,configure.in可手工编写shell命令检索特征。

除了少数特殊情况,在configur

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论