I am building a solution file with code similar to the following;
<Target Name="Build">
<MSBuild Projects="$(MySolution)"
Targets="Build"
BuildInParallel="false"
Properties="Configuration=$(Configuration);Platform=$(Platform);">
<Output ItemName="MyOutputs" TaskParameter="TargetOutputs"/>
</MSBuild>
<Message Importance="High" Text="MyOutputs=#(MyOutputs)" />
</Target>
And the output from that message that is supposed to show a list of assemblies that were built is missing some projects - projects that are actually being built (as they are shown being built in the rest of the log).
I found a descernible difference in the solution file regarding these projects: They each have a "ProjectSection" where others do not.
For example, this project is being sent to #(MyOutputs)
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Foo", "Foo\Foo.csproj", "{075C4960-951B-43FA-AAC5-99497C9C0EBC}"
EndProject
Whereas this one is not:
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bar", "Bar\Bar.csproj", "{EA7EEBE4-F7DB-4148-8B00-F49D747FA7B1}"
ProjectSection(ProjectDependencies) = postProject
{8B31D756-F858-43CA-81A7-5B4797554263} = {8B31D756-F858-43CA-81A7-5B4797554263}
EndProjectSection
EndProject
Would someone care to enlighten me on why this is happening, or what the consequences of removing these ProjectSections are?
I found my issue was this
I was doing this:
msbuild.exe myproject.vbproj /T:Rebuild
msbuild.exe myproject.vbproj /T:Package
VS This:
msbuild.exe myproject.vbproj /T:Rebuild;Package
I have no idea why this work or why it didn't in the first place. But hope that helps.
Related
I have a solution that contains a console application with a .csproj file like the this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
</Project>
I also have a library project that uses the console application to generate a heap of C# code that get compiled into the library, the library .csproj file looks like this.
<Project Sdk="Microsoft.NET.Sdk" InitialTargets="RunGenerator">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../generator/generator.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<Target Name="RunGenerator">
<Exec Command="dotnet run -p "../generator/generator.csproj" input output" />
</Target>
</Project>
This fails because the dependency analysis says that a netstandard1.4 assembly cannot reference a netcoreapp1.1 assembly. That is correct except that I am not referencing the assembly.
I can work around that issue by building the generator project like this:
<Project Sdk="Microsoft.NET.Sdk" InitialTargets="RunGenerator">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
</PropertyGroup>
<Target Name="RunGenerator">
<Exec Command="dotnet build "../generator/generator.csproj"" />
<Exec Command="dotnet run -p "../generator/generator.csproj" input output" />
</Target>
</Project>
The problem is that the generator project no longer takes part in the dependency analysis when these projects are built using the containing solution file and the explicit build of the generator project sometimes runs concurrently with another build of the same project initiated by the solution build and this results in errors because files are locked etc.
Is it possible to have a project dependency without checking the target framework?
Can anyone suggest a workaround?
Thanks.
Here are some MSBuild tips. You might need to combine a few of these ideas.
You can use your solution file to add an explicit project dependency. See https://learn.microsoft.com/en-us/visualstudio/ide/how-to-create-and-remove-project-dependencies (This question was originally asked here: Visual Studio 2010: How to enforce build order of projects in a solution?). Unfortunately, this is really hard to do if you don't have VS. The format is .sln files is kinda a nightmare.
To avoid the concurrent build issue, use the MSBuild task instead of the Exec task. See https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-task
<Target Name="CompileAnotherProject">
<MSBuild Projects="../generator/generator.csproj" Targets="Build" />
</Target>
dotnet-run invokes "dotnet build" automatically. This is actually problematic in concurrent builds. You can instead add a target to your generator.csproj that runs the app after it has been built. "dotnet filepath.dll" runs the compiled app without building it.
<Target Name="RunCodeGen" AfterTargets="Build">
<Exec Command="dotnet $(AssemblyName).dll input output"
WorkingDirectory="$(OutDir)" />
</Target>
Given an MSBuild Task that runs in AfterTargets="AfterCompile" and produces some files how do you get those files to be included in the current projects output so that the files will be copied to the bin directory of any projects referencing that project?
I have no guarantees that this is the right solution but it seems to work:
<Target Name="MyTarget" AfterTargets="AfterCompile">
<PropertyGroup>
<MyInput>D:\1.txt</MyInput>
<MyOutput>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)$(OutDir)\1.txt'))</MyOutput>
</PropertyGroup>
<Copy SourceFiles="$(MyInput)" DestinationFolder="$(OutDir)" SkipUnchangedFiles="true" />
<ItemGroup>
<AllItemsFullPathWithTargetPath Include="$(MyOutput)">
<TargetPath>1.txt</TargetPath>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</AllItemsFullPathWithTargetPath>
</ItemGroup>
</Target>
The relevant logic is here:
http://sourceroslyn.io/#MSBuildTarget=GetCopyToOutputDirectoryItems
http://sourceroslyn.io/#MSBuildItem=AllItemsFullPathWithTargetPath
Basically we rely on the fact that to determine the list of files to copy from dependent projects MSBuild calls the GetCopyToOutputDirectoryItems target of the dependent projects and uses its output (which is AllItemsFullPathWithTargetPath).
By adding ourselves to AllItemsFullPathWithTargetPath at the last minute we get picked up when a dependent project calls us.
Thank you, Kirill. That was an excellent answer and it helped me when trying to copy ETW manifest files from a different project's output. Below is the final output.
Since I've simply expanded upon Kirill's answer, I do not expect this answer to be accepted. I post this here in the hope it helps someone else.
<Target Name="IncludeEtwFilesInOutput"
BeforeTargets="GetCopyToOutputDirectoryItems">
<PropertyGroup>
<EtwManifestFile>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)$(OutDir)\My.etwManifest.man'))</EtwManifestFile>
<EtwResourceFile>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)$(OutDir)\My.etwManifest.dll'))</EtwResourceFile>
</PropertyGroup>
<ItemGroup>
<AllItemsFullPathWithTargetPath Include="$(EtwManifestFile)">
<TargetPath>My.etwManifest.man</TargetPath>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</AllItemsFullPathWithTargetPath>
<AllItemsFullPathWithTargetPath Include="$(EtwResourceFile)">
<TargetPath>My.etwManifest.dll</TargetPath>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</AllItemsFullPathWithTargetPath>
</ItemGroup>
</Target>
I have a project which has some post build events that do some copying around for other projects. I unfortunately cannot change that, and have been asked to write a build script for use on a CI server.
Problem is that the post build steps run off the debug/release bin folders and I compile through the build script to a different folder. So one solution would be to let the project build as is, and then manually copy all files from the bin folders to the output folder I am using. However that feels like a bit of a hack, so I was wondering if there is a way for an MSBuild task to tell the solution it is building to ignore PostBuild events, I believe you could set a property PostBuildEvent='' but it didnt seem to stop them from happening...
Here is an example of the build script target:
<Target Name="Compile" DependsOnTargets="Clean;">
<MSBuild Projects="$(SourceDirectory)\SomeSolution.sln"
Properties="Configuration=Release; OutputPath=$(CompilationDirectory); PostBuildEvent=''" />
</Target>
Anyone had to do anything similar before?
To disable all PostBuildEvents, set the CustomAfterMicrosoftCommonTargets to C:\PostBuild.config (or whatever you name the file) and have PostBuild.config to be:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="PostBuildEvent"/>
</Project>
Add /p:CustomAfterMicrosoftCommonTargets="C:\PostBuild.config" to your msbuild command line
Or update your MsBuild task properties:
<MsBuild Projects="$(ProjectTobuild)" Properties="Configuration=$(Configuration);Platform=$(Platform);CustomAfterMicrosoftCommonTargets='C:\PostBuild.config'" Targets="Build"/>
To disable PostBuildEvents at project level for MSBuild, simply put these codes inside .csproj:
<Target Name="BeforeBuild">
<PropertyGroup>
<PostBuildEvent Condition="'$(BuildingInsideVisualStudio)' == 'false' Or '$(BuildingInsideVisualStudio)' != 'true'"></PostBuildEvent>
</PropertyGroup>
</Target>
I wish to start using MS-Build. I have lots of projects which I build manually (from Visual Studio) as of now. I want to automate build process and preferably from a machine onto which I don't wish to install Visual Studio. I started reading about MS-Build on MSDN. But I am yet to get a step by step guidance where to start and how to do. My questions are like:
How can I start MS-Build? Is there any download-able?
What is the first step?
How to create an MS-Build script?
And a lot similar questions. Can somebody guide me?
MS Build comes with the .NET Framework itself and the executable (msbuild.exe) is located in the .NET-framework directory, something like (depending on version):
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319
C:\WINDOWS\Microsoft.NET\Framework\v3.5
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727
(The right version is also in %path% when using the "Visual Studio command prompt" from the start menu.)
MsBuild files are xml-files. You can start by making a new text file, lets say "c:\myscript.msbuild", and copy-paste this to the file:
<Project DefaultTargets="MyTarget" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="MyTarget">
<Message Text="Hello world!" Importance="high"/>
</Target>
</Project>
Then go to command prompt and type:
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\msbuild.exe c:\myscript.msbuild
That is a good start. :)
Then you can customize the targets and properties.
Second example:
<Project DefaultTargets="All" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(MyCondition)' == 'x'" >
<MyProperty>World2</MyProperty>
</PropertyGroup>
<Target Name="MyTarget">
<Message Text="Hello" Importance="high"/>
<Message Text="$(MyProperty)" Importance="high"/>
</Target>
<Target Name="MyTarget2">
</Target>
<Target Name="All">
<CallTarget Targets="MyTarget" />
<CallTarget Targets="MyTarget2" />
</Target>
</Project>
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\msbuild.exe c:\myscript.msbuild /target:mytarget /property:MyCondition=x
You can have also build files inside build-files.
<Project DefaultTargets="MyTarget" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="MyExternalProperties.msbuild"/>
<Target Name="MyTarget">
<Exec Command="echo Hello world 3"/>
</Target>
</Project>
MSBuild is similar to other build products like NAnt (just in case you've used one of those), but it is still different in a few respects.
Here is a good start page on MSDN. There are a truckload of different MSBuild task libraries released under various licences, most that i have seen are completely free to use and come with source code. Probably the two biggest are:
the open source MSBuild Community Tasks Project
the SDC Tasks Library on codeplex
Other good places to get info:
the MSBuild team blog
MSBuild Book
the blog of Sayed Ibrahim Hashimi (who is incredibly knowledgeable about the product but didn't work for MS until just recently). He also hangs out here on SO and may stumble across this question.
That should be enough to get started. If you can't find a task to do what you want, just write it yourseld - it is very easy.
I have an MSBuild project as follows:
<Target Name="StopApplications">
<BizTalk.BuildGenerator.Tasks.StopApplication MessageBoxConnection="$(BizTalkManagementDatabaseConnectionString)" ApplicationName="x.Direct.Brackmills"/>
<BizTalk.BuildGenerator.Tasks.StopApplication MessageBoxConnection="$(BizTalkManagementDatabaseConnectionString)" ApplicationName="x.Direct.Manhattan"/>
</Target>
<Target Name="RemoveApplications">
<Exec Command="BTSTask RemoveApp -ApplicationName:x.Direct.Brackmills -Server:$(BizTalkDatabaseServerName) -Database:$(BizTalkManagementDatabaseName)" />
<Exec Command="BTSTask RemoveApp -ApplicationName:x.Direct.Manhattan -Server:$(BizTalkDatabaseServerName) -Database:$(BizTalkManagementDatabaseName)" />
</Target>
My problem is that when calling the "RemoveApplications" target, the ContinueOnError does not work as I'd expect. I have a long list of applications to stop and remove. They won't all allways be present so I need the script to continue when it finds they're not there. This seems to work find for the "StopApplications" target but when it hits a missing application in the "RemoveApplications" target I get the message:
"Done building target "RemoveApplications" in project "cleardownApplications.proj" -- FAILED. Build continuing because "ContinueOnError" on the task "CallTarget" is set to "true".
But then, it drops out of "RemoveApplications" and moved onto "AddApplications"
Any help gratefully received,
Thanks
Rob.
I've solved this a bit differently and uses a separate target to check if the application exists before removing it.
<Target Name="ApplicationExists">
<BizTalk2006.Application.Exists Application="$(ApplicationName)">
<Output TaskParameter="DoesExist" PropertyName="ApplicationExists" />
</BizTalk2006.Application.Exists>
</Target>
The I use that exists-target as an "condition" in other targets.
<Target Name="DeleteApplication" Condition="$(ApplicationExists)=='True'" DependsOnTargets="ApplicationExists">
<BizTalk2006.Application.Stop Application="$(ApplicationName)"/>
<BizTalk2006.Application.Delete Application="$(ApplicationName)"/>
</Target>