这是关于将第三方Tools和库集成到 Visual Studio 系列中的第四篇文章。在第一篇文章中我说明了怎么创立 Visual Studio 属性对话框的自定义属性页。第二篇文章涵盖了属性表的外部构造和元素。第三篇文章经过构建 Boost 库的例子说明了怎么创立自定义构建。本文是第四篇,我将说明怎么集成自定义构建到 Visual Studio 项目标援用系统。
每一个 C++ 项目由几个较小的子项目和库构成。它们在编译或运转时被用于链接,需求被恰当地援用。假如一切的项目都是在 Visual Studio(MSBuild)中被创立的,那末援用是由 MSBuild 来担任。但当一个项目或库来自内部时,我们不能不经过手动设置装备摆设来恰当地集成。
其它翻译版本 (1) 加载中幻想状况下,我们应当能经过在 Visual Studio 中添加对某个项目标援用来将其集成到 MSBuild 中:
假如我们对任何库都能这么做,那不是很好吗?假如一切的 lib 文件会主动添加到 LINK 号令,同时一切的 DLL 文件都复制到输出目次,那末他们不就能够在运转时被链接上吗?假如既能调试我们的代码,还能调试库的代码,那又会如何呢?
在这篇文章中,我将通知你这该怎么做到。我会用库来演示怎么将它集成就任何项目,却无需手动操作库或设置途径。我假定你曾经晓得该怎么构建 Boost,不晓得的话就请读这篇文章。
当 Visual Studio 从一个项目添加援用到另外一个项目时,它会像下面如许将一笔记录添加到主项目中:
<ProjectReference Include="...oost.vcxproj"> <Project>{9cd23c68-ba74-4c50-924f-2a609c25b7a0}</Project> ... </ProjectReference>
关于援用是怎么被添加的具体信息,拜见这个链接。
在构建主项目标过程当中,MSBuild 会对 ProjectReference 段中列出的一切依靠实行剖析和构建。它会定位列出的子项目,并经过对每一个子项目挪用以下 Target 来搜集需要的信息:
GetTargetPath GetNativeManifest GetResolvedLinkLibs GetCopyToOutputDirectoryItems
我将简要地说明它们辨别做了甚么。
这个目的(Target)前往项目构建的顺序集/库的完好途径。在设计阶段,Visual Studio运用这个文件来判别援用能否准确,和能否可以找到输出文件。假如顺序集是托管类型,Visual Studio也会查询它以获得更多的信息。
实际上讲,只需这个途径指向曾经存在的文件,援用系统都会正常陈述援用是有效的。
关于Boost库而言,没有单一的库文件。它根据设置装备摆设,构建恣意数目的库文件,或许基本就不构建库文件。我们可使用这些来实行援用校验。我们可以前往指向恣意文件的途径,来标明援用是有效的。我决议前往文件Jamroot的途径,用来标明,本次构建是运用哪一个源代码来创立的库文件:
<Target Name="GetTargetPath" Returns="@(TargetPath)" > <ItemGroup> <TargetPath Include="$(BoostRoot)Jamroot"> <Private>true</Private> <FileType>info</FileType> <ResolveableAssembly>false</ResolveableAssembly> </TargetPath> </ItemGroup> </Target>
它需求在项目(Item)上设置如上所示的一些元数据( metadata)属性。FileType凡是包括如lib或dll为拓展名的文件,因为在这里不实用,所以我前往了假的类型。ResolveableAssembly标明,它是托管顺序集或许是原生的。Private包括了当地复制(Local Copy)设置。
假如因为某种缘由,子项目必需从头宣布Manifest文件和库文件,这个目的(Target)会前往manifest文件的列表信息。父工程会容易的拷贝这些manifest文件到输出目次。
Boost无需任何manifest文件,所以它不必做任何设置:
<Target Name="GetNativeManifest" />
这个目的(Target)前往一切链接库的列表信息。它们将会添加到LINK号令,如许这些lib文件就能够链接了。Boost库针对它创立的每一个模块都有一个lib文件。
对我们来讲,要前往准确的列表信息,起首要获得创立的库文件的列表信息,然后链接到实践的lib文件。我们需求完成两个步调:
运用以后选项 和 --show-libraries 号令挪用b2(GetBuiltLibs)
处置链接库的援用,并将它们参加前往列表(GetResolvedLinkLibs)
<Target Name="GetBuiltLibs" DependsOnTargets="BuildJamTool" Returns="@(BuiltLibs)" > <Exec Command="b2.exe @(boost-options, ' ') --show-libraries" ... /> <ReadLinesFromFile Condition="Exists('$(TempFile)')" File="$(TempFile)"> <Output TaskParameter="Lines" ItemName="RawOutput" /> </ReadLinesFromFile> <Delete Condition="Exists('$(TempFile)')" Files="$(TempFile)"/> <ItemGroup> <BuiltLibs Include="$([Regex]::Match(%(RawOutput.Identity), (?<=-s)(.*) ))" /> </ItemGroup> </Target>
请留意:为了明晰起见,文中一切的示例代码都做了简化处置。
<Target Name="GetResolvedLinkLibs" DependsOnTargets="GetBuiltLibs" Returns="@(LibFullPath)"> <ItemGroup> <LibFullPath Include="$(OutputDir)lib*boost*%(BuiltLibs.Identity)*.lib"> <ProjectType>StaticLibrary</ProjectType> <FileType>lib</FileType> <ResolveableAssembly>false</ResolveableAssembly> </LibFullPath> </ItemGroup> </Target>
当库列表信息前往后,我们简直不必为每一个项目设置元属性。
这个目的(target)前往内容文件的列表信息,这些文件需求拷贝到主工程的输出目次。 它们可所以恣意类型的文件。关于Boost库来讲,它们是构建过程当中创立的一切dll文件。我们运用,与之前一样的算法,来列出这些文件:
<Target Name="GetCopyToOutputDirectoryItems" DependsOnTargets="GetBuiltLibs" Returns="@(DLLToCopy)" Condition="'$(boost-link)'=='DynamicLibrary'" > <ItemGroup> <BoostDlls Include="$(OutputDir)lib*boost*%(BuiltLibs.Identity)*.dll" /> <DLLToCopy Include="@(BoostDlls)" Condition="'%(BoostDlls.Identity)'!=''" > <TargetPath>%(FileName).dll</TargetPath> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </DLLToCopy> </ItemGroup> </Target>
在上面的代码中,每一个项目需求设置两个元数据(Metadata)属性:TargetPath和CopyToOutputDirectory。
TargetPath包括文件名和拓展名。当拷贝到目的文件夹,相似如许:$(DestinationFolder)$(TargetPath)的时分,它被用来指定文件名。
CopyToOutputDirectory包括两个可能的值:Always和PreserveNewest,此中之一。
它告诉构建系统,要末老是拷贝文件,要末只拷贝源文件比目的文件新的文件。
对Boost库来讲,假如是最新的,就无需拷贝DLL文件。
如今,假如我们将boost工程作为援用添加出去,它将会注册为有效的,同时供给父工程在准确构建过程当中可能需求的一切信息。
我们Start用一个十分原始的名字(Sample)来创立一个容易把持台使用顺序。鉴于每一个人都晓得怎么在 Visual Studio 中创立一个把持台使用顺序,我将跳过相干的步调阐明。
将 boost 项目添加至处理计划。
前去 Sample 项目标属性页,添加对 boost 项目标援用。你会看到像如许的界面:
如图所示,boost 项目已被准确地援用并指向 Boost 库安装的 D:Boost 目次。因为 Boost 不是一个托管顺序集,顺序集标识(Assembly Name)、区域性(Culture)、版本号(Version)、描绘(Description)都不成用。
值得留意的是,Copy Local 属性用来断定库能否要被复制到援用它的项目标输出目次。假如子项陌生成的是托管顺序集或只是一个lib 文件,那是不会出问题的。但假如子项陌生成的是原生 DLL 或多个库的话,全部进程就会中断。我们经过从头定义 GetCopyToOutputDirectoryItems
来修复它。我们如今要来把持能否将 DLL 复制到主项目标输出目次,那就需求向 Boost 属性页的惯例选项卡添加额定的属性:
将这个属性设置为 No 可以禁用复制。这个设定只在 Boost 库是以同享的方法被构建时才起用处,对生成静态库是无效的。
每当我们构建的时分,b2
会反省设置装备摆设并决议它能否要构建组件的一部分。当 Boost 被用来开发其他项目时,它自身不大会发作甚么变化。所以反省能否发作变化根本上是过剩的。我曾经在属性页的惯例选项卡中增加了一个禁用这类反省的选项:
当这个选项是 Yes 或许空缺时,对从头构建的反省会被委派给 Visual Studio。它反省时会比对输出库的列表、已设置装备摆设库的列表和项目文件自身。如有任何库被删除或项目设置发作变卦,它便会实行构建。不然它会跳过构建,使得每次构建的耗时节俭大约半分钟。要从头启用这个反省,请将此选项设为 No。
将这些反省委派给 Visual Studio 需具有以下要素:
经过测试输出号令:b2 --show-libraries,可以揣度出构建的库列表。一旦我们有了列表,经过挪用Target GetBoostOutputs来验证库中所出现的工具。
<Target Name="GetBoostOutputs" DependsOnTargets="GetBuiltLibs" Returns="@(BoostOutputs)" > <ItemGroup> <BoostOutputs Include="$(OutputDir)lib*boost*%(BuiltLibs.Identity)*.lib" > <Library>%(BuiltLibs.Identity)</Library> </BoostOutputs> <ExistingLibs Include="%(BoostOutputs.Library)" /> <BoostOutputs Include="@(BuiltLibs)" Exclude="@(ExistingLibs)" Condition="'@(ExistingLibs->Count())'!='@(BuiltLibs->Count())'" /> <BoostOutputs Include="%(BoostOutputs.RootDir)%(BoostOutputs.Directory)%(BoostOutputs.Filename).dll" Condition="'@(BoostOutputs0>Filename->StartsWith("boost_"))'=='true' And '%(BoostOutputs.Library)'!='' And '$(boost-link)'=='DynamicLibrary'" /> </ItemGroup> </Target>
正如你上面所看到的那样,我们从GetBuiltLibs目的中失掉了库的列表,并且查找到一切*boost*<library-name>*.lib模样的lib文件。既前往了包括静态链接库也前往了包括了静态库。
下一步我们将在构建的库文件的列表上创立外部衔接,运用它来查找遗漏的库。
紧接着,我们添加遗漏的库到BoostOutputs为了在需求是运用。
然后我们添加静态链接库。
列表将由Build Target来断定能否需求履行反省。
本文中的一切译文仅用于进修和交换目标,转载请务必注明文章译者、出处、和本文链接。 2KB翻译任务按照 CC 协议,假如我们的任务有进犯到您的权益,请实时联络我们。2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务