Can I add a common reference path in an MSBuild target/project file? - visual-studio

I'm trying to write a custom project (targets?) file that is to be included in several projects.
For example, inside all of my .csproj and .vbproj files I have:
<Import Project="..\MyCustomTargets\custom.targets"/>
Inside that file I have a custom target (AfterBuild) which copies the compiled files to another location.
However, I'd like to add a reference path that each project can look to when trying to resolve references. Is this possible?
For example, I'd like to add something like this to my .targets file:
<AdditionalReferencePath>C:\LookHereForReferences</AdditionalReferencePath>
I've found a few links that describe a little about how to do this but I can't get it working.

I'd like to add a reference path that each project can look to when trying to resolve references. Is this possible?
You can set a Property Group in your .targets file:
<PropertyGroup>
<AdditionalReferencePath>C:\LookHereForReferences</AdditionalReferencePath>
</PropertyGroup>
After import this targets file in to the project file, you can look it by $(AdditionalReferencePath) when trying to resolve references:
<Import Project="Common.targets" />
<Target Name="Test" AfterTargets="Build">
<Message Text="$(AdditionalReferencePath)"></Message>
</Target>

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"/>

how use properties from Directory.Build.props in Import from project file of Visual Studio

I have this situation:
I have a .proj file in project directory:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Include="PQExtensionTest.pq">
<SubType>Code</SubType>
</Compile>
<Content Include="PQExtensionTest.query.pq">
<SubType>Code</SubType>
</Content>
</ItemGroup>
<!-- <Import Project="..\Directory.Build.props" /> -->
<Import Project="$(aProperty)add.targets" />
</Project>
In the solution directory (..\ from project directory) I have file Directory.Build.props:
<Project DefaultTargets="BuildExtension" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<aProperty>$(MSBuildProjectDirectory)/Subdir/</aProperty>
</PropertyGroup>
</Project>
In the project directory I have subdirectory "Subdir", where there is file add.targets, which contains all the targets I need (do not show it's contains here because it is not relevant to the problem).
So all above has this folder structure:
Solution directory
Directory.Build.props
Project Directory
Project.mproj
Subdir
add.targets
Preparing all the above, I expected that aProperty will be initiated before the import and the import of add.targets will happen without problem. But I get error that imported project is not found, and I see in error message that MSBuild tries to import from project directory, and not from subdirectory Subdir.
If I uncomment this row:
<Import Project="..\Directory.Build.props" />
all works fine.
The only reasonable explanation for me of such behavior is that aProperty is empty at the moment of importing, because explicit import happens before implicit one.
Is there any way to force MSBuild to inexplicitly import Directory.Build.props before any other imports, while work in Visual Studio?
"While in Visual Studio"
For C# and VB language project, we don't need to import
Directory.Build.props manually or force it before other imports.
When creating a new project(C# or VB) in VS, open its proj file we can find the format is like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
...
</PropertyGroup>
<ItemGroup>
...
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
Every time when creating new C# or VB project, the top line within the <Project>node is Import project="Microsoft.Common.props", and we can find the sentence from this document:
When MSBuild runs, Microsoft.Common.props searches your directory structure for the Directory.Build.props file (and Microsoft.Common.targets looks for Directory.Build.targets). If it finds one, it imports the property.
So in visual studio, we don't need to force it before other imports.Its always called after import Microsoft.Common.props, and since the import Microsoft.Common.props is always first line of project node by default, the Directory.Build.Targets is always implicitly imported right after the Microsoft.Common.props and before others.
Note: This feature only supports C# and VB, cause only these two kinds of projects will import the Microsoft.Common.Props in proj file.
And for other kinds of projects, just like your .mproj or
.vcxproj(C++), this feature(Directory.Build.props) is not supported
yet.
So the Directory.Build.Targets or .props is the same as any custom .props. It doesn't make difference between Directory.Build.Targets and anyName.props.
In this way,to read the value in it we have to use import project to call it manually. And that's why the build can't succeed until you uncomment the row:<Import Project="..\Directory.Build.props" />
The way to import properties from 'Directory.Build.props' file from nested folder structure is given below:
Refer: https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2019
Note that:
1. These property file definition works from MSBuild tools version 15.0
2. You need to be aware of where to place this import: At the beginning of the file or at the end of the file. Generally it is good to place at the end as nested properties will be visible to parent properties.

$(ProjectDir) prebuild event macro incorrect

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.

Is there a way to add source files to visual studio project from command-line?

I want to use sublime to edit a visual studio project.
I have a custom build:
{
"cmd": ["c:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\MSBuild.exe"],
"working_dir": "${project_path:${folder:${file_path}}}/../Project"
}
But if I add new files I also need to include them in the project.
Is there a way to do this from the command line, maybe at compile-time?
I am working with opengl using c++;
I basically set up a project using one of the examples provided on the opengl website.
Then I opened the project folder in sublime text and successfully compiled it using the custom build system.
However, when I add NEW source files to the project (*.h and *.cpp) I get a linking error.
I get the same error when I build in visual studio.
The error disappeared after I had included the files by manually browsing and adding them to the project.
What I wanted was a way to automatically add all the source files in a folder to the project(via command line, or wildcard or smth else).
This way I can easily work on a vs2010 project in sublime, add new source files and build the project.
Or maybe there already is a better workflow for this?
You could try to modify your .vcxproj file to include any .h and .cpp file in your project folder or folders below.
In case of a c++ VS project you can try to alter your .vcxproj file like this:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- rest of project file untouched -->
<!-- start of modified part -->
<ItemGroup>
<ClInclude Include="**\*.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="**\*.cpp" />
</ItemGroup>
<!-- end of modified part -->
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
Be aware that adding files to your project from inside VS at later point will replace the modification described above!
As an alternative you could also create an external project file holding the same <ItemGroup /> elements described above and include this project file into your .vcxproj.
I'll add an example of this alternative if you're interested.

Error MSB4062 when trying to use ServiceController

I use Visual Studio 2010 with TFS 2010 on a x64 machine.
I am trying to use the MSBuild Community Tasks target in my build. This target exists in source control. So in my csproj file i am import that particular target but i now get the following error:
error MSB4062: The "MSBuild.Community.Tasks.Attrib" task could not be loaded from the assembly C:\Program Files (x86)\MSBuild\MSBuildCommunityTasks\MSB
uild.Community.Tasks.dll. Could not load file or assembly 'file:///C:\Program F
iles (x86)\MSBuild\MSBuildCommunityTasks\MSBuild.Community.Tasks.dll' or one of
its dependencies. The system cannot find the file specified. Confirm that the
declaration is correct, that the assembly and all its dependencies
are available, and that the task contains a public class that implements Micros
oft.Build.Framework.ITask. [C:\SampleTest\SampleTest.csproj]
Here is my code:
<Import Project="..\..\Builds\Common\MSBuildTasks\MSBuild.Community.Tasks\MSBuild.Community.Tasks.Targets" />
<Target Name="BeforeBuild">
<PropertyGroup>
<MyService>ServiceName</MyService>
</PropertyGroup>
<ServiceController ServiceName="$(MyService)" Action="Stop" />-->
</Target>
Any thoughts on the above?
Why is MSBuild trying to look for the dll elsewhere when i have specified it in the project file?
Thanks in advance,
I think the problem comes from within the MSBuild.Community.Tasks.Targets file - it is this file that actually references the MSBuild.Community.Tasks.dll assembly.
If you open the file you can see a bunch of UsingTask elements, such as:
<UsingTask AssemblyFile="$(MSBuildCommunityTasksLib)" TaskName="MSBuild.Community.Tasks.Attrib" />
The $(MSBuildCommunityTasksLib) property is defined at the top of the file as:
<PropertyGroup>
<MSBuildCommunityTasksPath Condition="'$(MSBuildCommunityTasksPath)' == ''">$(MSBuildExtensionsPath)\MSBuildCommunityTasks</MSBuildCommunityTasksPath>
<MSBuildCommunityTasksLib>$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.dll</MSBuildCommunityTasksLib>
</PropertyGroup>
So it looks like you need to set the $(MSBuildCommunityTasksPath) property before calling <Import>.

Resources