how use properties from Directory.Build.props in Import from project file of Visual Studio - 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.

Related

How do you specify common package versions in a common.props file?

We've got a simple common.props file that is basically doing what Directory.Build.props appears to do. It specifies various settings and is then imported to the csproj files that need it.
Here's the basics of the MyProject.common.props file:
<Project>
<PropertyGroup>
// some settings
</PropertyGroup>
<PropertyGroup>
<LangVersion>8.0</LangVersion>
//some more settings...
<MyNewVariable>3.1.22</MyNewVariable>
</PropertyGroup>
</Project>
Here's the basics of the csproj:
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\folderA\MyProject.common.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="$(MyNewVariable)" />
</ItemGroup>
</Project>
When I use this approach I get the error:
Project dependency Microsoft.Extensions.Hosting.WindowsServices does
not contain an inclusive lower bound. Include a lower bound in the
dependency version to ensure consistent restore results.
However, if I just input 3.1.22 where the variable is it works fine. This suggests to me that the variable is coming through empty or not being picked up. If I remove the variable altogether and leave the Version blank I get the same error. I also double checked the path to the file by misspelling the import and I get a different error, so I know it can at least find the common.props file.

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

Difficulty using <Import> to modularize a Visual Studio project file

I'm attempting to modularize a Visual Studio project file, but it's not working. This is for Visual Studio 2008 with .Net 3.5.
Shown below, the first example works, but the second one does not. How can I make it work..?
I'm new to this topic and probably missing something. I first became aware of it while reading a 3rd-party blog, and then found it in the documentation too. I've googled for more help, but there's too much information for me to find a relevant answer.
The main project file:
...
<!-- main project file -->
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
<Target Name="AfterBuild">
<Message Text="This is a message from the *.vbproj file."/> ... this works
</Target>
</Project>
...but if <Import> is used, with the same <Target> and <Message> in the imported file, it doesn't work. MSBuild seems to process everything correctly, but nothing happens...
The main project file:
...
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
<Import Project="$(MSBuildProjectDirectory)\CustomBuildEvents.targets" /> ... new tag
<Target Name="AfterBuild">
<Message Text="This is a message from the *.vbproj file."/> ... this still works
</Target>
</Project>
The imported targets file:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="AfterBuild">
<Message Text="Hello from the imported file." Importance ="high"/> ... doesn't work
</Target>
</Project>
And the build output, with Verbosity set to Diagnostic:
### Halfway through the output, this is the only mention of the imported file. ###
None
CustomBuildEvents.targets ... custom file
My Project\Application.myapp
My Project\Settings.settings
### And then at the end, no mention of the imported file or its message. ###
Done building target "CoreBuild" in project "MsBuildCustomTargetTester.vbproj".
Target "AfterBuild" in file "C:\Visual Studio 2008\Solutions\MsBuildCustomTargetTester\MsBuildCustomTargetTester\MsBuildCustomTargetTester.vbproj":
Task "Message"
Hello from the *.vbproj file. ... message from main file
Done executing task "Message".
Done building target "AfterBuild" in project "MsBuildCustomTargetTester.vbproj".
Target "Build" in file "c:\WINDOWS\Microsoft.NET\Framework\v3.5\Microsoft.Common.targets":
Building target "Build" completely.
No input files were specified.
Done building target "Build" in project "MsBuildCustomTargetTester.vbproj".
Done building project "MsBuildCustomTargetTester.vbproj".
Project Performance Summary:
109 ms C:\Visual Studio 2008\Solutions\MsBuildCustomTargetTester\MsBuildCustomTargetTester\MsBuildCustomTargetTester.vbproj 1 calls
The problem with AfterBuild is that it can only be defined once. So if you import it and then later in the project file define it again, the last definition wins and becomes the only definition.
To solve this you need to use the more advanced way to register events. Given that you are using Visual Studio 2008 (WHY?!), you need to use the more advanced syntax for your custom targets files:
<Project>
<!--
Redefines the original build order, includes the standard targets and
adds your new custom target to the end of the list.
-->
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);
CustomTarget
</BuildDependsOn>
</PropertyGroup>
<CustomTarget>
<!-- Imported after Build -->
<Message Text="Hello from the imported file." Importance ="high"/>
</CustomTarget>
</Project>
There are other ways to do this which were introduced in MsBuild 4 with the BeforeTargets and AfterTargets attributes on any target, but If I'm remembering correctly the above syntax should also work with the version of MsBuild that ships with Visual Studio 2008.
See also:
What is the difference between 'DependsOnTargets' and 'AfterTargets'?
https://blogs.msdn.microsoft.com/msbuild/2006/02/10/how-to-add-custom-process-at-specific-points-during-build-method-2/

Update Post-Build with custom nuget package

I need help to be able to update the post-build event of a project thanks to a custom nuget package.
I've created a package thanks to a nuspec file that include a .targets file :
<file src="*.targets" target="build"/>
Here is the .targets file :
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="AfterBuild" AfterTargets="Build" >
<Message Text="Hello World" Importance="high" />
</Target>
</Project>
Actually, the file is read when i install the package (if i delete some '>', i've an error).
But the .csproj isn't updated (so, nothing in the post-build event textbox).
Did i miss something ?
I agree with Matt Ward. From NuGet 2.5, NuGet recognizes a new top-level folder: \build.
Within the \build folder, you can provide a “.props” file and/or a
“.targets” file that will be automatically imported into the project.
For this convention, the file name must match your package id with
either a “.props” or “.targets” extension.
Please refer to the MSBuild Integration part in following document:
http://blog.nuget.org/20130426/native-support.html
And after install the package, you will see a import node in .csproj which import the package.targets file. Then when build your project, you will see "Hello World" text in output window.

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.

Resources