Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NET.Sdk build runs unexpectedly #5486

Open
gojimmypi opened this issue Jul 4, 2020 · 7 comments
Open

NET.Sdk build runs unexpectedly #5486

gojimmypi opened this issue Jul 4, 2020 · 7 comments
Labels
author-responded Author responded, needs response from dev team. Visual Studio Issues relevant to Visual Studio scenarios
Milestone

Comments

@gojimmypi
Copy link

gojimmypi commented Jul 4, 2020

*edit: Issue title changed to "NET.Sdk build runs unexpectedly". In short, a project file as tiny as this:

<Project Sdk="Microsoft.NET.Sdk"  InitialTargets="InitFPGA">
	
	<Target Name="InitFPGA">
		<Exec Command="echo wow > init.log">
		</Exec>
	</Target>

</Project>

Seems to run automatically not only at Visual Studio startup time, but also gets triggered when the init.log file is manually deleted in File Explorer.

There's no build output, and no other indication that it runs. (other than the init.log that keeps re-appearing)

Edit 2:

The tinyproject mentioned above is not at all happy in VS2017, maxing out CPU with msbuild running continuously:

image

Original issue text:

This is a simplified custom msbuild project file from a larger one that targets building an FPGA in a WSL process. I'm using the newer SDK project files after being unable to see the long-running process from WSL in realtime, as mentioned by @rainersigwald in #5451 (comment). Note the somewhat wonky thing I am doing to switch the platform back to anycpu just before the main CoreCompile target. That's because I want to be able to do this for the build configuration management:

image

Ideally I'd like to omit the CoreCompile target altogether (it really does not like the custom platforms, of course)... but perhaps at some point the main C# app will contain some sort of diagnostic app.

The problem I am seeing is that files created by the build do not stay deleted after the clean process, nor even a manual del from the DOS prompt.

Steps to reproduce

Open the deletePersist project file in the deletePersist directory of this repo :

git clone https://github.com/gojimmypi/msbuildCustomTask.git

Then build and clean in Visual Studio 2019:

  1. Open solution file in Visual Studio.
  2. Right click on project: Build
  3. Right click on project: Clean
  4. Observe another.bit and top.json files re-appear after having been removed.
  5. Note output screen with warning that the "dir top.json" exited with code 1. showing the file was gone.
  6. Wait a few moments and the files reappear.

Project file:

<Project Sdk="Microsoft.NET.Sdk">
	<!--
  For tips on editing this file, see https://docs.microsoft.com/en-us/visualstudio/msbuild/target-build-order?view=vs-2019

  see: https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-extend-the-visual-studio-build-process?view=vs-2019
  "Be sure to use different names than the predefined targets listed in the table in the previous section 
  (for example, we named the custom build target here CustomAfterBuild, not AfterBuild), since those predefined 
  targets are overridden by the SDK import which also defines them. You don't see the import of the target file 
  that overrides those targets, but it is implicitly added to the end of the project file when you use the Sdk 
  attribute method of referencing an SDK."
-->
	<ItemGroup>
		<None Include="Properties\AssemblyInfo.cs" />
	</ItemGroup>

	<ItemGroup>
		<Compile Remove="Properties\AssemblyInfo.cs" />
	</ItemGroup>

	<ItemGroup>
		<None Remove="ulx3s.bit" />
	</ItemGroup>

	<PropertyGroup>
		<TargetFramework>net46</TargetFramework>
		<RootNamespace>VerilogLanguage</RootNamespace>
		<Platforms>ULX3S ECP5-12K;ULX3S ECP5-45K;ULX3S ECP5-85K;iCEBreaker</Platforms>
		<Configurations>Debug;Release;Upload;Verify</Configurations>
	</PropertyGroup>


	<PropertyGroup Condition=" '$(Platform)' == 'iCEBreaker' ">
		<PlatformTarget>iCE40</PlatformTarget>
	</PropertyGroup>

	<!-- each $(Configuration)|$(Platform) pair needs its own PropertyGroup to appease Project - Properties - Build - Output Path -->
	<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|iCEBreaker'">
		<OutputPath>bin\</OutputPath>
	</PropertyGroup>

	<Target Name="CustomClean" AfterTargets="Clean">
		<Message Text="Cleaning Verilog Files" />
		<RemoveDir Directories="$(OutputPath)" />
		<Delete Files="top.json"></Delete>
		<Delete Files="another.bit"></Delete>
		<Delete Files="other.json"></Delete> <!-- if this file is manually created, outside of visual studio - it will stay gone when deleted -->
	</Target>

	<!-- 
	*******************************************************************************************************************************
	iCEBeeaker
	******************************************************************************************************************************* -->
	<!-- Build Debug iCEBreaker-->
	<Target Name="Debug_iCEBreaker" Inputs="top_icebreaker.v" Outputs="top_icebreaker.bin" BeforeTargets="CoreCompile" Condition=" '$(Configuration)|$(Platform)'=='Debug|iCEBreaker' ">
		<Message Text="Build_iCEBreaker $(Configuration)|$(Platform)" />
		<Exec Command="echo wow > top.json" YieldDuringToolExecution="True" ConsoleToMSBuild="true" StandardOutputImportance="high">
			<Output TaskParameter="ConsoleOutput" ItemName="OutputOfExec" />
		</Exec>
		<Exec Command="echo wow > another.bit" YieldDuringToolExecution="True" ConsoleToMSBuild="true" StandardOutputImportance="high">
			<Output TaskParameter="ConsoleOutput" ItemName="OutputOfExec" />
		</Exec>
		<Message Text="Done creating files!" />
	</Target>


	<!-- Clean iCEBreaker-->
	<Target Name="Clean_iCEBreaker" AfterTargets="Clean">
		<Message Text="Clean_iCEBreaker $(Configuration)|$(Platform)" />
		<Exec Command="dir top.json" ContinueOnError="true" YieldDuringToolExecution="True" ConsoleToMSBuild="true" StandardOutputImportance="high">
			<Output TaskParameter="ConsoleOutput" ItemName="OutputOfExec" />
		</Exec>
		<Message Text="Done cleaning files!" />
	</Target>


	<!-- For the main C# compile, we need to switch platform -->
	<Target Name="SwitchToAnyCpu" BeforeTargets="CoreCompile" AfterTargets="Build_iCEBreaker;Upload_iCEBreaker;Build_ULX3S_85K">
		<Message Text="Platform=$(Platform)" />
		<Message Text="PlatformTarget=$(PlatformName)" />
		<PropertyGroup>
			<!-- we'll first save the current values for later use -->
			<MyStartupPlatform>$(Platform)</MyStartupPlatform>
			<MyStartupPlatformTarget>$(PlatformTarget)</MyStartupPlatformTarget>

			<!-- now we'll switch to anycpu-->
			<Platform>anycpu</Platform>
			<PlatformTarget>anycpu</PlatformTarget>
		</PropertyGroup>
		<Message Text="Platform=$(Platform)" />
		<Message Text="PlatformTarget=$(PlatformTarget)" />
	</Target>

	<!-- after the core compile, switch back to the seleted FPGA -->
	<Target Name="SwitchToMyStartupPlatform" AfterTargets="CoreCompile">
		<Message Text="Running SwitchToMyStartupPlatform..." />
		<PropertyGroup>
			<Platform>$(MyStartupPlatform)</Platform>
			<PlatformTarget>$(MyStartupPlatformTarget)</PlatformTarget>
		</PropertyGroup>
		<Message Text="Platform=$(Platform)" />
		<Message Text="PlatformTarget=$(PlatformTarget)" />
	</Target>
</Project>

Directory contents:

.>dir /s /b
.\deletePersist.csproj
.\deletePersist.sln
.\Program.cs
.\Properties
.\README.md
.\Properties\AssemblyInfo.cs
.\Properties\PublishProfiles
.\Properties\PublishProfiles\FolderProfile.pubxml

Expected behavior

Deleted files stay deleted.

Actual behavior

Deleted files created during build reappear. Manually created files such as other.json created with copy con: other.json at the DOS prompt will however, be deleted and stay deleted with the Clean process.

Note how the build output shows the file missing just before completion as shown by the warning for dir top.json:

1>  Using "Exec" task from assembly "Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".
1>  Task "Exec"
1>    dir top.json
1>     Volume in drive C is Windows
1>    File Not Found
1>     Volume Serial Number is 9078-2015
1>
1>     Directory of C:\temp\msbuildCustomTask\msbuildCustomTask\deletePersist
1>
1>    C:\temp\msbuildCustomTask\msbuildCustomTask\deletePersist\deletePersist.csproj(69,3): warning MSB3073: The command "dir top.json" exited with code 1.
1>    The previous error was converted to a warning because the task was called with ContinueOnError=true.
1>    Build continuing because "ContinueOnError" on the task "Exec" is set to "true".
1>  Done executing task "Exec" -- FAILED.
1>  Task "Message"
1>    Done cleaning files!
1>  Done executing task "Message".
1>Done building target "Clean_iCEBreaker" in project "deletePersist.csproj".
1>Target CleanXsdCodeGen:
1>  Task "Delete"
1>  Done executing task "Delete".
1>Target "_CleanPackageFiles" skipped, due to false condition; ('$(GeneratePackageOnBuild)' == 'true') was evaluated as ('false' == 'true').
1>
1>Done building project "deletePersist.csproj".
1>
1>Build succeeded.
1>
1>C:\temp\msbuildCustomTask\msbuildCustomTask\deletePersist\deletePersist.csproj(69,3): warning MSB3073: The command "dir top.json" exited with code 1.
1>    1 Warning(s)
1>    0 Error(s)
1>
1>Time Elapsed 00:00:00.31

Yet, a few moments later the top.json and another.bit files will reappear.

Environment data

msbuild /version output:

**********************************************************************
** Visual Studio 2019 Developer Command Prompt v16.6.1
** Copyright (c) 2020 Microsoft Corporation
**********************************************************************

C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise>msbuild /version
Microsoft (R) Build Engine version 16.6.0+5ff7b0c9e for .NET Framework
Copyright (C) Microsoft Corporation. All rights reserved.

16.6.0.22303

OS info:

Microsoft Windows [Version 10.0.18363.900]

If applicable, version of the tool that invokes MSBuild (Visual Studio, dotnet CLI, etc):

Microsoft Visual Studio Enterprise 2017 
Version 15.9.13
VisualStudio.15.Release/15.9.13+28307.718
Microsoft .NET Framework
Version 4.8.03752

Installed Version: Enterprise

Plus this abbreviated list that may be of interest:

Microsoft Continuous Delivery Tools for Visual Studio   0.4
Simplifying the configuration of Azure DevOps pipelines from within the Visual Studio IDE.

Snapshot Debugging Extension   1.0
Snapshot Debugging Visual Studio Extension Detailed Info

Visual Studio Tools for CMake   1.0
Visual Studio Tools for CMake

Visual Studio Tools for Universal Windows Apps   15.0.28307.718
The Visual Studio Tools for Universal Windows apps allow you to build a single universal app experience that can reach every device running Windows 10: phone, tablet, PC, and more. It includes the Microsoft Windows 10 Software Development Kit.
@gojimmypi gojimmypi changed the title Files do not stay deleted after clean or delete in NET.Sdk project NET.Sdk build runs unexpectedly Jul 5, 2020
@rainersigwald
Copy link
Member

Because you have specified the new target as an InitialTargets, it runs for any build of any target in the project. That includes design-time builds that Visual Studio uses to extract project information from the project to do internal operations like "load the project" and "update the project sidebar". Those builds can run at any time.

Consider hooking a more-specific target that won't be called as often, or if that's not possible, modify your target for design-time builds.

@rainersigwald rainersigwald added the Visual Studio Issues relevant to Visual Studio scenarios label Jul 8, 2020
@rainersigwald rainersigwald modified the milestones: Backlog, Discussion Jul 8, 2020
@gojimmypi
Copy link
Author

@rainersigwald thanks for the feedback and the design-time build links... certainly an interesting read. It does have information key to resolving this. Still, I think the documentation and/or defaults are a bit misleading and perhaps undesired:

Even removing the InitialTargets and the project build runs relentlessly.

I was hopeful that the DesignTimeBuild property would be the answer to this problem. The default is documented as being a blank but is apparently true despite the note in the example:

image

I'm trying to run tasks before the CoreCompile, which is not listed as one of the design-time targets:

Whether a target is run in design-time builds is based on whether a target's BeforeTargets and AfterTargets attributes specifies a direct or indirect dependency of any of the above targets.

(although perhaps CoreCompile is also a design-time trigger like Compile ?)

For example: this project file will create an init.log file with a value of wow true immediately upon just opening the project in VS:

<Project Sdk="Microsoft.NET.Sdk">

	<PropertyGroup>
		<TargetFramework>netcoreapp3.1</TargetFramework>
	</PropertyGroup>
	
	<Target Name="RelentlessCommand" BeforeTargets="CoreCompile">
		<Exec Command="echo wow $(DesignTimeBuild) > init.log">
		</Exec>
	</Target>
	
</Project>

so ok, the default is true. Let's set it to false:

<Project Sdk="Microsoft.NET.Sdk">

	<PropertyGroup>
		<TargetFramework>netcoreapp3.1</TargetFramework>
		<DesignTimeBuild>false</DesignTimeBuild>
	</PropertyGroup>
	
	<Target Name="RelentlessCommand" BeforeTargets="CoreCompile">
		<Exec Command="echo wow $(DesignTimeBuild) > init.log">
		</Exec>
	</Target>
	
</Project>

Delete the init.log file. It automatically comes back, showing the build is still running - and still contains the value wow true.

Still, given all of this, the workaround to determine whether a target is running in a design-time build does work, and this project does not run relentlessly:

<Project Sdk="Microsoft.NET.Sdk">

	<PropertyGroup>
		<TargetFramework>netcoreapp3.1</TargetFramework>
	</PropertyGroup>

	<Target Name="AddAdditionalReferences" BeforeTargets="ResolveAssemblyReferences">
		<PropertyGroup Condition="'$(DesignTimeBuild)' == 'true' OR '$(BuildingProject)' != 'true'">
			<_AvoidExpensiveCalculation>true</_AvoidExpensiveCalculation>
		</PropertyGroup>
	</Target>	
	
	<Target Name="RelentlessCommand" BeforeTargets="CoreCompile" Condition=" $(_AvoidExpensiveCalculation)=='' " >
		<Exec Command="echo wow $(DesignTimeBuild) > init.log">
		</Exec>
	</Target>
	
</Project>

The init.log does not automatically come back after deleting, as desired. Whew! Cool. :)

For reference, the reason I need to run the FPGA synthesis build before the CoreCompile is that I have custom Platforms:

	<PropertyGroup>
		<TargetFramework>net46</TargetFramework>
		<RootNamespace>VerilogLanguage</RootNamespace>
		<Platforms>ULX3S ECP5-12K;ULX3S ECP5-45K;ULX3S ECP5-85K;iCEBreaker;Orange Crab</Platforms>
		<Configurations>Debug;Release;Upload;Verify</Configurations>
	</PropertyGroup>

and these of course break the normal msbuild, so I switch to anycpu just before the compile:

	<!-- For the main C# compile, we need to switch platform -->
	<Target Name="SwitchToAnyCpu" BeforeTargets="CoreCompile">
		<Message Text="Platform=$(Platform)" />
		<Message Text="PlatformTarget=$(PlatformName)" />
		<PropertyGroup>
			<!-- we'll first save the current values for later use -->
			<MyStartupPlatform>$(Platform)</MyStartupPlatform>
			<MyStartupPlatformTarget>$(PlatformTarget)</MyStartupPlatformTarget>
			
			<!-- now we'll switch to anycpu-->
			<Platform>anycpu</Platform>
			<PlatformTarget>anycpu</PlatformTarget>
		</PropertyGroup>
		<Message Text="Platform=$(Platform)" />
		<Message Text="PlatformTarget=$(PlatformTarget)" />
	</Target>

Also, note that I do not have a View > Other Windows > Build Logging menu item as noted here.

image

Microsoft Visual Studio Enterprise 2019
Version 16.6.1
VisualStudio.16.Release/16.6.1+30128.74
Microsoft .NET Framework
Version 4.8.03752

Installed Version: Enterprise

@rainersigwald
Copy link
Member

so ok, the default is true.

The default is empty. When loading a project in Visual Studio, the project system does a design-time build, with the property set to true. When building on the command line or in Visual Studio, the property is not set and is empty.

Let's set it to false

Your example here doesn't do that. It changes the default value in that project to false, but when the project system explicitly sets DesignTimeBuild=true as a global property, it overrides that line in your project file. That is why you need to do the build-time check.

Also, note that I do not have a View > Other Windows > Build Logging menu item as noted here.

Did you follow step 1 there?

  1. Install the Project System Tools extension

@AR-May AR-May added the triaged label Feb 21, 2024
@maridematte
Copy link
Contributor

Hi @gojimmypi, is this still an issue that you are facing?

@maridematte maridematte added the needs-more-info Issues that need more info to continue investigation. label Nov 5, 2024
@gojimmypi
Copy link
Author

Hi @maridematte yes, there seems to still be undesired behavior.

In VS2022 17.11.5, the tiny project file now gives an error at load time:

Image

It is arguable whether a project file should refuse to load if there's no configuration. That's probably off topic.

I updated the project like this with a configuration to appease the above error message:

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  
  <ItemGroup>
    <ProjectConfiguration Include="Debug|x64">
      <Configuration>Debug</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|x64">
      <Configuration>Release</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
  </ItemGroup>

  <Target Name="InitFPGA">
    <Exec Command="echo wow > init.log" />
  </Target>

</Project>

It appears the build still attempt to run at load time. I also now see this error message repeated 20 times at project load time:

error : Designtime build failed for project 'C:\test\vs_build\project.vcxproj' configuration 'Debug|x64'. IntelliSense might be unavailable.
	Set environment variable TRACEDESIGNTIME = true and restart Visual Studio to investigate.

I'd like to be able to load a project, ideally a custom build project, without the build running immediately at load time.

I must have missed the "Project System Tools" suggestion extension; is that still advised / required?

Thank you.

@dotnet-policy-service dotnet-policy-service bot added author-responded Author responded, needs response from dev team. and removed triaged needs-more-info Issues that need more info to continue investigation. labels Nov 5, 2024
@maridematte
Copy link
Contributor

The design time build when VS opens / loads a project is a feature that we do not plan on disabling anytime soon from my understanding.
For a disabling the DesignTimeBuild have you tried Rainer's suggestions for doing a build-time check?

For the " Project System Tools" suggestion, if you're using a new version of VS there are some new steps that you can follow and get the output results of the design time build and check if the variable.

@maridematte maridematte added needs-more-info Issues that need more info to continue investigation. and removed author-responded Author responded, needs response from dev team. labels Nov 11, 2024
@gojimmypi
Copy link
Author

Hi @maridematte and thank you for the information.

Coincidentally, I'm working on a project that also has some interesting "design time build" issues. In this case, I'm using cmake-generated Visual Studio projects to add support for wolfSSL encryption to realm-core using Visual Studio 2022.

I added this code snippet into the VS2022/ALL_BUILD.vcxproj project:

  <Target Name="AddAdditionalReferences" BeforeTargets="ResolveAssemblyReferences">
    <PropertyGroup Condition="'$(DesignTimeBuild)' == 'true' OR '$(BuildingProject)' != 'true'">
        <_AvoidExpensiveCalculation>true</_AvoidExpensiveCalculation>
        <Message>Design Time Build!</Message>
    </PropertyGroup>
  </Target>

The curious thing here is that the entire project file seems to be regenerated when the underlying cmake files are detected as having changed. The problem of course, is that when the file is regenerated, the hand-crafted design-time code is lost.

Although still in the general topic of builds running unexpectedly, I realize this is also quite a bit different than my original example. Still, I was wondering if you'd happen to know how to add a project Condition="'$(DesignTimeBuild)' == 'true' for cmake files that generate Visual Studio project files?

For reference, there's some CMake projects in Visual Studio information, but the design-time (or project load, or project refresh-time) is not mentioned.

I'd like to be able to detect the initial reload of the project and take certain additional steps.

I'll still be revisiting the non-cmake builds soon.

Thanks again, cheers.

@dotnet-policy-service dotnet-policy-service bot added author-responded Author responded, needs response from dev team. and removed triaged needs-more-info Issues that need more info to continue investigation. labels Nov 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
author-responded Author responded, needs response from dev team. Visual Studio Issues relevant to Visual Studio scenarios
Projects
None yet
Development

No branches or pull requests

4 participants