$(ProjectDir) prebuild event macro incorrect - visual-studio

I am using Visual Studio 2017 and I have a pre-build event.
I have a project located at C:\Project\src\Project\, with a batch file doSomething.bat at C:\Project\src\Project\doSomething.bat.
In my pre-build event, I want to run doSomething.bat, so I have this script:
cd $(ProjectDir) && call ./doSomething.bat
However, $(ProjectDir) actually places me in C:\Project\src\Project\bin\Debug\netcoreapp1.1\
This is not what the documentation states. How can I fix this?

For SDK-based projects PreBuildEvent and PostBuildEvent are evaluated too early to receive "final" values for a lot of properties, they are even considered to be deprecated (see https://github.com/dotnet/project-system/issues/1569)
As a quick workaround, you can use $(MSBuildProjectDirectory) or $(MSBuildThisFileDirectory) which will give you the directory that the .csproj file is in.
For a more integrated solution, you can add a custom target to the csproj file like this (no cd step needed):
<Target Name="MyAfterBuild" AfterTargets="Build">
<Exec Command="test.bat" />
</Target>

There is a workaround on how to make it resolve common macros and properties correctly (discussed on the thread, mentioned by #MartinUllrich).
See an example on the link. The key points of the solution is to modify the .csproj file so:
The root tag stays without attributes: just <Project> rather than <Project Sdk="Microsoft.NET.Sdk">
Import Sdk.props at the beginning: <Import Sdk="Microsoft.NET.Sdk.Web" Project="Sdk.props" />
Import Sdk.targets before pre-, post- build events: <Import Sdk="Microsoft.NET.Sdk.Web" Project="Sdk.targets" />

I had this happen after I'd manually edited the project file.
I'd inadvertently moved this line:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
to below the <PropertyGroup><PreBuildEvent> ... elements from its original position above them.
Try this:
Open your project file, eg MyProject.csproj (or MyProject.vbproj).
Find <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> (or <Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />).
Move it to directly above the beginning of your build event elements (<PropertyGroup><PreBuildEvent> ...)
Clean the project/solution, recompile and you should be good.
Note: I also had another project that had this error out of the box so I think it can "just happen" as well.

Related

MSBuild - want to get the output assembly

I have a .targets file in a folder named .pack I have this:
<PropertyGroup>
<TaskAssembly>$(OutputPath)netstandard2.1\Test.dll</TaskAssembly>
</PropertyGroup>
Why instead of MyProject\bin\Debug\netstandard2.1\MyProject.dll it locates like the below line?
MyProject\.pack\bin\Debug\netstandard2.1\MyProject.dll
Why .pack is there!?
Then wanted to use it with a Using task
<UsingTask
TaskName="brand.ProBuild.Tasks.TestFunction"
AssemblyFile="$(TaskAssembly)"
/>
Defined as inline address, played with slashes, cleared bin/obj, restarted, don't why it can't understand some addresses.
Defined and used several path variables in my targets files, some working correctly and some are troublesome especially when want to use parents or some problems with slashes '/', don't know maybe some addresses are working randomly. But what is wrong with the $(OutputPath) ?!
Visual-studio 2019, .Net Standard 2.1 (It has multiple targets I want to get that specific dll)
You should check in your main project, before the import node like <Import Project=".pack\xxx.targets" />, check whether you defined the outputpath property again like
<outputpath>.pack\bin\Debug\</outputpath>
Suggestion
From your description, you created a custom MSBuild task dll to use its new custom task in another project, first, please make sure that the Test.dll is in the output folder of your project called MyProject.
Then, check whether you have redefined the outputpath before the import xml node.
Like this:
<PropertyGroup>
<outputpath>.pack\bin\Debug\</outputpath>
</PropertyGroup>
..........
<Import Project=".pack\xxx.targets" />
........
<UsingTask
TaskName="brand.ProBuild.Tasks.TestFunction"
AssemblyFile="$(TaskAssembly)"
/>
If so, you should change OutputPath to bin\Debug\.
In addition, if it does not help you, please share the xxx.csproj of project MyProject with us so that we can troubleshoot your issue more quickly.
Update 1
Since you have only one targets file in your project, I suggest you could follow these suggestions:
1) close VS Instance, enter your project folder, delete the .vs hidden folder under the solution folder, bin and obj folder. Then ,restart your project to test again.
2) you can define the correct value in the xxx.csproj file before the imports xml node to force the correct value of outputPath.
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<OutputPath>bin\Debug\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<OutputPath>bin\Release\</OutputPath>
</PropertyGroup>
.......
<Import Project="xxx.targets"/>

VS 2017 builds code redundantly when switching from msbuild console build over to IDE (the first time)

VS 2017 sets LastActiveSolutionConfig for a web application on its project load, thus triggering subsequent build of that project, because:
The property is set in the respective .csproj.user file, which is created, if needed.
The .csproj.user file is part of project dependencies
So by creating it, VS causes the project to be built the next time.
Imagine building it all on the command line with msbuild after cleaning up the workspace, then switching back to VS and hitting the build button. And it is building again!
So, there are these stupid auto generated CS files related to workflows, that are generated only by VS, not msbuild (TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs and friends) - our msbuild generates them on purpose to satisfy VS. Check.
Next we ensure all the Copy to Output Directory files use PreserveNewest - check.
I already forgot what else we had to do to make sure VS does not rebuild code redundantly when switching from msbuild to IDE. Now this one, which is new.
How can I prevent VS 2017 from adding this property? Is it absolutely necessary to have it?
In the mean-time, I will modify the .csproj files by adding it and see if it helps. Nobody builds Release locally at our place and it is always AnyCPU platform, so I do not care about other configurations, except Debug|AnyCPU.
This is what I would do:
Set the build verbosity to Diagnostic.
Build and look for where the msbuild file is located that generates this file:
TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs
Look for some conditions that you can alter to prevent the file from being
generated.
Set some property to alter the condition and prevent that file from being generated.
My solution is to generate the .csproj.user files if needed with the expected property. Which is incredibly annoying that one has to do it. Here is the build code that can go into your Directory.Build.Targets:
<Target Name="EnsureCSProjUserForWebApplications"
Condition="'$(IsWebApplication)' == True And !Exists('$(MSBuildProjectFullPath).user')">
<ItemGroup>
<CSProjUserContent Include="<?xml version="1.0" encoding="utf-8"?>" />
<CSProjUserContent Include="<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">" />
<CSProjUserContent Include="<PropertyGroup>" />
<CSProjUserContent Include="<LastActiveSolutionConfig>Debug|Any CPU</LastActiveSolutionConfig>" />
<CSProjUserContent Include="</PropertyGroup>" />
<CSProjUserContent Include="</Project>" />
</ItemGroup>
<WriteLinesToFile File="$(MSBuildProjectFullPath).user" Lines="#(CSProjUserContent)" ContinueOnError="true" />
</Target>
IsWebApplication is computed like this:
<IsWebApplication>$(ProjectTypeGuids.Contains('349c5851-65df-11da-9384-00065b846f21'))</IsWebApplication>
Finally, the target is part of a larger series of targets that run at the beginning and validate the csproj matches our requirements or ensure certain conditions:
<PropertyGroup>
<EnsureXyzProjectSettingsDependsOn>
AssertIISExpress;
AssertNoAssemblyInfo;
AssertDebugSymbols;
AssertLocalApplicationHostFile;
AssertImportsDFVersioning;
EnsureSharedBinLink;
AssertSharedBinOutputPath;
AssertHintPaths;
EnsureCSProjUserForWebApplications
</EnsureXyzProjectSettingsDependsOn>
</PropertyGroup>
<Target Name="EnsureXyzProjectSettings"
DependsOnTargets="$(EnsureXyzProjectSettingsDependsOn)"
BeforeTargets="BeforeBuild"
Condition="'$(SuppressStrictXyzTargetsChecks)' != true" />

How do I use msbuild in Visual Studio's post build event?

I'm trying to configure the YUICompressor.NET in my Visual Studio project.
As I've understood, I have to create a .proj file and add it to my solution. After that, I need to create a post-build event that will build this .proj file and I'll get my desired output (minified js/css files).
So, I have:
This .proj contains:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/MsBuild/2003">
<UsingTask TaskName="CssCompressorTask" AssemblyFile="\..\packages\YUICompressor.NET.MSBuild.2.7.0.0\lib\NET20\Yahoo.Yui.Compressor.Build.MsBuild.dll" />
<UsingTask TaskName="JavaScriptCompressorTask" AssemblyFile="\..\packages\YUICompressor.NET.MSBuild.2.7.0.0\lib\NET20\Yahoo.Yui.Compressor.Build.MsBuild.dll" />
<Target Name="Minify">
<ItemGroup>
<!-- Single files, listed in order of dependency -->
<CssFiles Include="Content\*.css"/>
<JavaScriptFiles Include="Scripts\*.js"/>
</ItemGroup>
<CssCompressorTask
SourceFiles="#(CssFiles)"
OutputFile="Content\min.css"
/>
<JavaScriptCompressorTask
SourceFiles="#(JavaScriptFiles)"
OutputFile="Scripts\min.js"
/>
</Target>
</Project>
I'm trying to build it as the following:
I'm getting the following error:
The command "msbuild C:\Users\Me\Desktop\MvcApplicationExample\MvcApplicationExample\YuiCompressorMsBuild.proj" exited with code 9009.
This error suggests that "msbuild" is not a valid command. So, how should I build this type of project? (I've followed this tutorial: youtube)
Thanks for any help.
As you said maybe the visual studio cannot find the MSBuild command, try with the following command instead
"$(MSBuildBinPath)\msbuild.exe"
That uses the complete path to msbuild.
Update (For futures references)
As the comment by Steve Medley, you should not forget the encapsulating quotes.
vfabre is correct to use $(MSBuildBinPath)\msbuild.exe but missing one thing. you should encapsulate that in quotes as there will 99.99% of the time be a space in the file path to msbuild
Well, one thing that might be a problem is that you want it as a Post-build event but it's set for the Pre-build command line.

SlowCheetah executes after post-build events

I use SlowCheetah to transform my app.configs. I have a multi-project solution where one of the projects executes a post-build event where the output of the bin is copied elsewhere. I've found that SlowCheetah does it's transforms after the post-build event, so the app.config I'm copying is the pre-transformed version.
Does anyone have a suggestion of how I can execute my copy after the SlowCheetah transforms? Is this going to require that I write a custom build task?
If you are using msbuild 4.0 for building your projects - you can hook to slowcheetah targets with new AfterTargets BeforeTargets attributes.
I dont know what exactly target name you want to hook after but this code could gave you base concept how to do this
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Some_Target_Name" AfterTargets="TransformAllFiles" >
<Message Text="= Script here will run after SlowCheetah TransformAllFiles ="/>
</Target>
<Project>
Edited: I installed SlowCheetah and found that AfterTargets attribute should be "TransformAllFiles".
Just set up your target dependency AfterTargets="TransformAllFiles"
Alexey's answer leads to the correct solution but here it is in full:
Right-click your project and select Unload Project
Now right-click the project and select Edit [your project name].csproj
Scroll to the bottom and uncomment the target named AfterBuild and add this attribute AfterTargets="TransformAllFiles"
Move your post build actions into this target using the Exec command:
An example:
<Target Name="AfterBuild" AfterTargets="TransformAllFiles">
<Exec Command="ECHO Hello PostBuild World!" />
</Target>
I have bumped into this problem too... decided to update to latest version of SlowCheetah (current 2.5.8), and this problem appears to have been fixed! No more problems using post-build events to deploy a project with transformed XML!
After the NuGet package upgrade process, I had a strange issue, though... transforms were no longer happening. Editing the project like Naeem Sarfraz suggested, I have found that the SlowCheetah's PropertyGroup section was placed at the end of the .csproj.
It was just a matter of moving it to the top, near the other PropertyGroup sections, and now it works like a charm!
If you need to copy/move other .config files (other than web.config) around after the build before publishing here is how it can be done with Visual Studio 2013 (I didn't test it on earlier versions). This section can be added at the end of the .csproj file right before the closing tag </Project> and it'll be fired just before MSDeploy starts the Publishing process.
<Target Name="MoveConfigFile" BeforeTargets="MSDeployPublish">
<Move
SourceFiles="$(IntermediateOutputPath)Package\PackageTmp\ThirdPartyApp.config"
DestinationFolder="$(IntermediateOutputPath)Package\PackageTmp\bin"
OverwriteReadOnlyFiles="true"
/>
</Target>
The company I work for purchased a third party product that needs to have a .config files in the bin folder along with its assembly in order to work.
At the same time we need to process the product's .config file and be able to move it to the bin folder after transformations.
The $(IntermediateOutputPath)Package\PackageTmp folder contains the whole application that will be copied over the target server.

Visual Studio 2010: How to publish an ASP.NET web app to a target folder with MSBUILD?

In Visual Studio 2010, you know how you can change your configuration (debug, release, etc), right-click a project in the solution explorer, click publish, and have all the important web app project files for the selected configuration copied to a target folder along with an xdt-transformed web.config? Well, I am looking for the MSBUILD equivalent of exactly that.
My challenge to you: Provide the ONE LINE that I need to execute at my command prompt in order to accomplish this. No third party programs. No tutorial videos. Just a single, straight-up command that I can literally copy from your response, paste into a command window, modify as necessary to support my directory structure, and then hit enter.
If not, then perhaps someone could provide a link to a complete MSBUILD reference showing every command, switch, and value I can use at the command line.
Put the below to ProjectPublish.MSBuild.xml file (change PropertyGroup as needed):
<?xml version="1.0" encoding="utf-8" ?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Publish">
<PropertyGroup>
<ProjectFile>Path\To\Web.csproj</ProjectFile>
<PublishDir>Path\For\Publish\Output</PublishDir>
<TempDir>Path\To\Temp\Folder</TempDir>
<BuildConfig>Release|Debug</BuildConfig>
</PropertyGroup>
<Target Name="Publish">
<MSBuild Projects="$(ProjectFile)"
Properties="Configuration=$(BuildConfig);WebProjectOutputDir=$(PublishDir);OutDir=$(TempDir)\;BuildingProject=true"
Targets="ResolveReferences;_CopyWebApplication" />
</Target>
</Project>
Calling this from command line (or .bat file) should do the trick:
%windir%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe ProjectPublish.MSBuild.xml
I found the solution I was looking for after all these months here
In case the above link goes bad, here's the skinny of what it says:
Unload then edit your project file. Look for the line where it's importing Microsoft.WebApplication.targets. Will look like:
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
Beneath that line, paste in this XML:
<Target Name="PublishToFileSystem" DependsOnTargets="PipelinePreDeployCopyAllFilesToOneFolder">
<Error Condition="'$(PublishDestination)'==''" Text="The PublishDestination property must be set to the intended publishing destination." />
<MakeDir Condition="!Exists($(PublishDestination))" Directories="$(PublishDestination)" />
<ItemGroup>
<PublishFiles Include="$(_PackageTempDir)\**\*.*" />
</ItemGroup>
<Copy SourceFiles="#(PublishFiles)" DestinationFiles="#(PublishFiles->'$(PublishDestination)\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="True" />
</Target>
Now, run this in a command prompt within the same folder as your project file:
msbuild TestWebApp.csproj "/p:Platform=AnyCPU;Configuration=Debug;PublishDestination=C:\pub" /t:PublishToFileSystem
Remember to specify the path to MSBUILD in the command or add the path to your global path environmental variable (which is what I did). On my machine, it was here:
C:\Windows\Microsoft.NET\Framework\v4.0.30319
To test this, I put a config transform in my Web.Release.config to add an AppSetting key (if you do this, make sure the AppSettings node is present in your base config file or you will get an error). When I used the above command to build the debug configuration, the key was not present in the published config file as expected. However, when I used the release config, the key was successfully added to the file.
I really wish Microsoft hadn't obfuscated the heck out of this. At any rate, this is the simplest solution I have found anywhere on the internet. I hope it helps the rest of you.

Resources