MS的这个IDE,实在庞杂得恐怖。从大学开始,我就一直用VC的各个版本写程序至今,细细想来,也仅仅是窥豹一斑,羞愧不已。但若仔细翻阅文档,又觉得时间花得不值,于是便一直凑活地用着。
前段时间负责搭建新项目的开发环境,兼一直在看Gamebryo的代码,从中借鉴了不少工程管理的方法。结合以前使用VC的经验,现总结下来与大家分享,希望能起到抛砖引玉的作用。
VC用解决方案(solution)来表示项目,每个项目由一个或多个工程(project)组成,每个project用来管理一个相对独立的模块。有些大型的项目,例如Gamebryo,甚至会有多个solution:一个solution用来管理LIB或DLL;一个solution用来管理所有的DEMO;一个solution用来管理tools等等。拆分成多个工程,好处之一是结构清晰,二是方便并行开发。每个project由不同的程序员维护,输出LIB或DLL,再链接生成最终的EXE。项目的拆分是很有技术含量的,可以看出设计的功力。拆分得不好,就会产生一些诸如”project间相互依赖”的负面效应。
建立项目的时候,第一件要考虑的事情就是如何配置文件目录结构。如果是只有几个obj的微型项目,将所有东西全部扔到一个目录下也未尝不可(当然,这依然是最糟糕的做法);而对于包含N个project,一大堆的源代码,还有文档,资源等等的大项目,糟糕的文件管理会把事情弄成一团浆糊。优秀的目录配置可以帮助开发者迅速找到需要的东西,很容易就可以添加新的模块和代码,甚至反映出项目的基础架构…最后,好的目录结构,是充满美感的:)
下面就是本人比较喜欢的目录结构设置,实际中根据项目大小和需求有所增减:
src:存放所有的源代码
build:存放所有的工程文件(solution,project,makefile等)
doc:项目文档,doxygen文件等
sdk:工程依赖的所有库文件和头文件
out:编译器产生的中间文件,目标文件
bin:编译得到的exe和dll
上图没有列出来,但通常还会有:
res:资源目录,可执行文件需要用到的各种图像,声音,模型,场景,UI等资源文件
script:存放脚本文件
tool:各种自动化工具
稍微说明一下:一是最重要的源码要单独放一个目录,且不同模块的代码都归置到不同的子目录下。这样的好处是,别人只需要看一眼你的目录结构,就大概知道你工程是怎样的一个架构了。我讨厌所有header放一个目录,所有cpp放另一个目录的做法,想找个文件都很痛苦。
二是工程文件(vc solution,project,makefile等)单独放一个目录。工程文件的重要程度仅次于源码,无工程文件,别人要编译或浏览你的代码都很杯具。插一句,有VC Project的话,Windows下用VC看代码真的是不二之选(当然,Linux下的工具集自不必提),再次就是Editplus+Everything了。话说回来,专业一点的话,build下应该有两个子目录:MSVC和MakeFile,当然还可以有CodeBlock,CodeLite等等。VC工程的话,solution,project和property sheet也应该分开存放
然后就是静态库。每个项目都应该是自给自足的,即整个项目目录我随便copy到什么地方,都照样能编译运行。所以必须要包含所有项目依赖的第三方的库文件和相关的头文件。还有我们的project本身也可能会输出lib,也应该放在同样的目录下,这样方便设置搜索路径。
最后就是编译过程中产生的中间文件,这些我全部扔到out目录里,按"“project_name /[debug | release | shipping] /”分目录存放,一是清理方便,二是看着舒心,最近我还发现这样还能避免链接静态库时的PCH Replacing的问题。而exe和DLL我输出到bin下,Lib则输出到SDK,debug版带后缀_d,release版带后缀_r,类似地shipping版带后缀_s
接下来会具体聊一下project属性的设置…
ConfigurationName
|
配置名字,通常是Debug或者Release
|
IntDir
|
编译器使用的中间目录,产出obj文件
|
OutDir
|
链接器使用的输出目录
|
ProjectDir
|
项目目录
|
ProjectName
|
项目名字
|
SolutionDir
|
解决方案目录
|
TargetDir
|
目标输出文件所在的目录
|
TargetExt
|
目标输出的扩展名
|
TargetFileName
|
目标输出文件名,包括扩展名
|
TargetName
|
目标输出名,不包括扩展名
|
TargetPath
|
目标输出文件的全路径名
|
下面开始一个虚拟解决方案设计:
假设此解决方案有应用程序项目A,动态链接库项目B,静态链接库项目C,其中项目A依赖项目B和项目C,则构建解决方案项目结构如下图所示:
而目录结构则如下图所示:
Bin:存放所有动态链接库和可执行程序,分Debug和Release两个版本
A:应用程序项目文件夹
B:动态链接库项目文件夹
C:静态链接库项目文件夹
Doc:存放项目文档
Include:存放引用库的头文件
Lib:存放动态链接库的导入库、静态链接库
Temp:存放临时生成文件,其中Compile存放编译时的中间文件,Link存放链接时的输出文件
除了Doc需要自己建立外,其他文件夹无需手动建立。
项目结构创建步骤:
1.创建一个新应用程序项目,名称A,解决方案名称为Work,如下图所示:
2.在"解决方案资源管理器"右键"解决方案Work","添加"→"新建项目"→"Win32"→"Win32 项目",名称为B,在"应用程序设置"选择"DLL"和"空项目";
3.同样的方式添加一个"Win32项目",名称为C,在"应用程序设置"选择"静态库",去掉"预编译头"选项;
4.项目结构如下图所示:
项目属性设置
配置项目A的属性
1.配置"所有配置","常规",
输出目录:$(SolutionDir)Temp\Link\$(ProjectName)\$(ConfigurationName)
中间目录:$(SolutionDir)Temp\Compile\$(ProjectName)\$(ConfigurationName)
2."生成事件"→"生成后事件",
命令行:echo D | xcopy "$(TargetPath)" "$(SolutionDir)Bin\$(ConfigurationName)" /y
3."C/C++"→"常规",
附加包含目录:$(SolutionDir)Include
4."链接器"→"常规",
附加库目录:$(SolutionDir)Lib
5."调试",
命令:$(SolutionDir)Bin\$(ConfigurationName)\$(TargetFileName)
工作目录:$(SolutionDir)Bin\$(ConfigurationName)
配置项目B的属性
1.配置"所有配置","常规"下,
输出目录:$(SolutionDir)Temp\Link\$(ProjectName)\$(ConfigurationName)
中间目录:$(SolutionDir)Temp\Compile\$(ProjectName)\$(ConfigurationName)
2.配置"Debug","链接器"→"常规"下,
输出文件:$(SolutionDir)Temp\Link\$(ProjectName)\$(ConfigurationName)\$(ProjectName)d.dll
3.配置"所有配置","生成事件"→"生成后事件"下,
命令行:
echo D | xcopy "$(TargetPath)" "$(SolutionDir)Bin\$(ConfigurationName)" /y
echo D | xcopy "$(TargetDir)$(TargetName).lib" "$(SolutionDir)Lib" /y
echo D | xcopy "$(ProjectDir)B.h" "$(SolutionDir)Include" /y
第三行为xcopy动态库的头文件,实际要根据所需进行改动名称。
配置项目C的属性
1.配置"所有配置","常规"下,
输出目录:$(SolutionDir)Temp\Link\$(ProjectName)\$(ConfigurationName)
中间目录:$(SolutionDir)Temp\Compile\$(ProjectName)\$(ConfigurationName)
2.配置"Debug","管理员"→"常规"下,
输出文件:$(SolutionDir)Temp\Link\$(ProjectName)\$(ConfigurationName)\$(ProjectName)d.lib
3.配置"所有配置","生成事件"→"生成后事件"下,
命令行:
echo D | xcopy "$(TargetPath)" "$(SolutionDir)Lib" /y
echo D | xcopy "$(ProjectDir)C.h" "$(SolutionDir)Include" /y
第二行为xcopy静态库的头文件,实际要根据所需进行改动名称。
最后设置项目生成顺序
菜单栏→"项目"→"项目依赖项",项目A依赖于B和C,如下图所示:
编译生成,可看到如下结果:
用tree命令看下文件夹结构: