build: split dual-role exes (AssetCompiler, ConnectionRouter) into lib/ reference + tools/ runnable

AssetCompiler and ConnectionRouter are referenced in-process (Stride.Editor, GameStudio,
Presentation) AND run as a tool/service. Packing the whole runnable app (apphost + resolver
bootstrap dlls) into lib/ made consumers inherit those dlls and exposed the apphost as a duplicate
of the managed assembly (found as both .exe and .dll), which blocks deps.json generation.

Add a gated StridePackRunnableInTools option to the shared NuGet-resolver targets: it packs the
runnable app under tools/<tfm>/ instead of the build-output folder (lib/), leaving lib/<tfm>/ with
just the reference assembly. Default behaviour (root apps) is unchanged. Opt both in. AssetCompiler's
StrideCompileAssetCommand locator is repointed lib/->tools/; ConnectionRouter needs no locator change
(RouterHelper and the router's service resolution find the exe by package file search).
This commit is contained in:
Virgile Bello
2026-06-25 13:45:12 +09:00
parent 08f93ed5aa
commit 14d3bed506
4 changed files with 58 additions and 17 deletions
@@ -2,6 +2,10 @@
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'Directory.Build.props'))/sdk/Stride.Build.Sdk.Editor/Sdk/Sdk.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<!-- Referenced in-process (Stride.Editor) AND run as a tool: pack the runnable app under tools/<tfm>/
and keep lib/<tfm>/ to just the reference assembly, so consumers don't inherit the apphost
(a duplicate of the managed assembly) or the resolver bootstrap dlls. -->
<StridePackRunnableInTools>true</StridePackRunnableInTools>
<StrideAssemblyProcessor>true</StrideAssemblyProcessor>
<TargetFrameworks>$(StrideXplatEditorTargetFramework)</TargetFrameworks>
<TargetFrameworks Condition="$(StridePlatforms.Contains('Windows'))">$(StrideFrameworkWindows);$(TargetFrameworks)</TargetFrameworks>
@@ -143,9 +143,10 @@
and dev-redirect consumers (seeded from $(StrideDevRoot) by the build/<PkgId>.props
stub written by build/GenerateDevPackages.cs). -->
<StrideCompileAssetCommand Condition="'$(StrideCompileAssetCommand)' == '' And '$(StrideRoot)' != ''">$(StrideRoot)sources\assets\Stride.AssetCompiler\bin\$(Configuration)\net10.0\Stride.AssetCompiler.dll</StrideCompileAssetCommand>
<!-- NuGet consumer: lib/<TFM>/ relative to this targets file's package root. -->
<StrideCompileAssetCommand Condition="'$(StrideCompileAssetCommand)' == '' And Exists('$(MSBuildThisFileDirectory)..\lib\net10.0-windows7.0\') And $(TargetFramework.Contains('-windows'))">$(MSBuildThisFileDirectory)..\lib\net10.0-windows7.0\Stride.AssetCompiler.dll</StrideCompileAssetCommand>
<StrideCompileAssetCommand Condition="'$(StrideCompileAssetCommand)' == ''">$(MSBuildThisFileDirectory)..\lib\net10.0\Stride.AssetCompiler.dll</StrideCompileAssetCommand>
<!-- NuGet consumer: the runnable app ships under tools/<TFM>/ (lib/ holds only the reference
assembly), relative to this targets file's package root. -->
<StrideCompileAssetCommand Condition="'$(StrideCompileAssetCommand)' == '' And Exists('$(MSBuildThisFileDirectory)..\tools\net10.0-windows7.0\') And $(TargetFramework.Contains('-windows'))">$(MSBuildThisFileDirectory)..\tools\net10.0-windows7.0\Stride.AssetCompiler.dll</StrideCompileAssetCommand>
<StrideCompileAssetCommand Condition="'$(StrideCompileAssetCommand)' == ''">$(MSBuildThisFileDirectory)..\tools\net10.0\Stride.AssetCompiler.dll</StrideCompileAssetCommand>
</PropertyGroup>
<Error Condition="!Exists('$(StrideCompileAssetCommand)')" Text="Stride AssetCompiler could not be found (Command: &quot;$(StrideCompileAssetCommand)&quot;)"/>
@@ -12,10 +12,15 @@
<ProjectReference Condition="'$(StrideNuGetResolverUI)' != 'true'" Include="$(MSBuildThisFileDirectory)..\Stride.NuGetResolver\Stride.NuGetResolver.csproj" PrivateAssets="all" />
<ProjectReference Condition="'$(StrideNuGetResolverUI)' == 'true'" Include="$(MSBuildThisFileDirectory)..\Stride.NuGetResolver.UI\Stride.NuGetResolver.UI.csproj" PrivateAssets="all" />
</ItemGroup>
<!-- Some targets so that referencing projects include a tools folder with executables-->
<!-- Ship the NuGet-resolver bootstrap set (and, for executables, the apphost) so the runtime resolver
can start. By default these go into the package build-output folder (lib/<tfm>/). A project that is
both referenced as a library and run as a tool sets StridePackRunnableInTools=true to instead place
the whole runnable app under tools/<tfm>/, leaving lib/<tfm>/ with just the reference assembly so
consumers don't inherit the bootstrap dlls (or see the apphost duplicate the managed assembly). -->
<PropertyGroup>
<TargetsForTfmSpecificBuildOutput>_StrideIncludeNuGetResolver;$(TargetsForTfmSpecificBuildOutput)</TargetsForTfmSpecificBuildOutput>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.config;.exe</AllowedOutputExtensionsInPackageBuildOutputFolder>
<TargetsForTfmSpecificBuildOutput Condition="'$(StridePackRunnableInTools)' != 'true'">_StrideIncludeNuGetResolver;$(TargetsForTfmSpecificBuildOutput)</TargetsForTfmSpecificBuildOutput>
<TargetsForTfmSpecificContentInPackage Condition="'$(StridePackRunnableInTools)' == 'true'">_StrideIncludeNuGetResolverInTools;$(TargetsForTfmSpecificContentInPackage)</TargetsForTfmSpecificContentInPackage>
<AllowedOutputExtensionsInPackageBuildOutputFolder Condition="'$(StridePackRunnableInTools)' != 'true'">$(AllowedOutputExtensionsInPackageBuildOutputFolder);.config;.exe</AllowedOutputExtensionsInPackageBuildOutputFolder>
</PropertyGroup>
<Target Name="NuGetResolverModuleInitializerGenerate" BeforeTargets="BeforeCompile;CoreCompile" DependsOnTargets="PrepareForBuild">
<PropertyGroup>
@@ -30,19 +35,46 @@
<Compile Include="$(NuGetResolverModuleInitializerFile)" />
</ItemGroup>
</Target>
<Target Name="_StrideIncludeNuGetResolver">
<!-- The resolver bootstrap set (and, for executables, the apphost) produced in the output folder. -->
<Target Name="_StrideCollectNuGetResolverFiles">
<ItemGroup>
<BuildOutputInPackage Condition="Exists('$(OutputPath)$(AssemblyName).exe')" Include="$(OutputPath)$(AssemblyName).exe" />
<BuildOutputInPackage Include="$(OutputPath)NuGet*.dll" />
<BuildOutputInPackage Include="$(OutputPath)Newtonsoft.Json.dll" />
<BuildOutputInPackage Include="$(OutputPath)Microsoft.Extensions.FileProviders.Abstractions.dll" />
<BuildOutputInPackage Include="$(OutputPath)Microsoft.Extensions.FileSystemGlobbing.dll" />
<BuildOutputInPackage Include="$(OutputPath)Microsoft.Extensions.Primitives.dll" />
<BuildOutputInPackage Include="$(OutputPath)Stride.NuGetResolver*.dll" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == '$(StrideXplatEditorTargetFramework)' ">
<_StrideNuGetResolverFile Condition="Exists('$(OutputPath)$(AssemblyName).exe')" Include="$(OutputPath)$(AssemblyName).exe" />
<_StrideNuGetResolverFile Include="$(OutputPath)NuGet*.dll" />
<_StrideNuGetResolverFile Include="$(OutputPath)Newtonsoft.Json.dll" />
<_StrideNuGetResolverFile Include="$(OutputPath)Microsoft.Extensions.FileProviders.Abstractions.dll" />
<_StrideNuGetResolverFile Include="$(OutputPath)Microsoft.Extensions.FileSystemGlobbing.dll" />
<_StrideNuGetResolverFile Include="$(OutputPath)Microsoft.Extensions.Primitives.dll" />
<_StrideNuGetResolverFile Include="$(OutputPath)Stride.NuGetResolver*.dll" />
<!-- Needed by NuGet.Packaging in cross platform builds (see https://github.com/stride3d/stride/issues/2232) -->
<BuildOutputInPackage Include="$(OutputPath)System.Security.Cryptography.Pkcs.dll" />
<_StrideNuGetResolverFile Condition=" '$(TargetFramework)' == '$(StrideXplatEditorTargetFramework)' " Include="$(OutputPath)System.Security.Cryptography.Pkcs.dll" />
</ItemGroup>
</Target>
<!-- Default: bootstrap set (and apphost) into the package build-output folder (lib/<tfm>/). -->
<Target Name="_StrideIncludeNuGetResolver" DependsOnTargets="_StrideCollectNuGetResolverFiles">
<ItemGroup>
<BuildOutputInPackage Include="@(_StrideNuGetResolverFile)" />
</ItemGroup>
</Target>
<!-- StridePackRunnableInTools: the full runnable app (apphost + bootstrap + entry assembly +
runtimeconfig + app.config) under tools/<tfm>/, leaving lib/<tfm>/ with just the reference
assembly so consumers don't inherit the bootstrap dlls. -->
<Target Name="_StrideIncludeNuGetResolverInTools" DependsOnTargets="_StrideCollectNuGetResolverFiles">
<PropertyGroup>
<!-- Mirror NuGet's lib folder name, which appends the default platform version
(net10.0-windows -> net10.0-windows7.0), so locators resolve the same way. -->
<_StrideToolsTfm>$(TargetFramework)</_StrideToolsTfm>
<_StrideToolsTfm Condition="'$(TargetPlatformVersion)' != '' and !$(TargetFramework.EndsWith($(TargetPlatformVersion)))">$(TargetFramework)$(TargetPlatformVersion)</_StrideToolsTfm>
</PropertyGroup>
<ItemGroup>
<_StrideToolFile Include="@(_StrideNuGetResolverFile)" />
<_StrideToolFile Include="$(OutputPath)$(AssemblyName).dll" />
<_StrideToolFile Condition="Exists('$(OutputPath)$(AssemblyName).runtimeconfig.json')" Include="$(OutputPath)$(AssemblyName).runtimeconfig.json" />
<_StrideToolFile Condition="Exists('$(OutputPath)$(AssemblyName).dll.config')" Include="$(OutputPath)$(AssemblyName).dll.config" />
<TfmSpecificPackageFile Include="@(_StrideToolFile)">
<PackagePath>tools/$(_StrideToolsTfm)/%(Filename)%(Extension)</PackagePath>
</TfmSpecificPackageFile>
</ItemGroup>
</Target>
</Project>
@@ -3,6 +3,10 @@
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>$(StrideEditorTargetFramework)</TargetFramework>
<!-- Referenced in-process (GameStudio, Presentation) AND run as a service: pack the runnable app under
tools/<tfm>/ and keep lib/<tfm>/ to just the reference assembly, so consumers don't inherit the
apphost or the resolver bootstrap dlls. The router exe is located by package file search. -->
<StridePackRunnableInTools>true</StridePackRunnableInTools>
<StrideAssemblyProcessor>true</StrideAssemblyProcessor>
<StrideAssemblyProcessorOptions>--auto-module-initializer</StrideAssemblyProcessorOptions>
<StrideNuGetResolverUI>true</StrideNuGetResolverUI>