假设您有一个工程叫solar,路径结构是这样的:

公用头路径: d:\solar\public\include
公用库路径:d:\solar\public\lib
mars项目 : d:\solar\mars
uranus项目: d:\solar\uranus
工具路径 : d:\solar\tools

现在mars这个项目中要引用公用头路径和公用库路径,有以下几种方法但都不完美:

1. 用绝对路径。这个的缺点就不用说了,呵呵

2. 用相对路径:..\public\include, ..\public\lib
缺点是,如果mars这个项目的路径深度改了,这些引用都失效了,必须重新定义。有时要写..\..\..\..\blablabla好深好深,难写难记难调试。

3. 用环境变量定义%SOLAR% = d:\solar。然后%SOLAR%\public\include , %SOLAR%\public\lib。这其实和第1种办法类似。用环境变量表面上比第1种办法灵活了,但这只是个假象。首先,如果solar工程的位置变了,那么要记得改%SOLAR%这个环境变量;其次,如果你同时想编译solar的多个分支呢?只好编某个分支的时候现改环境变量。。。。

说到底,我们缺少一种有效的定位到Source Root的手段。在VS2010中,一切都简单了。先要从property说起,其实从VS2005起就支持property了,property就是一个游离成为文件形式的工程配置片断,然后可以在project文件里包含,于是project就继承了property中指定的选项。在property中支持用户定义的“宏”,也就是VC工程配置里那些形如 $(ProjectDir) 这样的VC可以理解的变量。

好了,在vs2010中,property的宏支持调用一些函数来动态计算值。比如在某个property中设置NUM: $([System.Guid]::NewGuid()),那么在包含这个property的工程中就会有一个 $(NUM),值是一个随机的GUID。VS2010的可爱之处在于,他实现了一个函数:GetDirectoryNameOfFileAbove(dir,filename),这个函数可以从传入的第一个路径dir开始,一层一层的向父目录寻找"filename"这个文件,直到找到为止,然后返回包含这个文件的路径。这个奇怪的函数有什么用呢?如果我们在solar目录下创建一个专门让孩子们找到它的特殊文件名,比如solar_is_here,那么每个工程建一个property文件包含进来,在这个property文件中定义一个宏 $(SOLAR),值是$([MSBuild]::GetDirectoryNameOfFileAbove($(ProjectDir), solar_is_here)),那么这个工程的其它配置都可以使用$(SOLAR)了,于是$(SOLAR)\public\include, $(SOLAR)\public\lib, $(SOLAR)\tools,成功的解决了所有问题。

等等,这样的话需要每个新建的工程都新建一个property文件,这多少有点麻烦。于是我们用到了VS2010的另一个可爱之处,它有一个文件%LOCALAPPDATA%\Microsoft\Msbuild\v4.0\Microsoft.Cpp.Win32.user.props,这个property会在每个新建的win32工程中缺省包含!

该总结一下了,我们使用VS2010开发一个产品(如叫SOLAR)时,在项目初期只需做两件事:
1. 在产品的根建一个特殊文件,比如叫solar_starts_here。
2. 在Microsoft.Cpp.Win32.user.props里加一个宏:SOLAR ,值是$([MSBuild]::GetDirectoryNameOfFileAbove($(ProjectDir), solar_starts_here))
以后,在项目中的工程只要是项目根的子目录,不管路径深度如何,不用额外做任何配置,即可使用$(SOLAR)这个免费的午餐来引用其它的相对路径。感谢可爱的VS2010,感谢可爱的MSBUILD 4.0,感谢CCTV:)

注:在Microsoft.Cpp.Win32.user.props里加宏的方法:随便建一工程,在菜单上 view->property manager,Debug|Win32下有一个叫Micaosoft.Cpp.Win32.User,右键属性,出来设置页,左边有一个User Macros,点开后在右边"Add Macro",填入要写的宏名字和值就可以了。