There is a two-component solution if you do not want to edit solution files or projects, and you are happy that it works from the MSBuild command line, but not from Visual Studio.
Firstly, the error you get at startup:
MSBuild Solution1.sln /t:Foo
Is it not that ProjectA does not contain the target Foo, but the solution itself does not contain the target Foo. As @Jaykul suggests, setting the MSBuildEmitSolution environment variable will show the default targets contained in the meta-solution of the solution.
Using metapride as inspiration, you can enter a new file "before.Solution1.sln.targets" next to the solution file (the file name template is important) with the following contents:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Target Name="Foo"> <MSBuild Projects="@(ProjectReference)" Targets="Foo" BuildInParallel="True" Properties="CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" /> </Target> </Project>
The MSBuild element is basically just copied from the metaproj Publish Target solution. Adjust the target name and any other data according to your scenario.
With this file in place, you will now receive an error message that ProjectA does not contain the target Foo. ProjectB may or may not build in any case depending on cross-project dependencies.
So, secondly, to solve this problem, we need to give each project an empty Foo target, which is then redefined in projects that actually already contain one.
We do this by entering another file, for example, "EmptyFoo.targets" (the name is not important), which looks like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Target Name="Foo" /> </Project>
And then we get each project to automatically import this target file either by launching MSBuild with an additional property, for example:
MSBuild Solution1.sln /t:Foo /p:CustomBeforeMicrosoftCommonTargets=c:\full_path_to\EmptyFoo.targets
Or, include the CustomerBeforeMicrosoftCommonTargets property in the Properties attribute in the MSBuild element in the first goals file, where you can specify the full path relative to the $ (SolutionDir) property.
However, if you are ready to run Foo in conjunction with any default solution targets (e.g. Build, Rebuild, Clean, or Publish), you might be a little inspired to see how Pipeline Web Publishing in MSBuild uses the DeployOnBuild property to invoke the target publication on the web -projects in a solution containing other types of projects that do not support publication.
More information about before.Solution1.sln.targets file: http://sedodream.com/2010/10/22/MSBuildExtendingTheSolutionBuild.aspx