Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MSBuild TargetOutputs doesn't include Foo.exe.config file?

Tags:

msbuild

I'm using the MSBuild task as follows:

<MSBuild Projects="Foo.csproj">
    <Output TaskParameter="TargetOutputs" ItemName="FilesToDeploy" />
</MSBuild>

However, the FilesToDeploy item group only includes the .EXE files built by the project file; it doesn't include the Foo.exe.config file.

Another question: Who copies App.config to App.exe.config? explains how exactly MSBuild finds/copies the config file, but I can't figure out how to actually get the name.

I found a similar question elsewhere, but that has no answers.

How do I solve this?

like image 857
Roger Lipscombe Avatar asked Aug 18 '10 14:08

Roger Lipscombe


1 Answers

Let me explain what is happening here. I'm gonna have to assume that the sub-project you are building here is an un-modified C-Sharp project.

By default, .csproj files have a Default Target of "Build", so when you use the MSBuild task here, it will be executing the default target in the Project. This is defined as

   <Target
        Name="Build"
        Condition=" '$(_InvalidConfigurationWarning)' != 'true' "
        DependsOnTargets="$(BuildDependsOn)"
        Outputs="$(TargetPath)"/>

The documentation for the MSBuild task indicates that the "TargetOutputs" output, will contain the items declared in the "Outputs" of that called target - in this case, it is a single item, namely the value of the property $(TargetPath) in the called project. This only contains the name of the built executable or DLL (note that not only is the .config missing but any referenced assemblies too!)

I'm withdrawing my original solution that gets all built outputs as it just does not work - it was a target I had in one of my utiltity import files, but on investigation it seems I did not actually use it anywhere, plus it required that you reference the Microsoft.CloudService.targets (which I am, but doubt you are).

What I think you are trying to do, is get all the files that make up Foo.exe (including .config) into one itemgroup so that you can copy them somewhere. This is pretty tricky, if you consider all the things that can make up the output of a csproj: primary output (.exe), config file, referenced assemblies marked copyLocal, satellite assemblies (your .resx DLLs), Content items etc etc.

This is all very easy to take care of if you know that when the csproj file is built, that it will output all the items into a single folder. This is true when building within Visual Studio, as it puts all the compilation outputs into bin\Debug. So you would think that FilesToDeploy is $(OutDir)\**\*.*. But this is not the case when your project is being built by TeamBuild - as TeamBuild will change the OutDir to be a global "Binaries" directory that every project outputs into (to prevent duplicate builds etc). That would mean then that FilesToDeploy would contain the content of that folder, which I doubt is what you want.

You no doubt have discovered this (or at least anticipate this), and that is why you have a target that is asking the .csproj "what are your build outputs, and where are they?". However, there is no single Target within Microsoft.Common.Targets that you can call that declares the entire set of build outputs. The Azure group attempted to solve that (and perhaps succeeded within the context of what they are trying to do), and I attempted to copy that in the previous solution that I had here, but it just doesn't work (or at least, I don't have the time to make it work).

This is not all ideal, but not all is lost if you revisit an asumption that I shot down: The problem would be easier to solve if we did know that all the build output would appear in a single folder when built both within VS and TeamBuild. Then FilesToDeploy would be a simple ***.* glob.

You could achieve this if you were to modify your TfsBuild.Proj and force the OutDir of your project to be (say) $(BinariesRoot)\FooExe:

<SolutionToBuild Include=$(SolutionRoot)\Sources\Foo.csproj">
    <Properties>
        OutDir=$(BinariesRoot)\FooExe\
    </Properties>
</SolutionToBuild>

I actually have a complete set of customized "bundling" targets - within a .csproj file I can declare that I want copies of the output to go to a set of directories, and the targets will copy all output to each directory. But, that's just something I had to introduce as I have spent the last few months gradually converting a legacy nmake.exe build to a native MSBuild, and I don't want to post that here and mess up this answer

like image 189
Peter McEvoy Avatar answered Oct 05 '22 03:10

Peter McEvoy