I have a bunch of project that requires tweaks to be build in a continuous environement.
I put every tweaks in a separate .target file to reuse this file across all projects.
At the very end of my csproj files, I put (before the closing) Project element:
This is working quite well unless I try to include additional reference path.
If I specify using command line the path (msbuild myproject.csproj /p:ReferencePath="C:\path\to\dlls"). The project compile.
My target file is :
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- some tweaks here -->
<PropertyGroup Condition="'$(CompileFor)' == 'SP2013'">
<SomeProperty>some value</SomeProperty>
<AdditionalReferencePaths>C:\path\to\dlls</AdditionalReferencePaths>
</PropertyGroup>
</Project>
But this does not works (dll cannot be resolved).
I also tried :
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<AdditionalReferencePaths Include="C:\path\to\dlls"/>
</ItemGroup>
</Project>
This is not working, because the ItemGroup element can't be out of a Target element
Lastly, I tried:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="SomeTarget" BeforeTargets="BeforeBuild">
<ItemGroup>
<AdditionalReferencePaths Include="C:\path\to\dlls"/>
</ItemGroup>
</Target>
</Project>
This still isn't working. No error, I can see the target is called in the build log, but the DLLs are still not resolved.
How to fix it?
To give a bit of context, tweaks I include in the target file allows me to compile the project against different version of DLLs. The code is a plugin of a 3rd party application (SharePoint to name it), and I want to compile for several different versions of the product. Using some conditional, I can target either a folder with one version of the product or another folder for other version of the product.
I get rid of this issue after two fixes.
The correct property wasn't AdditionalReferencePath but ReferencePath
I also have to move the Import before the first ItemGroup of my csproj. I guess this was required to have to properties set before the Reference element
Related
I have a Visual Studio solution which has several projects. One of them, which is called Extensions is supposed to build a Nuget package. Problem is that if I use MSBuild to build the solution (by executing msbuild from the command prompt), I get the following error for the Nuget package build task:
"C:\git\repo\dirs.proj" (default target) (1:7) ->
"C:\git\repo\sources\dirs.proj" (default target) (2:5) ->
"C:\git\repo\sources\dev\Sdk\CompanyName.ProductName.Extensions.csproj" (default target) (7:6) ->
(GenerateNuspec target) ->
C:\Program Files\dotnet\sdk\5.0.408\Sdks\NuGet.Build.Tasks.Pack\build\NuGet.Build.Tasks.Pack.targets(221,5): error : Could not find a part of the path 'C:\git\ess\target\distrib\Debug\Amd64\CompanyName.ProductName.Extensions'. [C:\git\ess\sources\dev\Sdk\CompanyName.ProductName.Extensions.csproj]
Here is the CSPROJ file I have for this project:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>CompanyName.ProductName.Extensions</AssemblyName>
<RootNamespace>CompanyName.ProductName.Extensions</RootNamespace>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);IncludeCoreAssets</TargetsForTfmSpecificBuildOutput>
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
<NuspecFile>CompanyName.ProductName.Extensions.nuspec</NuspecFile>
<PackageOutputPath>$(DistribRoot)\$(Configuration)\$(Platform)\$(MSBuildProjectName)</PackageOutputPath>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<SkipAssemblyComVisible>true</SkipAssemblyComVisible>
<IncludeBuildOutput>false</IncludeBuildOutput>
<OutputPath>$(DistribRoot)\$(Configuration)\$(Platform)\$(MSBuildProjectName)</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<NuspecBasePath>$(OutputPath)</NuspecBasePath>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Core\CompanyName.ProductName.Core.csproj" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<Target DependsOnTargets="ResolveReferences" Name="IncludeCoreAssets">
<ItemGroup>
<BuildOutputInPackage Include="#(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
</ItemGroup>
</Target>
</Project>
I personally think the issue is that for some reason when MSbuild is trying to create the Nuget package, it's trying to find the Extensions project DLLs in this path: \target\distrib\Debug\Amd64\CompanyName.ProductName.Extensions\, even though it actually built and stored the said binaries in this path earlier: \target\distrib\CompanyName.ProductName.Extensions\. <--- This is something I checked manually myself by examining the 'target' folder after running msbuild.
Visual Studio however doesn't have this issue. When I build this solution within Visual Studio, it stores the binaries for this Extensions project in \target\distrib\Debug\AnyCPU\CompanyName.ProductName.Extensions\ folder, which I think matches the pattern defined in the CSPROJ file:
<OutputPath>$(DistribRoot)\$(Configuration)\$(Platform)\$(MSBuildProjectName)</OutputPath>
Visual Studio also stored the Nuget package in this folder, which is also correct as per the <PackageOutputPath> spec mentioned in the CSPROJ file.
So can someone suggest why MSbuild is storing the built DLLs of this project in \target\distrib\CompanyName.ProductName.Extensions\, but is trying to find them in \target\distrib\Debug\Amd64\CompanyName.ProductName.Extensions\ ?
I think the issue is that for some reason, MSbuild isn't adhering to the <OutputPath> spec mentioned above when it's storing the built DLLs for this project.
The issue:
If I change the file extension from the visual studio, the file Build Action will be according to the previous Build Action and not according to the new file extension.
There is a way to enforce the visual studio always takes the Build Action from the file extension?
Example:
Create a new .Net Core 3.1 Class library. There is a single file (Class1.cs) The csproj will be like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
</Project>
Change the name of Class1.cs to Class1.txt from the visual studio. The result, Class1.txt is with Build Action Compile although it is a text file. The content of csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<None Remove="Class1.txt" />
</ItemGroup>
<ItemGroup>
<Compile Include="Class1.txt" />
</ItemGroup>
</Project>
Additional information:
I'm using Visual Studio 2019.
If I change the file extension from the file system the issue doesn't appear.
Update:
A possible solution is to change the file Build Action after each file rename, but I want an automatic way.
A resource added to a .NET standard project does not get compiled into the DLL.
I'm porting a .NET Framework project to .NET Standard. My original project has some resources, marked as "Build Action : Resource" which are being consumed by other assemblies.
The .NET Standard project file.
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup> *** not sure why this is added by VS2019 **
<None Remove="Resources\ErrorLarge.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\ErrorLarge.png" />
</ItemGroup>
</Project>
DLL content and size does not change with "Build Action" property changed from "None" to "Resource". Naturally my consumer assemblies will return a IOException: Cannot locate resource 'resources/errorlarge.png'. error.
VS2019 - 16.2.4
It sounds like you want EmbeddedResource in your CSProj file rather than Resource.
This seems useful at describing adding & reading them.
I created a task in our C# Projects to auto-version projects when they are built (changes are made) in release mode. The versioning part works perfectly. However, all the projects are being built regardless if the project actually changed when done from command line. This causes projects to be versioned unnecessarily. Building in Visual Studio works, unchanged projects are not built, however we made a tool to do automated build using msbuild.exe and are using this as a temporary fix while we work on Bamboo and that method always does a blind build, even if there are no changes to the project. I need to be able to identify if changes were made to the project.
Something like
'$(wasSourceUpdated)' == 'true' or some kind of target condition to use on my custom versioning target.
Here is a sample of my versioning task in our projects
<Import Project="..\..\DXT.BuildTasks\Targets\DXTAutoIncrementVersion.targets" Condition="Exists('..\..\DXT.BuildTasks\Targets\DXTAutoIncrementVersion.targets') And '$(Configuration)|$(Platform)' == 'Release|AnyCPU' And '$(DeployOnBuild)' != 'true'" />
I also checked this and this articles to no avail.
EDIT
I need the task to run before the build is actually executed in order to stamp the generated assemblies with the new versions
EDIT 2
What I'm really looking for is the condition to run CoreCompile or to run CoreCompile again when I detect that the assembly was updated
What I've tried so far:
<Project>
<PropertyGroup>
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
</PropertyGroup>
<PropertyGroup>
<_AssemblyTimestampBeforeCompile>%(IntermediateAssembly.ModifiedTime)</_AssemblyTimestampBeforeCompile>
</PropertyGroup>
<PropertyGroup>
<_AssemblyTimestampAfterCompile>%(IntermediateAssembly.ModifiedTime)</_AssemblyTimestampAfterCompile>
</PropertyGroup>
<PropertyGroup>
<_ProjectVersioned Condition="'$(_ProjectVersioned)'==''">false</_ProjectVersioned>
</PropertyGroup>
<Target Name="IncrementVersionBeforeBuild" AfterTargets="CoreCompile" Condition="'$(_AssemblyTimestampBeforeCompile)'!='$(_AssemblyTimestampAfterCompile)' and '$(_ProjectVersioned)' == 'false'">
<Message Text="Before $(_AssemblyTimestampBeforeCompile) After $(_AssemblyTimestampAfterCompile)" Importance="High"/>
<IncrementVersion
ProjectPath="$(MSBuildProjectFullPath)"
VersionRule="3.3.0.+"
FileName="Properties\AssemblyInfo.cs">
</IncrementVersion>
</Target>
<PropertyGroup>
<TaskPath>$(MSBuildThisFileDirectory)..\Tasks\AutoVersionTask\AutoVersionTask\bin\Debug</TaskPath>
</PropertyGroup>
<!-- Sample import for projects
<Import Project="..\..\DXT.BuildTasks\Targets\DXTAutoIncrementVersion.targets" Condition="Exists('..\..\DXT.BuildTasks\Targets\DXTAutoIncrementVersion.targets') And '$(Configuration)|$(Platform)' == 'Release|AnyCPU' And '$(DeployOnBuild)' != 'true'" />
-->
<UsingTask AssemblyFile="$(TaskPath)\AutoVersionTask.dll" TaskName="AutoVersionTask.IncrementVersion" />
<PropertyGroup>
<_ProjectVersioned>true</_ProjectVersioned>
</PropertyGroup>
Thanks in advance
So Thanks to Lance for getting me to understand MSBuild to the point that I understand the issue way better.
After a long time researching the default task, I ran upon this question that had the perfect solution to my issue. After applying the fix the versioning task now only runs when changes are made to the msbuild code.
The inputs and outputs are the same as the CoreCompile target and ensures that the task is only run if there were changes to the source
Here is the target I ran that works:
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<CoreCompileDependsOn>
$(CoreCompileDependsOn);
IncrementVersionBeforeBuild
</CoreCompileDependsOn>
</PropertyGroup>
<Target Name="IncrementVersionBeforeBuild"
Inputs="$(MSBuildAllProjects);
#(Compile);
#(_CoreCompileResourceInputs);
$(ApplicationIcon);
$(AssemblyOriginatorKeyFile);
#(ReferencePath);
#(CompiledLicenseFile);
#(EmbeddedDocumentation);
$(Win32Resource);
$(Win32Manifest);
#(CustomAdditionalCompileInputs)"
Outputs="#(DocFileItem);
#(IntermediateAssembly);
#(_DebugSymbolsIntermediatePath);
$(NonExistentFile);
#(CustomAdditionalCompileOutputs)"
>
<Message Text="Version Task running" Importance="High"/>
<IncrementVersion
ProjectPath="$(MSBuildProjectFullPath)"
VersionRule="3.3.0.+"
FileName="Properties\AssemblyInfo.cs">
</IncrementVersion>
</Target>
<PropertyGroup>
<TaskPath>$(MSBuildThisFileDirectory)..\Tasks\AutoVersionTask\AutoVersionTask\bin\Debug</TaskPath>
</PropertyGroup>
<UsingTask AssemblyFile="$(TaskPath)\AutoVersionTask.dll" TaskName="AutoVersionTask.IncrementVersion" />
<PropertyGroup>
<_ProjectVersioned>true</_ProjectVersioned>
</PropertyGroup>
</Project>
Normaly, we can add the script below into .csproj file:
<PropertyGroup>
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
</PropertyGroup>
<Target Name="AutoVersionWhenBuild" AfterTargets="CoreBuild"
Condition="'$(_AssemblyTimestampBeforeCompile)'!='$(_AssemblyTimestampAfterCompile)'">
<Message Importance="high" Text="Auto-version begins when changes are made!"/>
<!--<AutoVersionTask>Do your auto-version task here.</AutoVersionTask>-->
</Target>
It will be called during the build when changes are really made to the project. See this similar issue.
As for your situation:
It seems your tasks and target comes from the targets file DXTAutoIncrementVersion.targets,you can open that file and change the target in it to the format above.
In addition: Please check the relationship between tasks, targets and .targets file.
1.MSBuild uses tasks to perform these actions.
2.Targets group tasks together.
3.MSBuild includes several .targets files that contain items, properties, targets, and tasks for common scenarios.
So you can either modify your auto-version target in the xx.targets file, or use the script above, and call the auto-version task in the AutoVersionWhenBuild target. Hope it helps:)
Quick question about MSBuild. I have the following MSBuild file in directory d:\MyDirectory
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
<Import Project="d:\MyDirectory\BuildTest\BuildTest\BuildTest.csproj"></Import>
</Project>
When I run this the build fails because BuildTest.csproj can't find the .cs source files and it seems to be looking in d:\MyDirectory. I was expecting the working directory to be set to that of the BuildTest.csproj and thus be able to resolve the references. What am I doing wrong?
I think ,the import element is used to import other MSBuild projects into this one (see here).
If you want to specify C# projects to build, you should do it like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
<ItemGroup>
<ProjectReferences Include="d:\MyDirectory\BuildTest\BuildTest\BuildTest.csproj" />
<!--more-->
</ItemGroup>
Then if you want to build all referenced projects, you can for example do this:
<Target Name="BuildAllProjects">
<MSBuild ContinueOnError="False" Projects="#(ProjectReferences)"
Targets="Clean;ReBuild">
</MSBuild>
</Target>
Remember though, that this is just 1 of many ways of doing things with MSBuild. There are a lot of good articles on MSDN on this topic, should you require more customization.