I am trying to extend the build functionality with VS2017. I've added following code to the bottom of the proj file, just before </Project> tag:
<Target Name="PrintA" BeforeTargets="Build">
<Message Text="AAAAAAAAAAAAAa" Importance="high" />
</Target>
That works as expected. Now I would like to create separated targets file - build.targets and add the build logic there. So, I've created build.targets file in the solution root folder with following code:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="PrintB" BeforeTargets="Build">
<Message Text="BBBBBBBBBBBBBBBBBBBBB" Importance="high"/>
</Target>
</Project>
And then added import to the proj file also:
Then I build the project the AAAAAA is printed and BBBBB is not. What do I miss?
UPDATED: This is the build log(I am unable to paste full log, I think that only this info can be relevant):
Task "WriteLinesToFile" skipped, due to false condition; ('#(_CleanUnfilteredPriorFileWrites)'!='#(_CleanUniqueRemainingFileWritesAfterIncrementalClean)') was evaluated as ('C:\Users\kultasev\source\repos\Database2\Database2\bin\Debug\Database2.dacpac;C:\Users\kultasev\source\repos\Database2\Database2\bin\Debug\Database2.dll;C:\Users\kultasev\source\repos\Database2\Database2\bin\Debug\Database2.pdb;C:\Users\kultasev\source\repos\Database2\Database2\obj\Debug\Database2.dll;C:\Users\kultasev\source\repos\Database2\Database2\obj\Debug\Database2.pdb'!='C:\Users\kultasev\source\repos\Database2\Database2\bin\Debug\Database2.dacpac;C:\Users\kultasev\source\repos\Database2\Database2\bin\Debug\Database2.dll;C:\Users\kultasev\source\repos\Database2\Database2\bin\Debug\Database2.pdb;C:\Users\kultasev\source\repos\Database2\Database2\obj\Debug\Database2.dll;C:\Users\kultasev\source\repos\Database2\Database2\obj\Debug\Database2.pdb').
Target "PostBuildEvent" skipped, due to false condition; ('$(PostBuildEvent)' != '' and ('$(RunPostBuildEvent)' != 'OnOutputUpdated' or '$(_AssemblyTimestampBeforeCompile)' != '$(_AssemblyTimestampAfterCompile)')) was evaluated as ('' != '' and ('' != 'OnOutputUpdated' or '' != '')).
Target "AfterBuild" in file "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.targets" from project "C:\Users\kultasev\source\repos\Database2\Database2\Database2.sqlproj" (target "Build" depends on it):
Target "_CheckForInvalidConfigurationAndPlatform" skipped. Previously built successfully.
Target "_CheckForInvalidConfigurationAndPlatform" skipped. Previously built successfully.
Target "PrintA" in project "C:\Users\kultasev\source\repos\Database2\Database2\Database2.sqlproj" (target "Build" depends on it):
Task "Message"
Task Parameter:Text=AAAAAAAAAAAAAa
Task Parameter:Importance=high
AAAAAAAAAAAAAa
Target "PrintB" in file "C:\Users\kultasev\source\repos\Database2\Database2\build.targets" from project "C:\Users\kultasev\source\repos\Database2\Database2\Database2.sqlproj" (target "Build" depends on it):
Task "Message"
Task Parameter:Text=BBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBB
Target "Build" in file "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\VisualStudio\v15.0\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets" from project "C:\Users\kultasev\source\repos\Database2\Database2\Database2.sqlproj" (entry point):
Task "CallTarget" skipped, due to false condition; ('$(UnloadProjectsOnCompletion)'=='true') was evaluated as (''=='true').
Task "CallTarget" skipped, due to false condition; ('$(UnloadProjectsOnCompletion)'=='true') was evaluated as (''=='true').
Task "CallTarget" skipped, due to false condition; ('$(UnloadProjectsOnCompletion)'=='true') was evaluated as (''=='true').
Target "CleanupEmptyRefsFolder" in file "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\VisualStudio\Managed.Web\Microsoft.Web.IISSupport.targets" from project "C:\Users\kultasev\source\repos\Database2\Database2\Database2.sqlproj" (entry point):
Set Property: _RefsFolderFullPath=C:\Users\kultasev\source\repos\Database2\Database2\bin\Debug\
Added Item(s):
_FilesInRefsFolder=
C:\Users\kultasev\source\repos\Database2\Database2\bin\Debug\\Database2.dacpac
C:\Users\kultasev\source\repos\Database2\Database2\bin\Debug\\Database2.dll
C:\Users\kultasev\source\repos\Database2\Database2\bin\Debug\\Database2.pdb
Task "RemoveDir" skipped, due to false condition; (#(_FilesInRefsFolder ->Count()) == 0) was evaluated as (3 == 0).
Done building project "Database2.sqlproj".
Note that the classic project system in VS 2017 doesn't reload imported files during builds even if they change. You need to at least close and re-open the solution to make it work.
For "SDK-style" projects (.NET Standard/Core, ASP.NET Core) in VS 2017 15.3+, this is handled by the new project system (GitHub issue).
Related
I want to add a custom target in my .csproj file.
The custom target will be :
msbuild /t:assembleDebug
This target will just build with the Debug mode.
I have already try to modify the .csproj file but no success.
Thanks for your answers.
This target will just build with the Debug mode. I have already try to modify the .csproj file but no success.
To accomplish this, unload your project. Then at the very end of the project, just before the end-tag </Project>, place below scripts:
<Target Name="assembleDebug" Condition=" '$(Configuration)' == 'Debug' ">
<Message Text="This custom target will only be executed when configuration is debug" Importance="high"></Message>
</Target>
With the condition '$(Configuration)' == 'Debug', the target will only be executed when configuration is Debug:
When you build it with the Release mode, this custom target will not be executed:
msbuild "CustomTarget.csproj" /p:Configuration=Release /t:build;assembleDebug
Update for comment:
Can I just call : msbuild /t:assembleDebug and this target will do the
same thing as msbuild /p:Configuration=Debug
/p:Configuration=Release is used to overwrite the default configuration on Visual Studio, if you just want call : msbuild /t:assembleDebug and this target will do the same thing as msbuild /p:Configuration=Debug, you should set the default configuration to Debug, then build it with the command msbuild /t:assembleDebug:
Then build it with that command line:
Hope this helps.
I have a document-level customisation associated with an excel template (.xlt) application. The solution includes a postAction class that drops a copy of the template in the default location for excel User Templates (Application.TemplatesPath).
There is a manual process that needs to be done, post publishing, in order to make this work, which I have automated in a powershell script.
After I publish the solution...
I want to run the script to integrate the postAction and re-sign the manifests.
Is there any way to do this?
I figured it out eventually.
First I had to understand the configuration splatter around MSBuild...
Configuration splatter
The .csproj file pulls together the configuration XML that drives MSBuild.
This includes things called Targets which are executable chunks of buildness
Targets are not executed in the order they are parsed. In fact they are not executed at all unless they are somehow tied to the Predefined Targets of MSBuild.
Targets are tied to the build process using their AfterTargets, BeforeTargets and DependsOnTargets optional attributes.
There are loads of predefined values available, including Targets environment variables and key filenames and paths, which are oozing out of the config splatter.
The csproj file has Import elements that bring in a lot of predefined elements and metadata from system .target and .props files.
You can make your own .target files and import them as well. They have the same execution context as the importing file.
There is a special Targets file that takes care of the VSTO build and publishing process: $(VSToolsPath)\OfficeTools\Microsoft.VisualStudio.Tools.Office.targets and you can see the publishing Targets in there.
The Target that I need is BeforePublish. If I turn up the build output to "detailed" I can see that the only thing happening after this point is that the distribution files are moved around from a temp, build location and then to the publishing folder. Along the way, the manifests are updated to reflect changes to the paths before being re-signed. So I chained my custom post-publish Target off of that via it's AfterTargets attribute and took advantage of the convenience of the temp build location inside the project directory structure.
In my command line version of the post-action I operated in the publishing folder location so I adjusted it to find the files in the temp location.
Final steps of the publish build
To take a look around, I added this markup at the end of the .csproj file (inside the Project tag).
<Target Name="AfterPublish">
<Message Text="After Publish >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" />
</Target>
<Target Name="DisplayMessages" AfterTargets="PublishOnly">
<Message Text="After PublishOnly in project.csproj>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" />
</Target>
And this is the end of the build output...
1>Task "SignFile"
1>Done executing task "SignFile".
1>Done building target "CreateBootstrapper" in project "WeekEndingTabs.csproj".
1>Target "AfterPublish" in project "<MSBuildProjectFullPath>\project.csproj" (target "PublishOnly" depends on it):
1>Task "Message"
1> After Publish >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
1>Done executing task "Message".
1>Done building target "AfterPublish" in project "project.csproj".
1>Target "PublishOnly" in file "<path to Microsoft.VisualStudio.Tools.Office.targets>" from project "path to project.csproj" (entry point):
1>Done building target "PublishOnly" in project "project.csproj".
1>Target "DisplayMessages" in project "path to project.csproj" (entry point):
1>Task "Message"
1> After PublishOnly >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
1>Done executing task "Message".
1>Done building target "DisplayMessages" in project "project.csproj".
1>
1>Build succeeded.
Custom tasks (faked with PowerShell script)
In order to invoke my post-publish script, I first created a custom .targets file called postAction.targets and put this in it...
<?xml version="1.0" encoding="Windows-1252"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="EstablishLogDir" Condition="!Exists('$(MSBuildProjectDirectory)\Logs')">
<MakeDir Directories=".\Logs"/>
</Target>
<Target Name="AddPostAction" AfterTargets="PublishOnly" DependsOnTargets="EstablishLogDir">
<PropertyGroup>
<PowerShellExe Condition=" '$(PowerShellExe)'=='' ">%WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe</PowerShellExe>
<ScriptLocation Condition=" '$(ScriptLocation)'=='' ">C:\Users\Admin\Documents\GitHub\powershell-scripts\postAction-MSBuild.ps1</ScriptLocation>
<Switches>-NonInteractive -executionpolicy Unrestricted</Switches>
<PostAction>FileCopyPDA.FileCopyPDA</PostAction>
<Arguments>"& { &'$(ScriptLocation)' '$(PostAction)' $(Configuration)} "</Arguments>
<LogFile >PostAction.log</LogFile>
<LogFile Condition="Exists('$(MSBuildProjectDirectory)\Logs')">.\Logs\$(LogFile)</LogFile>
</PropertyGroup>
<Exec Command="$(PowerShellExe) $(Switches) -command $(Arguments) > $(LogFile)" />
</Target>
</Project>
(I pipe it to a log file in a subdirectory of the project)
Then I added an import element into the .csproj file
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\OfficeTools\Microsoft.VisualStudio.Tools.Office.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project=".\OfficeTools\PostAction.targets" />
I put it in the same place as the other Import elements, it doesn't work if I just tack it on at the end.
And that's it. Now my post-publish script runs at the end of every Publish build I do and the postsAction section is added to the application manifest of the current version and it and the deploy manifest are re-signed with the correct certificate.
I've upgraded to a new windows 10 development machine.
Everything works great, except post-build events in msbuild/visual studio.
This is for any project, new or existing.
They all work fine on other people's windows 7 machines.
They work fine on the build server (jenkins).
They do not work for me at all, either in visual studio or with msbuild.
PRE-build events works fine.
It's like the postbuild event isn't even defined.
Unfortunately, our postbuild events are fundamental to the build process.
Starting with a basic console app and configuring the build events as:
<PropertyGroup>
<PreBuildEvent>echo before build</PreBuildEvent>
</PropertyGroup>
<PropertyGroup>
<PostBuildEvent>echo after build</PostBuildEvent>
</PropertyGroup>
The build output is literally:
1>------ Rebuild All started: Project: ConsoleApplication1, Configuration: Debug Any CPU ------
1> before build
1> ConsoleApplication1 -> C:\dev\postbuild\ConsoleApplication1\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========
Running msbuild with /v:diag I get this section:
PostBuildEvent = echo after build
PostBuildEventDependsOn =
PreBuildEvent = echo before build
PreBuildEventDependsOn =
When running prebuild:
Target "PreBuildEvent: (TargetId:22)" in file "C:\Program Files (x86)\MSBuild\14.0\bin\Microsoft.Common.CurrentVersion.targets" from project "c:\dev\postbuild\ConsoleApplication1\ConsoleApplication1\ConsoleApplication1.csproj" (target "CoreBuild" depends on it):
Using "Exec" task from assembly "Microsoft.Build.Tasks.Core, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".
Task "Exec" (TaskId:19)
Task Parameter:WorkingDirectory=bin\Debug\ (TaskId:19)
Task Parameter:Command=echo before build (TaskId:19)
echo before build (TaskId:19)
before build (TaskId:19)
Done executing task "Exec". (TaskId:19)
Done building target "PreBuildEvent" in project "ConsoleApplication1.csproj".: (TargetId:22)
When running PostBuild:
Target "PostBuildEvent: (TargetId:77)" in file "C:\Program Files (x86)\MSBuild\14.0\bin\Microsoft.Common.CurrentVersion.targets" from project "c:\dev\postbuild\ConsoleApplication1\ConsoleApplication1\ConsoleApplication1.csproj" (target "CoreBuild" depends on it):
Done building target "PostBuildEvent" in project "ConsoleApplication1.csproj".: (TargetId:77)
I have added the following AfterBuild task to a .vcxproj file:
<Target Name="AfterBuild">
<Message Text="Hi" Importance="high" />
</Target>
It seems to run only if the the C++ code is built (or if I do a rebuild):
1>------ Rebuild All started: Project: ConsoleApplication1, Configuration: Debug Win32 ------
1> stdafx.cpp
1> SomeClass.cpp
1> ConsoleApplication1.vcxproj ->
1> Hi
D:\Projects\CppTest\Debug\ConsoleApplication1.lib
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========
It doesn't run when the the C++ code is up-to-date:
========== Build: 0 succeeded, 0 failed, 1 up-to-date, 0 skipped ==========
How can I make it always run? I'm using Visual Studio 2015.
The line
========== Build: 0 succeeded, 0 failed, 1 up-to-date, 0 skipped ==========
means that Visual Studio didn't run MSBuild at all.
For incremental builds VS uses File Tracker to intercept file operations being performed by compiler and linker. Results can be fould in ProjName.tlog folder. In subsequent builds VS checks dates of files listed in .read and .write files and decides whether to run MSBuild. This feature is described in Hashimi S., Bartholomew S. - Using MSBuild, 2nd Edition - 2013, "File Tracker" chapter.
Some of possible solutions:
Use FileTracker API in your custom build tool.
Manually write file name(s) produced by your tool in some .write file (this solution is simple but is not guaranteed to work in future VS versions).
Build your solution by MSBuild instead VS.
Build your solution from bat-file, which calls devenv first then your tool.
I got around this issue by adding the following lines to my .vcxproj file:
<PropertyGroup>
<DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
</PropertyGroup>
It seems to force the IDE to actually run MSBuild.
I tested it in Visual Studio 2015. Adding the same lines to an imported .targets file did not help.
There is a setting "Run the post-build event:" in project properties in Visual Studio, and you need to set it to: "Always" for the post-build event to run always.
I guess it's now set to "On successful build".
Build your project with detailed logging (/v:detailed or /v:diag) and find the target which being run last but present in both cases (lets say it's a made up target "FinalCleanup"), then hook your post-build target like this rather than by the special name
<Target Name="RunMyLastTarget" AfterTargets="FinalCleanup">
<Message Text="Hi" Importance="high" />
</Target>
I finally went with Custom Build Step. To ensure that it always runs I set the Outputs field to some dummy file path. Since the dummy file path is never present, the Custom Build Step always runs. It is hacky, but it gets the job done.
How can a .cmd script run from within a Visual Studio (2005, 2008, 2010, 2012 and 2013 respectively) project's pre-link stage determine whether this is a full rebuild (Build.RebuildSolution/Build.RebuildOnlyProject) or "ordinary" build (Build.BuildSolution/Build.BuildOnlyProject)?
This is an external script (LuaJIT, if you must know) and I don't want to rebuild the library every single build of the project. Instead I'd like to limit the complete rebuild to situations where I choose exactly that option.
How can a .cmd script run from within a Visual Studio (2005, 2008, 2010, 2012 and 2013 respectively) project's pre-link stage determine whether this is a full rebuild ... or "ordinary" build ... ?
I do not know if the exact thing that you are asking can be done - perhaps someone else knows how to do it. I will, however, suggest an alternate approach.
My approach is to remove the build of the Lua library from the pre-link step to a separate Visual Studio NMake project. If you create an NMake project, you will be able to know which type of build (build or rebuild) is occurring.
Note that later versions of Visual Studio simply refer to the project type as "Make". For discussion purposes here, I will refer to the project type as "NMake". I believe this is just a naming difference, and that the underlying build project remains the same between the two versions.
As a simple test, I created two Visual Studio applications: 1) an NMake project that calls a batch file to create a static library, and 2) a console application that consumes the library from step 1.
The NMake Project
In Visual Studio, if you create a new NMake project, you will see a dialog that allows you to provide MS-DOS commands:
As you can see, there are commands for: Build, Clean, Rebuild, and others. I don't have a screen shot of the above dialog with my commands, but here is my NMake project's properties:
My Build command just checks for the existence of the output file (lua.lib). If it does not exist, then it calls the rebuild.bat batch file. My Rebuild command always calls the batch file. My Clean command just deletes the output. I am not really sure what the Output command is used for, but I just filled in the path to the build output (lua.lib).
Now if you do a build, the lua.lib file will only be created if it is not there. If it is already there, nothing is done. If you do a rebuild, then a new lua.lib file is created.
The Console Application
In my console application, I added a reference to the NMake project - this way the NMake project is built prior to the console application. Here is the console application's reference page:
I also added the lua.lib file as an input during the application's link stage:
When the console application is built (during a build), it will build the NMake project if needed, and use the output (lua.lib) during the linker stage. When the console application is rebuilt (during a rebuild), it will also rebuild the NMake project.
Other Thoughts
My screen shots above only show the debug version of the properties. Your projects will have to account for the release version. There probably is a VS macro to handle this, but I am not sure since it has been ages since I've done anything with C/ C++.
In my testing above I use a single build batch file for both the build and rebuild. Obviously, you could do the same or you could use different batch files.
It may be a bit of a hack, but in .csproj file there are sections
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
You can set an variable from BeforeBuild and retrieve it from cmd script. Later on reset this variable in AfterBuild and you should be good to go.
Ok, this is going to be a long one.
First of all - do not take my code 'as is' - it is terrible one with lots of hacks, I had no idea msbuild is so broken by default (it seems at work I have access to waaaay more commands that make life easier). And another thing - it seems vcxproj is broken at some poin - I was not able to integrate the way I wanted with only BeforeRebuild and AfterRebuild targets - I had to redefine hole Rebuild target (it is located in C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets)
So, the idea is the following: when a Rebuild is happening we create an anchor. Then, during PreLink stage we execute cmd which is able to use created anchor. If the anchor is in place - we deal with Rebuild, if there is no anchor - it is a simple Build. After Rebuild is done - we delete the anchor.
modifications in vcxproj file:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
....
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
....
<PreLinkEventUseInBuild>true</PreLinkEventUseInBuild>
....
</PropertyGroup>
....
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
.....
<PreLinkEvent>
<Command>D:\PreLink\b.cmd</Command>
</PreLinkEvent>
.....
</ItemDefinitionGroup>
.....
<Target Name="BeforeRebuild">
<Exec Command="echo 2 > D:\PreLink\2.txt" />
</Target>
<Target Name="AfterRebuild">
<Exec Command="del D:\PreLink\2.txt" />
</Target>
<!-- This was copied from MS file -->
<PropertyGroup>
<_ProjectDefaultTargets Condition="'$(MSBuildProjectDefaultTargets)' != ''">$(MSBuildProjectDefaultTargets)</_ProjectDefaultTargets>
<_ProjectDefaultTargets Condition="'$(MSBuildProjectDefaultTargets)' == ''">Build</_ProjectDefaultTargets>
<RebuildDependsOn>
BeforeRebuild;
Clean;
$(_ProjectDefaultTargets);
AfterRebuild;
</RebuildDependsOn>
<RebuildDependsOn Condition=" '$(MSBuildProjectDefaultTargets)' == 'Rebuild' " >
BeforeRebuild;
Clean;
Build;
AfterRebuild;
</RebuildDependsOn>
</PropertyGroup>
<Target
Name="Rebuild"
Condition=" '$(_InvalidConfigurationWarning)' != 'true' "
DependsOnTargets="$(RebuildDependsOn)"
Returns="$(TargetPath)"/>
<!-- End of copy -->
</Project>
And the cmd looks like this:
if exist 2.txt (
echo Rebuild818181
) else (
echo Build12312312
)
The output from Output window:
1>Task "Exec" (TaskId:41)
1> Task Parameter:Command=D:\PreLink\b.cmd
1> :VCEnd (TaskId:41)
1> Build12312312 (TaskId:41)
Things to improve:
Use normal variables instead of external file (it seems MsBuild extension pack should do it)
Probably find a way to override only BeforeRebuild and AfterRebuild instead of the hole Rebuild part
It is much easier. Just add the following target to your build file or visual Studio Project
<Target Name="AfterRebuild">
<Message Text="AFTER REBUILD" Importance="High" />
<!--
Do whatever Needs to be done on Rebuild - as the message shows in VS Output
window it is only executed when an explicit rebuild is triggered
-->
</Target>
If you want a two step solution use this as a template:
<PropertyGroup>
<IsRebuild>false</IsRebuild>
</PropertyGroup>
<Target Name="BeforeRebuild">
<Message Text="BEFORE REBUILD" Importance="High" />
<PropertyGroup>
<IsRebuild>true</IsRebuild>
</PropertyGroup>
</Target>
<Target Name="BeforeBuild">
<Message Text="BEFORE BUILD: IsRebuild: $(IsRebuild)" Importance="High" />
</Target>