Include MsBuild files in multi-repo solution - visual-studio

I would like to add some MsBuild files, which are in different Git repositories, but are always built at the same time, to a common Visual Studio solution. In VS2022, support for multi-repo solutions has been added, which seems like a useful feature for me.
My MsBuild files are quite simple. They do not compile anything; they only run an external tool or print summaries of item lists.
I have renamed the extension of my files from .proj to .msbuildproj, since .proj was not an accepted project format in Visual Studio. Still, Visual Studio will not accept my MsBuild files as project files ("cannot be opened by the project system", "missing some critical imports").
What is the recommended project/solution type to go with in this case? What would a valid .msbuildproj file look like?
Sample MsBuild file:
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<Import Project="MyFiles.xml"/>
<Target Name="Build" DependsOnTargets="PrintSourceFiles"/>
<Target Name="PrintSourceFiles">
<ItemGroup>
<MyList Include="#(MyFirstFiles);#(OtherTexts);#(TheRestOfTheFiles)"></MyList>
</ItemGroup>
<Message Text="%(MyList.FullPath)" />
</Target>
</Project>

Related

Create proj file that loads csproj files into solution explorer when opened with visual studio

I am building a project file for our application that I am going to execute from our build machine. I was wondering if it is possible for me to open the project file and get the same view visual studio gives me of the solution when I open a solution file.
So here is my Contosa.proj file so far.
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)'=='' ">Debug</Configuration>
<RootNamespace>Contosa</RootNamespace>
<AssemblyName>Contosa</AssemblyName>
</PropertyGroup>
<ItemGroup>
<Projects Include="C:\Users\localuser\Documents\Perforce\Contosa\Branches\Working23\UI\Desktop\ContosaClient\ContosaClient.csproj" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="Build">
<PropertyGroup>
<Contosa>$(MSBuildProjectDirectory)\UI\Desktop\ContosaClient\ContosaClient.csproj</Contosa>
</PropertyGroup>
<MSBuild Projects="$(Contosa)"
Properties="Configuration=QA;
VisualStudioVersion=12.0;
DevEnvDir=C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\;
SolutionDir=$(MSBuildProjectDirectory)\"/>
</Target>
</Project>
Currently double clicking this file with visual studio as its associated application will open the Contosa.proj for text editing. I would really like it to be possible to associate my Contosa.proj file with visual studio like our Contosa.sln file is. So when developers open it with visual studio they get the same view that you get from the Contosa.sln. I don't understand what parts of a csproj or a sln file make them open as projects or solutions in visual studio.
UPDATE 1
I am looking to do what this user Replace .sln with MSBuild and wrap contained projects into targets did but I want the project file to be able to opened by the user like a solution file. I want the Projects I include to be loaded into the solution explorer.
When visual studio installs, it configures explorer to launch visual studio when a .csproj is doubleclicked (How can I set a file association from a custom file type to a program). Your windows, and your developers windows, don't know what a .proj is. So rename it .csproj or distribute a .sln which references .proj

How to build visual studio solution file .sln command line with selected project

I have .sln file which has around 352 projects.
I have created batch file below
"C:/Windows/Microsoft.NET/Framework/v4.0.30319/msbuild.exe myproj.sln /nologo /t:Build /p:Configuration="Debug" /property:Platform="Win32"
pause"
But this builds all the projects from solution. I have configured "Configuration Manager" to unselect unwanted projects or unload project and used above batch file to build but that did not serve the purpose.
I have searched MSBuild options but could not find exact answer.
Can anyone help me in this ?
MSbuild accepts project name as part of "target" (/t) specification:
MSBuild /nologo /t:ProjectName:Build SolutionFile.sln
Or, if your project does not depend on other projects in solution, directly use project file with MSBuild:
MSBuild /nologo /t:Build ProjectFile.vcxproj
This trick works with Visual Studio 2010, 2012, 2013, 2015, 2017, 2019 and with the latest MSVS 2022.
You could make a solution containing just the projects you want to build. Alternatively you could make an MSBuild .proj file that collects together the projects you want to build:
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ProjectsToBuild Include="X.csproj;Child\Y.csproj" />
</ItemGroup>
<PropertyGroup>
<Configuration>Release</Configuration>
</PropertyGroup>
<Target Name="Build">
<MSBuild Projects ="#(ProjectsToBuild)" ContinueOnError ="false" Properties="Configuration=$(Configuration)">
<Output ItemName="OutputFiles" TaskParameter="TargetOutputs"/>
</MSBuild>
</Target>
</Project>
Derived from http://sedodream.com/PermaLink,guid,ed3a0c98-fdac-4467-9116-5b3bf6755abc.aspx.

How do I use an MSBuild file from Visual Studio 2012?

I have a simple MSBuild file that I'm learning with.
Here it is:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Clean" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{D5A16164-962E-4A6D-9382-240F31AB6C50}</ProjectGuid>
</PropertyGroup>
<Target Name="Clean">
<ItemGroup>
<BinFiles Include="bin\*.*" />
<fff Include="f\*.*" />
</ItemGroup>
<Delete Files="#(BinFiles)" />
<Delete Files="#(fff)" />
</Target>
</Project>
Now I want to include this in a Visual Studio solution and be able to run the "clean" target from Visual Studio 2012. I tried naming it testproject.msbuildproj like the internet seems to suggest "works", but it doesn't work. When I run the clean command I just get "unexpected error".
If I rename the project to testproject.csproj, it does some unintuitive things like creating compilation directories, but it does actually run my clean command properly. However, this is undesireable because it creates obj and bin/x86/debug type directories. It also looks goofy in Visual Studio because it still gives the References drop down.
How can I use just a plain vanilla MSBuild project from Visual Studio without random errors or false assumptions?
Note I only am having a problem with this from Visual Studio. Using msbuild from the command line it works perfectly
Visual Studio creates bin / obj folders when it opens csproj file. When you click Build / Rebuild / Clean it just uses appropriate targets from the project file.
You cannot stop VS from creating these folders, but you can ask it to create them in say temp folder by setting appropriate properties - refer this MSDN article for details.
So the steps are to rename your project to csproj, and add the following lines into project:
<PropertyGroup>
<OutputPath>$(Temp)\bin</OutputPath>
<IntermediateOutputPath>$(Temp)\obj</IntermediateOutputPath>
</PropertyGroup>
I usually use a bit different approach to work with MSBUILD files from VS:
I use regular csproj file with removed Import ... CSharp.targets part as pure container for my Build projects.
I add actual build files with targets and logic, and all properties, necessary artifacts like XSLT etc using "Include into project", so I can manage hierarchy and change any file from within VS.Net.
I redefine Build / Rebuild targets in csproj file for whatever I need, for example Build may contain minimum output, and while rebuild diagnostic one.
Like this:
<Target Name="Build">
<Exec Command="%windir%\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe Builds\build.proj /t:Build /v:m" />
</Target>
<Target Name="Rebuild">
<Exec Command="%windir%\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe Builds\build.proj /t:Build /v:d" />
</Target>

Build doesn't work from VisualStudio, but is ok from msbuild

From a brand new console application template in visual studio, I edited the .csproj to build another project like this:
...
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="BeforeBuild">
<MSBuild Projects=".\other.mproj"/>
</Target>
...
Where other.mproj is:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<Target Name="Build">
<Message Text="kikou" />
</Target>
</Project>
After a while I discovered that modifying the content of other.mproj (for instance, by introducing errors or changing text kikou to something else) would not be taken into account unless unloading/reloading the project in visual studio.
Building from the command line with 'msbuild myproj.csproj' immediatly detect changes in 'other.mproj'. So it all looks like visual studio is working from a cached version of other.mproj file.
Why is visual studio caching this other script (which is even not included to the project), and how can I solve this issue ?
Update: I also tried this UseHostCompilerIfAvailable, it doesn't work.
NB1: I didn't add other.mproj as a project reference in the .csproj because it is not a .NET project at all (it just creates resources files for the .csproj from other inputs before the build)
NB2: I'm using VS2010 (10.0.10219.1SP1Rel + MSBuild 4.0.30319.1)
Visual Studio caches all MSBuild files, this is done for performance reasons. You will not be able to have an MSBuild only way around this. It may be possible to achieve this via a VS add-in but I'm not 100% sure of that.

How can I use a single Visual Studio solution to build both x86 and x64 at the same time?

I've got an x86 Visual Studio solution with many project files in it. Some of the DLL files are designed to work as plug-ins to other applications on a user's system.
We're expanding some of the DLL files to be able to support 64-bit applications. I'd like to set up the solution/projects so that just hitting "Build" will build both the x86 and x64 versions of those DLL
files. The solution contains both C++ and C# projects.
I realize that "Batch Build" is capable of building both, though it would be more convenient if developers could just click the same button as they have previously and have all of the output DLL files generated.
Here are a couple of the modifications that I've tried to a test project, but that I haven't gotten to work:
I've tried modifying the <Target Name="AfterBuild"> to try:
<Target Name="AfterBuild" Condition=" '$(Platform)' == 'x86' ">
<PropertyGroup>
<Platform>x64</Platform>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<CallTarget Targets="Build"/>
</Target>
But that results in the following error:
C:\Windows\Microsoft.NET\Framework\v3.5\Microsoft.Common.targets(565,5): error MSB4006: There is a circular dependency in the target dependency graph involving target "Build".
I think my conditions will prevent infinite recursion, but I understand how MSBuild could not see it that way.
I've also tried:
<Project DefaultTargets="MyBuild86;MyBuild64" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
...
<Target Name="MyBuild86">
<PropertyGroup>
<Platform>x86</Platform>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<CallTarget Targets="Build"/>
</Target>
<Target Name="MyBuild64">
<PropertyGroup>
<Platform>x64</Platform>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<CallTarget Targets="Build"/>
</Target>
But my DefaultTargets appears to be ignored from within the Visual Studio IDE.
Last, I've tried creating a separate project that imports the first project:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform>x64</Platform>
<PlatformTarget>x64</PlatformTarget>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<OutputPath>..\$(Configuration)\x64\</OutputPath>
<ProjectGuid>{A885CAC3-2BBE-4808-B470-5B8D482CFF0A}</ProjectGuid>
</PropertyGroup>
<Import Project="BuildTest.csproj" />
</Project>
And this so far has shown the most promise. However, Visual Studio seems to ignore my OutputPath setting from this new project and instead outputs the EXE/DLL file to the path specified in the original project. There isn't any PropertyGroup block that I can see that is being executed in the original project to override this, so I'm not sure what's happening.
We do something similar to build core assemblies for .NET Compact Framework.
Try this:
<Target Name="AfterBuild">
<MSBuild Condition=" '$(Platform)' == 'x86' " Projects="$(MSBuildProjectFile)" Properties="Platform=x64;PlatFormTarget=x64" RunEachTargetSeparately="true" />
</Target>
Importing a project in such manner works for me in Visual Studio 2010:
TestProject64.vcxproj
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="TestProject.vcxproj" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{B7D61F1C-B413-4768-8BDB-31FD464AD053}</ProjectGuid>
</PropertyGroup>
</Project>
TestProject64.vcxproj.filters
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="TestProject.vcxproj.filters" />
</Project>
TestProject.vcxproj has two configurations defined inside: Release|x86 and Release|x64. As you can see, TestProject64.vcxproj has only the Release|x64 configuration. Defining of at least one configuration in TestProject64.vcxproj is necessary, otherwise Visual Studio will not be able to add TestProject64.vcxproj to a solution.
Now it's possible to include both TestProject.vcxproj and TestProject64.vcxproj to the same solution and build Release|x86 and Release|x64 at the same time.
I think the best way of doing this is to invoke MSBuild from the command line. It shouldn't need editing of MSBuild files. Just run
msbuild myproj.sln /p:Configuration="Debug|Win32"
msbuild myproj.sln /p:Configuration="Debug|x64"
I assume that if a developer is using Visual Studio then they'll only be generating the DLL files so they can debug with them, and that you have a separate build process if you're actually deploying the DLL files.
For C++, and if it's a project whose files/settings don't change often, one way to do it is create two projects within the solution, with both projects referring to the same source files. Then, in x64 builds, set one project to build 64-bit and the other 32-bit. (In x86 builds, set one as 32-bit and turn off the other.)
We've been using this for a while and it works fine.
Of course, you have to be careful that any changes you make to one are also made to its copy. i.e. if you add/remove a file or change its build setting, you have to do it in two places. Source-code changes still only need to be done once, because there's still only one copy of each source file.
And, of course, you may decide that doing this is more complex/risky than switching away from using the IDE. In our case it's worked really well, though.
You are not going to be able to do this with the UI of Visual Studio. For this you will need to hack the MSBuild files.
Try this link from MSDN for MSBuild Overview
I would suggest to create a dummy C++ Makefile project and then invoke MSBuild twice from it:
msbuild myproj.sln /p:Configuration="Debug|Win32"
msbuild myproj.sln /p:Configuration="Debug|x64"
Perhaps I've missed the point of this discussion.
Using Visual Studio, go to menu Build → Configuration Manager. In the Active Solution Platform drop down, select "New...", and a New Solution Platform dialog appears. Select x64 and accept the default Copy From. Close the dialog and the Configuration Manager.
Now open menu Build → Batch Build. Check those configurations you want to build and build them. You will find the x64 build executables separate from the Win32 executable files.
You can verify that these are what was intended by right clicking on the executable files, selecting Properties, and select the Compatibility tab. In the dropdown window you can check to see what operating systems the executable file can be run in.
Obviously, there may be some other tweaking you might have to do to get all the output files in their proper places, but this method seem somewhat simpler than fooling with build than those described above.
I ran into this problem with a project running in Visual Studio 2008 (on Windows XP) (32-bit) and also Visual Studio 2010 (on Windows 7) (64-bit).
The solution I used was to use the $(PROGRAMFILES) variable. It resolved correctly on both machines.

Resources