EmbeddedResource not working inside Target - msbuild

I have some TypescriptCompile files which I then want to embed in my .dll. I had to migrate to the new csproj format and now I can't embed while inside a Target.
This works:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="testFile.ts" />
</ItemGroup>
</Project>
While this does not:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
</PropertyGroup>
<Target Name="AddGeneratedToBuildOutput" BeforeTargets="CoreCompile">
<ItemGroup>
<PackageFiles Include="$(MSBuildProjectDirectory)\**\*.*;"/>
</ItemGroup>
<Message Text="The target is called: %(PackageFiles.FullPath)" Importance="high"/>
<ItemGroup>
<EmbeddedResource Include="testFile.ts" />
</ItemGroup>
</Target>
</Project>
Note that the message gets written and the referred file is in the project, hence we can be sure that the target gets called.
I already tried other targets instead of CoreCompile but since I'm using TypeScriptCompile, if I use BeforeBuild, Build or ResolveReferences I get compilation errors since the .js files are not generated yet.
I'm using JetBrains' DotPeek to inspect the resources and using msbuild 15.0.

Solution to this problem is here: https://github.com/microsoft/msbuild/issues/4778

Related

How to copy contentFiles of NuGet package references via MSBuild Copy task

We have several repositories and each one has its own .editorconfig. Obviously, these are not synced, which is why I would like to distribute the .editorconfig from our framework solution (along with other files) via NuGet package to all our repositories/solutions and copy it via a simple Copy build Task to the solution directory.
I attempted to do the following:
Create a project "EditorConfigDistribution", which is supposed to contain the master .editorconfig file.
<Project Sdk="Microsoft.NET.Sdk">
...
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<NoDefaultExcludes>true</NoDefaultExcludes>
</PropertyGroup>
<ItemGroup>
<Content Include=".editorconfig">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Pack>true</Pack>
<PackageCopyToOutput>false</PackageCopyToOutput>
<PackagePath>contentFiles\any\any\content</PackagePath>
</Content>
</ItemGroup>
</Project>
This all works as expected and I do get the desired .editorconfig file in my project from the other solutions and it is referenced as shortcut in a folder content/.editorconfig (see EditorConfigConsumer Project Structure).
The file is only a reference to C:\Users\<user>\.nuget\packages\editorconfigdistribution\1.0.0\contentFiles\any\any\content\.editorconfig.
Now, I want to copy that .editorconfig file via build task:
<Project Sdk="Microsoft.NET.Sdk">
...
<ItemGroup>
<PackageReference Include="EditorConfigDistribution" Version="1.0.0">
</PackageReference>
</ItemGroup>
<Target Name="CopyEditorConfig" BeforeTargets="BeforeBuild">
<ItemGroup>
<EditorConfigFileToCopy Include="$(MSBuildProjectDirectory)\content\.editorconfig" />
</ItemGroup>
<Copy SourceFiles="#(EditorConfigFileToCopy)" DestinationFolder="$(MSBuildProjectDirectory)\.." SkipUnchangedFiles="true" UseHardlinksIfPossible="false" />
</Target>
</Project>
However, I do get the following error:
Error MSB3030: Could not copy the file "C:\Users\weberma9\source\repos\<some_path>\EditorConfigConsumer\content\.editorconfig" because it was not found. (20, 5)
I can understand that the file (since it is a shortcut) cannot be found, but I just cannot figure out a way to reference that shortcut correctly in my build task.
What do I need to change in that line <EditorConfigFileToCopy Include="$(MSBuildProjectDirectory)\content\.editorconfig" />?
Of course, if you have better approaches to my general problem - I'm glad to hear about it.
I was able to find a solution for my problem:
I not only provide the .editorconfig, but also the Copy-Build task via my EditorConfigDistribution project, which looks like this now:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<NoDefaultExcludes>true</NoDefaultExcludes>
</PropertyGroup>
<ItemGroup>
<None Include="..\..\.editorconfig">
<Link>Rules\.editorconfig</Link>
<Pack>true</Pack>
<PackageCopyToOutput>false</PackageCopyToOutput>
<PackagePath>Rules\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<None Include="build\**">
<Pack>true</Pack>
<PackageCopyToOutput>false</PackageCopyToOutput>
<PackagePath>build\</PackagePath>
</None>
</ItemGroup>
</Project>
The .props file is straightforward and due to the convention that <package_id>.props and <package_id>.target are added to projects that consume the package (see Include MSBuild props and targets in a package), it will always be executed before 'BeforeBuild'.
EditorConfigDistribution.props (placed in build folder):
<Project>
<Target Name="CopyEditorConfig" BeforeTargets="BeforeBuild">
<ItemGroup>
<EditorConfigFilesToCopy Include="$(MSBuildThisFileDirectory)..\Rules\.editorconfig" />
</ItemGroup>
<Copy SourceFiles="#(EditorConfigFilesToCopy)" DestinationFolder="$(SolutionDir).." SkipUnchangedFiles="true" UseHardlinksIfPossible="false" />
</Target>
</Project>

MSBuild well-known item metadata and NuGet pack

I am trying to add custom files to a specific NuGet package (basically I need all output files included in the NuGet package since it serves as a tool for Chocolatey).
After some searching, I found this potential fix:
<PropertyGroup>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);GetToolsPackageFiles</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<Target Name="GetToolsPackageFiles">
<ItemGroup>
<BuildOutputInPackage Include="$(OutputPath)\**\*.dll" />
<BuildOutputInPackage Include="$(OutputPath)\**\*.exe" />
</ItemGroup>
</Target>
Unfortunately, this won't work correctly for subdirectories, so I tried this:
<PropertyGroup>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);GetToolsPackageFiles</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<Target Name="GetToolsPackageFiles">
<ItemGroup>
<BuildOutputInPackage Include="$(OutputPath)\**\*.dll">
<TargetPath>$([MSBuild]::MakeRelative('$(OutputPath)', %(FullPath)))</TargetPath>
</BuildOutputInPackage>
<BuildOutputInPackage Include="$(OutputPath)\**\*.exe">
<TargetPath>$([MSBuild]::MakeRelative('$(OutputPath)', %(FullPath)))</TargetPath>
</BuildOutputInPackage>
</ItemGroup>
</Target>
According to the docs, I should be able to use %(FullPath), but I am getting this error:
error MSB4184: The expression "[MSBuild]::MakeRelative(C:\Sour
ce\RepositoryCleaner\output\Release\RepositoryCleaner\netcoreapp3.1\, '')" cannot be evaluated. Parameter "path" cannot have zero length.
[C:\Source\RepositoryCleaner\src\RepositoryCleaner\RepositoryCleaner.csproj]
Any idea why the well-known items don't seem to work in this scenario?
Got a fix by specifying the item group outside the target and then using that instead.
<PropertyGroup>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);GetToolsPackageFiles</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<ItemGroup>
<ToolDllFiles Include="$(OutputPath)\**\*.dll" />
<ToolExeFiles Include="$(OutputPath)\**\*.exe" />
</ItemGroup>
<Target Name="GetToolsPackageFiles">
<ItemGroup>
<BuildOutputInPackage Include="#(ToolDllFiles)">
<TargetPath>$([MSBuild]::MakeRelative('$(OutputPath)', %(ToolDllFiles.FullPath)))</TargetPath>
</BuildOutputInPackage>
<BuildOutputInPackage Include="#(ToolExeFiles)">
<TargetPath>$([MSBuild]::MakeRelative('$(OutputPath)', %(ToolExeFiles.FullPath)))</TargetPath>
</BuildOutputInPackage>
</ItemGroup>
</Target>

MSBuild: how to include generated classes into compilation?

I have the following MSBuild script:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
...
<BuildDependsOn>
NSwag;
$(BuildDependsOn)
</BuildDependsOn>
<!--<AfterTransform>NSwag</AfterTransform>-->
</PropertyGroup>
<ItemGroup>
...
</ItemGroup>
<Target Name="NSwag" BeforeTargets="BeforeBuild">
<Message Text="Generating C# client code via NSwag" Importance="high" />
<!-- ISSUE HERE -->
<Copy SourceFiles="..\..\MyClient.cs" DestinationFiles="Gen\MyClient.cs" />
</Target>
</Project>
The Target "NSwag" above is going to be used for code generation tool. But to simplify things, I use here just a file copy command.
The issue is that the .cs files added within this Target are not visible in the MSBuild compilation:
The type or namespace name 'MyClient' does not exist in the namespace 'MyNamespace'
NOTE: The issue occurs only if the file didn't exist in the destination folder.
NOTE: I was trying to mangle with the following but with no success so far:
<Target Name="RemoveSourceCodeDuplicates" BeforeTargets="BeforeBuild;BeforeRebuild" DependsOnTargets="UpdateGeneratedFiles">
<RemoveDuplicates Inputs="#(Compile)">
<Output TaskParameter="Filtered" ItemName="Compile"/>
</RemoveDuplicates>
</Target>
and
<Target Name="UpdateGeneratedFiles" BeforeTargets="BeforeBuild;BeforeRebuild" DependsOnTargets="NSwag">
<ItemGroup>
<Compile Include="Gen\MyClient.cs" Condition="!Exists('Gen\MyClient.cs')" />
</ItemGroup>
</Target>
What am I missing here?
I think I found a workaround for that - check and include the files first (UpdateGeneratedFiles target), then generate them (NSwag target). See the script below:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
...
<BuildDependsOn>
NSwag;
$(BuildDependsOn)
</BuildDependsOn>
</PropertyGroup>
<Target Name="NSwag" BeforeTargets="BeforeBuild;BeforeRebuild"
DependsOnTargets="UpdateGeneratedFiles">
<Message Text="Generating C# client code via NSwag" Importance="high" />
<Copy SourceFiles="..\..\MyClient.cs" DestinationFiles="Gen\MyClient.cs" />
</Target>
<Target Name="UpdateGeneratedFiles" BeforeTargets="BeforeBuild;BeforeRebuild" >
<ItemGroup>
<Compile Include="Gen\MyClient.cs" Condition="!Exists('Gen\MyClient.cs')" />
</ItemGroup>
</Target>
</Project>

MSBuild reference assembly not being included in build

I can build my project with the following command...
csc /reference:lib\Newtonsoft.Json.dll SomeSourceFile.cs
... but when I use this command...
msbuild MyProject.csproj
... with the following .csproj file my .dll reference isn't included. Any thoughts?
<PropertyGroup>
<AssemblyName>MyAssemblyName</AssemblyName>
<OutputPath>bin\</OutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="SomeSourceFile.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>lib\Newtonsoft.Json.dll</HintPath>
</Reference>
</ItemGroup>
<Target Name="Build">
<MakeDir Directories="$(OutputPath)" Condition="!Exists('$(OutputPath)')" />
<Csc Sources="#(Compile)" OutputAssembly="$(OutputPath)$(AssemblyName).exe" />
</Target>
You didn't get your Reference group hooked up to the Csc task. Also the references the way you specified could not be used directly inside the task. Tasks that ship with MSBuild include ResolveAssemblyReference, that is able to transform short assembly name and search hints into file paths. You can see how it is used inside c:\Windows\Microsoft.NET\Framework64\v4.0.30319\Microsoft.Common.targets.
Without ResolveAssemblyReference, the simplest thing that you can do is to write it like this:
<PropertyGroup>
<AssemblyName>MyAssemblyName</AssemblyName>
<OutputPath>bin\</OutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="SomeSourceFile.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="lib\Newtonsoft.Json.dll" />
</ItemGroup>
<Target Name="Build">
<MakeDir Directories="$(OutputPath)" Condition="!Exists('$(OutputPath)')" />
<Csc Sources="#(Compile)" References="#(Reference)" OutputAssembly="$(OutputPath)$(AssemblyName).exe" />
</Target>
Notice that Reference item specifies direct path to the referenced assembly.
What you've done is to overload the default Build target typically imported via Microsoft.CSharp.targets. In the default Build target, it takes the item array #(Compile), in which your .cs source files are resident, and also the #(Reference) array, among other things, and composites the proper call to the C# compiler. You've done no such thing in your own minimal Build target, which effectivly ignores the declaration of #(Reference) and only supplies #(Compile) to the Csc task.
Try adding the References="#(References)" attribute to the Csc task.

TFS Build SourceTfs.Checkout

I am trying to get my build to checkout some files (using Microsoft.Sdc.Common.tasks) and then to check them in after the build has finished.
But I can't seem to get this working at all, let alone before and after the build.
Whaereabouts should this sort of code live?
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="DesktopBuild;MyProjectDbUpdate" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TeamBuild\Microsoft.TeamFoundation.Build.targets" />
<PropertyGroup>
<TasksPath>C:\Program Files\MSBuild\sdc\</TasksPath>
</PropertyGroup>
<Import Project="$(TasksPath)\Microsoft.Sdc.Common.tasks" />
<Target Name="MyProjectDbUpdate">
<Message Text="MyProjectDbUpdate checkin start"/>
<SourceTfs.Checkout Path="$/MyProject/Code/MyProjectDbUpdate" TfsVersion="2008" workingDirectory="C:\buildagent\MyProject\ContinuousIntegration\Sources\Code" />
<SourceTfs.Checkin Path="$/MyProject/Code/MyProjectDbUpdate" workingDirectory="C:\buildagent\MyProject\ContinuousIntegration\Sources\Code" Comments="Build checkout/checkin." TfsVersion="2008" Override="Build overrides checkin policy" />
<Message Text="MyProjectDbUpdate checkin complete"/>
</Target>
<ProjectExtensions>
<ProjectFileVersion>2</ProjectFileVersion>
<Description>Build</Description>
<BuildMachine>MYSERVER</BuildMachine>
</ProjectExtensions>
<PropertyGroup>
<TeamProject>MyProject</TeamProject>
<BuildDirectoryPath>c:\buildagent\MyProject\ContinuousIntegration</BuildDirectoryPath>
<DropLocation>\\UNKNOWN\drops</DropLocation>
<RunTest>false</RunTest>
<RunCodeAnalysis>Never</RunCodeAnalysis>
<WorkItemType>Bug</WorkItemType>
<WorkItemFieldValues>System.Reason=Build Failure;System.Description=Start the build using Team Build</WorkItemFieldValues>
<WorkItemTitle>Build failure in build:</WorkItemTitle>
<DescriptionText>This work item was created by Team Build on a build failure.</DescriptionText>
<BuildlogText>The build log file is at:</BuildlogText>
<ErrorWarningLogText>The errors/warnings log file is at:</ErrorWarningLogText>
<UpdateAssociatedWorkItems>true</UpdateAssociatedWorkItems>
<AdditionalVCOverrides></AdditionalVCOverrides>
<CustomPropertiesForClean></CustomPropertiesForClean>
<CustomPropertiesForBuild></CustomPropertiesForBuild>
</PropertyGroup>
<ItemGroup>
<SolutionToBuild Include="$(BuildProjectFolderPath)/../../Code/MyProject.sln">
<Targets></Targets>
<Properties></Properties>
</SolutionToBuild>
</ItemGroup>
<ItemGroup>
<ConfigurationToBuild Include="Release|Any CPU">
<FlavorToBuild>Release</FlavorToBuild>
<PlatformToBuild>Any CPU</PlatformToBuild>
</ConfigurationToBuild>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<PropertyGroup>
</PropertyGroup>
<ItemGroup>
</ItemGroup>
</Project>
Specifying your target as a default target is not going to call it as Team build explicity sets the target it is going to call.
Try renaming the Target to AfterGet or overriding the the GetDependsOn property to include your target
<GetDependsOn>
$(GetDependsOn)
MyProjectDbUpdate;
</GetDependsOn>