********************************LoongEmbedded********************************
作者:LoongEmbedded(kandi)
时间:2011.9.9
类别:WINCE 系统开发
********************************LoongEmbedded********************************
WINCE的编译工具Build.exe通过WINCE源代码配置文件提供的下面的信息来编译指定的目录及子目录的源代码(source code):
1) 要贯穿的目录。
2) 要编译的C和微软的的Visual C++文件。
3) 创建的二进制文件。
WINCE源代码配置文件是指下面类型的文件
1) Dirs File
指dirs文件,用于识别包含源代码的子目录,也就是要编译的源代码所在的目录。
2) Make File
指文件夹下面的makefile文件,包含要编译和链接源代码所需要的变量。
3) Module-Definition File
比如是power按键驱动文件下下面的PowerButton.DEF文件,包含在一个可执行或者dll文件中定义的共用符号、函数和变量的声明。
4) Sources File
包含编译源代码所需要的宏变量。
编译工具贯穿一个目录树,先查找dirs文件,然后是sources文件。其中dirs文件指定要包含的源代码或是另外的包含sources文件的子目录。当编译工具在当前的目录定位到一个sources文件,它就调用Nmake tool(Nmake.exe)来编译指定的C或C++源代码文件,并且根据包含在makefile文件的链接规则来链接目标模块。
图1
下面就来分别学习这四种源代码配置文件:
1. dirs文件
dirs文件是一个text文件,它指定了包含要编译的源代码的目录,如SMDK6410 \SRC下的dirs内容:
DIRS = \
common \
oal \
kitl \
drivers \
bootloader
这表示SMDK6410 \SRC目录下的dirs文件要编译common、oal、kitl、drivers和bootloader目录下或者是这些目录下的子目录的源代码文件。
Dirs文件内容中使用DIRS、DIRS_CE和OPTIONAL_DIRS关键字来如何编译指定的目录
1) DIRS
如上面所示,DIRS用于指定要编译的目录,其中DIRS=*表示要编译当面所有的目录,另外上面的例子也可以改为下面的方式来指定要编译的目录
DIRS=common oal kitl dirvers bootloader
也就是用空格键把要编译的目录隔开,但这样不直观,不提倡采用这样的方式。
2) DIRS_CE
只有DIRS_CE指定的目录下的源代码被写进WINCE运行时镜像的时候才编译该目录下的源代码,如camera驱动下的dirs文件内容如下:
DIRS_CE=\
CAMERA_PDD\
OV9650_MODULE\
S3C6410_camera\
DLL
3) OPTIONAL_DIRS
指定可以选择编译的目录,比如OPTIONAL_DIRS=projectA,如果要编译projectA目录,可以设置BUILD_OPTIONS= projectA,然后运行build命令就可以了。
2. makefile文件
源代码树的每个子目录(subdirectory)下面有一个sources文件和一个makefile文件,如下图:
图2
在应用开发环境中(an application development enviroment),比如VS2005中,一个makefile文件包含所有的命令、宏定义、和用于指定如何编译的可选项,但是,相比之下,PB(Platforom Builder)中的makefile文件只包含有对一个共用的makefile的引用,如下:
图3
而图上中提到的环境变量_MAKEENVROOT在\PUBLIC\COMMON\OAK\MISC中的wince.bat中定义,见下图:
图4
图4中环境变量_WINCEROOT,再结合图1我们可知_WINCEROOT=F:\WINCE600,这样就可以确定完整的路径了,同时就可以推断出这个被WINCE各部分共享的编译文件也就在F:\WINCE600\PUBLIC\COMMON\OAK\MISC下,文件名为makefile.def。
在Build.exe定位dirs和sources文件后,它设置一个内部环境变量,然后Nmake.exe使用此环境变量来附加(append) sources.cmn文件到适当的子目录下的sources文件中。接下来,Nmake.exe使用makefiel.def文件来把当前的一系列环境变量转换成对编译器、链接器或其他工具的调用,这样就可以编译sources文件中指定的源代码或者是链接需要指定的目标模块(object modules)。
3. xxx.def文件
xxx.def文件是一个模块定义文件,它包含一个可执行或者dll文件中定义的共用符号、函数和变量的声明,这些声明是可执行文件(一般是dll文件)向外导出的接口,比如PowerButton驱动的PowerButton.def文件,内容如下:
LIBRARY PWR
EXPORTS
PWR_Init
PWR_Deinit
PWR_Open
PWR_Close
PWR_Read
PWR_Write
PWR_Seek
PWR_IOControl
PWR_PowerUp
PWR_PowerDown
虽然所有的可执行文件都能使用xx.def文件,但它主要用于链接器来定义dll的导出函数,xx.def文件的包含有下面的内容,,简要描述常用的部分:
1) NAME
2) LIBRARY
此声明告诉链接器创建一个DLL和此声明部分必须先于其他声明的部分,同时,链接器创建一个入口库(an import library),除非在编译的时候采用export文件(.export),在此不知道是哪里决定是否生成.export文件的,我试过,就算是把我的PowerButton.def文件的内容清空,在对PowerButton驱动编译的时候,也会在PLATFORM\SMDK6410\lib\ARMV4I\retail目录下生成smdk6410_PwrBtn.exp和smdk6410_PwrBtn.lib文件,这两个文件我似乎没有在哪里指定要生成啊????????????????
3) STACKSIZE
4) SECTIONS
5) EXPORT
此声明使一个或多个定义被有效导出给其他应用,主要是用于导出dll文件中的函数接口,这样系统或者应用可以通过把此dll装载到自己的内存空间后就可以使用这些导出的接口函数了。
6) VERSION
4. sources文件
sources文件设置目录中源代码的宏定义,这样Build.exe就可以用这些宏定义来决定如何编译和链接源代码。在Build.exe解析(parsers)一个sources文件之后,Nmake.exe把sources文件并入共用的makefile文件Makefile.def中,Build.exe只遵照宏的指派和忽略其他指示。下面就来学习一下sources文件中的知识点。
1) SYNCHRONIZE_DRAIN
Help文档中的说明如下:
When you perform a build on a development workstation that has multiple processors, this creates the possibility that different Build Tool (Build.exe) threads might be running on different processors.
In this case, you must ensure that your build process is ordered so that Build.exe does not attempt to build a product without first building its dependencies.
If this macro definition is set to 1 in a directory's sources file, Build.exe builds that directory last.
A directory containing a sources file that uses this macro is not built until all other directories are built. For example, this macro definition allows you to ensure that all preceding library (.lib) files are built before building the current directory, which depends on the previous libraries.
Use this macro, and the related SYNCHRONIZE_BLOCK macro, very sparingly. When encountered, these macros completely stall the build process until synchronization is complete.
If a directory uses this macro, list the directory last in its parent dirs file
上面的重点就是如果在sources文件中设置了SYNCHRONIZE_DRAIN=1,那么Build.exe会最后才构建此文件,也就是说先编译dirs文件中其他的sources文件中没有设置了SYNCHRONIZE_DRAIN=1的文件,最后再编译sources文件中定义了此宏的文件,这样就可以解决了库依赖的问题,在这样的情况下,我们就要确保WINCE操作系统的构建过程是有顺序的,以便于Build.exe不会尝试去构建一个它的依赖文件都还没有构建的文件,否则就会产生找不到库的错误。还要注意的一点就是,如果那个目录文件中的sources文件设置了SYNCHRONIZE_DRAIN=1,我们还要应该把此目录文件放在dirs文件的dirs表示行的最后面,这样也说明了dirs中只是的文件也是有顺序的。
2) TARGETNAME
编译生成的目标文件的文件名,不包括扩展名,比如:
TARGETNAME = s3c6410_touch
表示触摸屏驱动生成的目标文件名为s3c6410_touch。
3) TARGETTYPE
生成的目标文件的类型,可以是下面三种类型
LIBRARY:目标文件是一个静态链接库文件(.lib)。
DYNLINK:目标文件是一个动态链接库文件(.dll)。
PROGRAM:目标文件是一个可执行文件(.exe)。
4) RELEASETYPE
此宏定义设置为另个标示(flag):RELEASEDIR和RELEASELIBDIR,用于指定编译的时候生成的二进制文件和库文件存放的目录,下图是RELEASETYPE的取值说明
图5
5) DLLENTRY
如果TARGETYPE=DYNLINK,那么宏DLLENTRY的定义是用于指定此dll文件的入口函数,如果没有为DLLENTRY赋值,就默认此dll的入口函数为_DllMainCRTStartup,下面就来介绍DLLENTRY可能的取值:
⑴_DllMainCRTStartup
此函数是一个dll首先的C运行时(Runtime)入口点,此函数在C运行时中定义。当dll文件被映射到进程的地址空间的时候,操作系统调用此函数来初始化C运行时和调用全局变量的初始化,接下来会调用DllMain函数;在卸载dll的时候,操作系统会调用_DllMainCRTStartup函数来deinitialize C运行时和释放初始化时的内存占用。
因为_DllMainCRTStartup会调用DllMain函数,所以我们的dll要定义DllMain函数,但不需要导出DllMain函数,其函数原型如下:
BOOL DllMain(HANDLE hInstDll, DWORD dwReason, LPVOID lpvReserved);
⑵DllMain
Dll直接的入口函数,如果你的应用中使用C运行时,在我们的dll被进程attach的时候,那么此函数负责执行C运行时任何的初始化,并且在dll被进程detach的时候,负责deinitialize C运行时和释放初始化时的内存占用。
如果DLLENTRY=DllMain,那么我们的dll中就一定要定义此函数,和上面的原型一样,当然,也可以不需要导出此函数,操作系统自己可以调用到这个函数。
⑶_DllEntryCRTStartup
dll基本(basic)的C运行时入口函数,此函数也是在C运行时中定义的,其作用和_DllMainCRTStartup函数类似,但是它会调用DllEntry函数,而不是DllMain函数,所以如果DLLENTRY=_DllEntryCRTStartup,那么就必须定义但不一定需要导出的DllEntry函数,此函数原型如下:
BOOL DllEntry(HANDLE hInstDll, DWORD dwReason, LPVOID lpvReserved);
WINCE系统中C运行时库(CRT)提供很多有用的函数来使编程更加容易,CRT存在于(reside in)coredll.dll和corelibc.lib中,我们可以通过链接coredll.lib来链接在coredll.dll中CRT的一部分。corelibc.lib包含CRT的启动函数,初始之外,我们必须要链接它包含的其他CRT函数来首先确保最适宜的性能。
6) DEFFILE
指定此模块的.def文件,我们知道模块是通过.def文件来导出此模块的接口的。
7) PRECOMPILED_INCLUDE
指定预编译头文件的名字,如果设置了此项,在编译的过程中将enable指定的头文件来参与编译。
8) TARGETLIBS
指定要链接到目标可执行文件(.exe或.dll)中的的其他(additional)库文件和对象文件(.obj)。此宏代表性地只用于在我们的模块需要链接输出库(export library)时,比如coredll.lib,ceddk.lib等,这些库文件就是输出库,它们导出一些共用的函数给应用程序或者是模块,这样就可以使用这些共用的函数了。
9) SOURCELIBS
指定要链接到我们的模块中的库文件,代表性地只用于链接静态库(static library),静态库是一个包含对象、函数和数据的文件。根据我的观察,SOURCELIBS中指定要链接的库一般是针对某个驱动用的而不是整个系统可以共用的库,而TARGETLIBS指定的要链接的库是一些共用的库。
10) SOURCES
指定要编译的源代码文件。
11) SKIPBUILD
如果SKIPBUILD=1,则跳过而不会编译此目录。
12) PREPROCESSDEFFILE
如果设置为1,必须预先处理包含了路径指示的.def文件,触摸屏驱动中PDD
层的sources文件就设置了这一项。
13) INCLUDES
指定额外的要搜索的头文件,只有在要包含的头文件是私有的(private)的情况下才使用此宏,因为标准的共用的头文件被自动包含进来。
14) WINCEOEM
设置该值,表示需要使用系统下一些共用的输入库(import library)和头文件,此宏一般是系统级的部分来设置,比如是BSP包中的一些驱动。
15) WINCEMAP
用于指定在编译过程中生成相应的.map文件,默认的情况下此宏设置为1,此.map文件对于debug很有帮助。
Sources文件中还有一些其他的宏变量,详细请参考MSDN中的help文档