關於如何將模組名新增到程式集的ModuleName說簡單吧也簡單,說不簡單吧也不簡單。
簡單的原因是程式碼只有幾行,不簡單的原因是這些都不是c#,都是MSbuild的程式碼。這可真難為我了,所以這個地方我卡了兩個星期。
首先我們來看下解決方案的目錄:
都知道這些資料夾都是解決方案資料夾,但是build解決方案資料夾裡放的是什麼,居然不是專案?不是專案如何呼叫呢,這可觸及我的知識盲點。經過多方查閱資料和博問上提問,我重於搞清楚了。
首先,這兩個.yml檔案刪了也無所謂,我第一次見到yml檔案還是在docker的配置上,也就是一個跟我們常用的json一樣的配置檔案(用縮排代替json裡面那堆括號,有人覺得這樣比較方便)。開始我還以為是通過其它程式靠yml配置檔案注入模組,被誤導了好久,這兩個配置檔案就是持續整合的配置檔案(沒進過大廠不懂這些被浪費好久),也就是專案釋出到github會自動幫你build等等。
其次,就是剩下的檔案是靠Directory.Build.props和Directory.Build.targets呼叫到專案裡的。這就是我的第二個知識盲點了。我們開啟資料夾(直接開啟資源管理器的,不是visual studio的),可以發現src原始碼資料夾下的OrchardCore、OrchardCore.Modules和OrchardCore.Themes三個資料夾都有Directory.Build.props和Directory.Build.targets這兩個檔案。這意味著還有檔案不在專案裡?這又怎麼用呢?我真是快崩潰了!經過查詢資料,重要明白:在MSBuild15以後新增一個功能就是讓開發者可以自己定義專案資訊放在一個檔案,這個檔案會在Microsoft.Common.props和Microsoft.Common.target引用,而且會在csproj專案檔案所在的資料夾開始尋找,只要找到存在Directory.Build.props和Directory.Build.target檔案就會自動匯入裡面的內容。也就是從專案的csproj當前專案開始自動向上尋找Directory.Build.props和Directory.Build.target,直到解決方法根目錄,只要找到立即停止。這就是意味著每個專案編譯的時候會把這些檔案給包括進來。
好了,明白了這些後,開始按順序瞭解模組的載入過程。
我是從啟動專案OrchardCore.Cms.Web開始看,它專案引用了OrchardCore.Application.Cms.Core.Targets,而這個專案又引用了OrchardCore.Application.Targets,這下Targets專案都在OrchardCore資料夾下,也就是編譯的時候會包含上面說的Directory.Build.props和Directory.Build.targets檔案。
OrchardCore.Application.Targets裡面只有一個OrchardCore.Application.Targets.targets檔案,開啟如下:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <!-- This file is packaged with "OrchardCore.Application.Targets.nupkg" in "./build" such that any Application that references it will embed in its assembly a list of the referenced modules. --> <Target Name="ResolveModuleProjectReferences" AfterTargets="AfterResolveReferences"> <MSBuild Targets="GetModuleProjectName" BuildInParallel="$(BuildInParallel)" Projects="@(_MSBuildProjectReferenceExistent)" Condition="'@(_MSBuildProjectReferenceExistent)' != ''" SkipNonexistentTargets="true" ContinueOnError="true"> <Output ItemName="ModuleProjectNames" TaskParameter="TargetOutputs" /> </MSBuild> <ItemGroup> <ModuleNames Include="@(ModulePackageNames);@(ModuleProjectNames)" /> </ItemGroup> <ItemGroup> <AssemblyAttribute Include="OrchardCore.Modules.Manifest.ModuleNameAttribute" Condition="'@(ModuleNames)' != ''"> <_Parameter1>%(ModuleNames.Identity)</_Parameter1> </AssemblyAttribute> </ItemGroup> </Target> <Target Name="NoWarnOnRazorViewImportedTypeConflicts" BeforeTargets="RazorCoreCompile"> <PropertyGroup> <NoWarn>$(NoWarn);0436</NoWarn> </PropertyGroup> </Target> </Project>
看到AssemblyAttribute元素了吧,沒錯,這就是程式集的屬性啊,在看看它裡面有啥?OrchardCore.Modules.Manifest.ModuleNameAttribute,沒錯,這個就是上篇反射找到的ModuleName,那麼怎麼來的呢,有個Condition="'@(ModuleNames)' != ''",明顯這個條件就是ModuleNames元素不為空,然後獲取ModuleNames元素的Identity。那麼我們順著往上看就可以看到上個ItemGroup元素裡面就包含ModuleNames,但是沒有Identity屬性啊,只能查閱資料了,誰叫我MSbuild兩眼一抹黑呢,根據msdn介紹明白Identity就是Include屬性中指定的項,大家也可以看看msdn。那麼我們接著看INclude,@(ModulePackageNames);@(ModuleProjectNames),分號間隔符,間隔而已,繼續看看第二個ModuleProjectNames,上面的MSBuid的Oupt就是了,而任務呢,就是MSBuild的GetModuleProjectName。這個任務在哪呢,感覺很無厘頭是不是,不對,前面不是介紹了Directory.Build.props和Directory.Build.targets嗎,沒錯,往裡面找,開啟Directory.Build.targets看看
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="..\OrchardCore.Build\OrchardCore.Commons.targets" /> <Import Project="..\OrchardCore\OrchardCore.Module.Targets\OrchardCore.Module.Targets.targets" /> <PropertyGroup> <AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder> </PropertyGroup> </Project>
看到裡面包含了的OrchardCore.Module.Targets.targets檔案沒有,開啟接著看(這個有點長直接看最後部分):
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> ... <Target Name="GetModuleProjectName" Returns="@(ModuleProjectName)"> <ItemGroup> <ModuleProjectName Include="$(AssemblyName)" /> </ItemGroup> </Target> </Project>
看到Name為GetModuleProjectName的Target任務了吧,請注意這個檔案的位置,在..\OrchardCore\OrchardCore.Module.Targets\OrchardCore.Module.Targets.targets,沒錯就在OrchardCore.Module.Targets專案裡,而所有模組都是以用這個專案,也就是說所有應用這個專案的專案名都會加到程式集的ModuleName上!