CZEMacLeod / MSBuild.SDK.SystemWeb

This MSBuild SDK is designed to allow for the easy creation and use of SDK (shortform) projects targeting ASP.NET 4.x using System.Web.
MIT License
161 stars 10 forks source link

Wrong target order when using both `MvcBuildViews=true` and `DeployOnBuild=true` MSBuild properties? #82

Open julealgon opened 1 month ago

julealgon commented 1 month ago

I'm playing around with switching our deployment process from a "raw" file copy of build outputs, to a more proper "publish"-based approach, where we first publish to a folder and then copy those contents instead. One of the things I want to tap into with this change is the native XML transform step, which we currently cannot use as it only runs on publish.

However, I noticed that when I try to publish in Release mode, the contents of the bin/obj/Package folder (the standard place where publish outputs are placed) is empty.

I'm running the following simple commandline:

msbuild /p:Configuration=Release /p:DeployOnBuild=true

I noticed that if I executed the exact same command, but using Debug configuration, the files were output just fine.

After analyzing the logs, I noticed that the publish results were being cleaned by the aspnet compilation target, which appears to be running after the publishing step. This of course is triggered by the MvcBuildViews flag, which is true by default only in Release mode.

At first glance, this makes little sense to me: shouldn't aspnet compilation be happening before packaging, so that the extra compiled bits are included in the publish package?

Here is a slightly redacted version of the MSBuild logs from one of our affected projects, starting after the CoreCompile target:

CoreCompile:
Skipping target "CoreCompile" because all output files are up-to-date with respect to the input files.
_CopyAppConfigFile:
Skipping target "_CopyAppConfigFile" because all output files are up-to-date with respect to the input files.
CopyFilesToOutputDirectory:
  ...
SetRoslynCompilerFiles:
  Using Roslyn from 'C:\Users\JulianoLealGoncalves\.nuget\packages\microsoft.codedom.providers.dotnetcompilerplatform\4
  .1.0\tools\roslyn-4.1.0' folder
ValidateGlobalPackageSetting:
  $(PackageAsSingleFile) is True
  $(PackageFileName) is ... Validating...
CollectFilesFromIntermediateAssembly:
  Gather all files from Project items @(IntermediateAssembly). Adding:
  ...
CollectFilesFromContent:
  Gather all files from Project items @(Content). Adding:
  Web.config;Global.asax;Default.aspx;Ping.aspx;V2\Default.aspx
CollectFilesFromIntermediateSatelliteAssembliesWithTargetPath:
  Gather all files from Project output (IntermediateSatelliteAssembliesWithTargetPath). Adding:
CollectFilesFromReference:
  Gather all files from Project items @(ReferenceCopyLocalPaths,ReferenceComWrappersToCopyLocal,ResolvedIsolatedComModu
  les,_DeploymentLooseManifestFile,NativeReferenceFile).
CollectFilesFromAllExtraReferenceFiles:
  Gather all files from Project items @(AllExtraReferenceFiles). Adding:
CollectFilesFrom_binDeployableAssemblies:
  Gather all files from Project items @(_binDeployableAssemblies). Adding:
PipelineCollectFilesPhase:
  Publish Pipeline Collect Files Phase
PreTransformWebConfig:
  Found The following for Config tranformation:
  Web.config
  ...
TransformWebConfigCore:
Skipping target "TransformWebConfigCore" because all output files are up-to-date with respect to the input files.
PostTransformWebConfig:
  Transformed Web.config using ... into obj\Release\Tran
  sformWebConfig\transformed\Web.config.
PipelineTransformPhase:
  Publish Pipeline Transform Phase
PreAutoParameterizationWebConfigConnectionStrings:
  Skip copying obj\Release\TransformWebConfig\transformed\Web.config to obj\Release\CSAutoParameterize\original\Web.con
  fig, File obj\Release\CSAutoParameterize\original\Web.config is up to date
AutoParameterizationWebConfigConnectionStringsCore:
Skipping target "AutoParameterizationWebConfigConnectionStringsCore" because all output files are up-to-date with respe
ct to the input files.
PostAutoParameterizationWebConfigConnectionStrings:
  Auto ConnectionString Transformed obj\Release\TransformWebConfig\transformed\Web.config into obj\Release\CSAutoParame
  terize\transformed\Web.config.
PipelineMsdeploySpecificTransformPhase:
  Publish Pipeline Deploy phase Stage PipelineMsdeploySpecificTransformPhase
CopyAllFilesToSingleFolderForMsdeploy:
  ...
PipelineCopyAllFilesToOneFolderForMsdeploy:
  Publish Pipeline Deploy phase Stage PipelineCopyAllFilesToOneFolderForMsdeploy
Package:
  Invoking Web Deploy to generate the package with the following settings:
  $(LocalIisVersion) is 10
  $(DestinationIisVersion) is 10
  $(UseIis) is False
  $(IncludeIisSettings) is False
  $(_DeploymentUseIis) is False
  $(DestinationUseIis) is False
GetMSDeployInstalledVersionPath:
  $(_DefaultMSDeployMaxVersion) is 3
  $(_MSDeployVersionsToTry) is 9.0
  $(MSDeployPath) is C:\Program Files\IIS\Microsoft Web Deploy V3\
GenerateMsdeployManifestFiles:
  Generate source manifest file for Web Deploy package/publish ...
PackageUsingManifest:
  Packaging into ...
  Starting Web deployment task from source: manifest(...) to Destination: package(...).
  Successfully executed Web deployment task.
  ...
GenerateSampleDeployScript:
  ...
PipelineDeployPhase:
  Publish Pipeline Deploy Phase
CleanupForBuildMvcViews:
  Deleting file "obj\Release\TransformWebConfig\transformed\Web.config".
  Deleting file "obj\Release\TransformWebConfig\original\Web.config".
  Deleting file "obj\Release\TransformWebConfig\assist\Web.config".
  Deleting file "obj\Release\CSAutoParameterize\transformed\Web.config".
  Deleting file "obj\Release\CSAutoParameterize\original\Web.config".
  Deleting file "obj\Release\Package\PackageTmp\V2\Default.aspx".
  Deleting file "obj\Release\Package\PackageTmp\bin\roslyn\csc.exe".
  ...
MvcBuildViews:
Done Building Project ... (default targets).

The package contents are generated in the PackageUsingManifest target, but right after that, CleanupForBuildMvcViews is executed and deletes all the files it generates.

I found a similar problem described here:

But in that case, the person had the MvcBuildViews target declared in their own project, which is not the case with this SDK, where that target (AFAIK) is part of the SDK itself.

Am I missing something obvious here, or is there an issue in the SDK that should be fixed so that MvcBuildViews (aspnet compiler dynamic compilation) takes place before the standard publishing targets do?

Is this SDK supposed to work with the DeployOnBuild MSBuild flag? Or should I actually be looking into a different deployment approach altogether?

CZEMacLeod commented 1 month ago

Hmm - I've not encountered this myself. There are some examples of how to publish manually or with CI/CD in https://github.com/CZEMacLeod/MSBuild.SDK.SystemWeb/issues/21#issuecomment-1478474537

You might be able to fix the ordering you are talking about with

<PropertyGroup>
  <PipelineCollectFilesPhaseDependsOn>
    $(PipelineCollectFilesPhaseDependsOn);
    MvcBuildViews;
  </PipelineCollectFilesPhaseDependsOn>
</PropertyGroup>

If this fixes things, then I would think that a PR to add this to the SDK would be in order.

You might also try

msbuild /p:Configuration=Release /p:DeployOnBuild=true /p:PrecompileBeforePublish=true /p:MvcBuildViews=false
julealgon commented 1 month ago

I realized after looking a bit further into this that we don't really need to have the MvcBuildViews flag at the point of publish, as we are already building the project in a previous step with that flag enabled (by default).

So I worked around this by adding <MvcBuildViews>false</MvcBuildViews> to my publish profile.

You might also try

msbuild /p:Configuration=Release /p:DeployOnBuild=true /p:PrecompileBeforePublish=true /p:MvcBuildViews=false

As I understand it, PrecompileBeforePublish is a different behavior, that optimizes for first access on the server. We are not (yet) using this flag on our deployment, but I'll definitely look into it.

For now, the only reason we've been using the standard MvcBuildViews was for PR validation purposes (so it would fail if any additional error was detected on views after dynamic compilation).

leusbj commented 1 month ago

@julealgon

You do understand correctly about the PrecompileBeforePublish and related properties will perform an AspNet Compilation immediately before and include that output into the publish artifacts. Once you are using the precompile option having the MvcBuildViews disabled is even preferred because running both would just run the AspNet Compiler twice.

If you decide that while you are investigating the PrecompileBeforePublish you would like to retain the ability to fail the build process on any AspNet Runtime Compilation errors, you can alter your publish profile to set the "destination" of the publish output to anywhere outside the project directory (the name of this property changes between Folder and WebDeploy profiles so I'm not including it).