Search code examples
buildmsbuildvisual-studio-2017devenv

msbuild doesn't copy output of referenced native project to c# project out dir


I'm struggling with this for a long time now.

The setup:

  • c# project
  • c++ project
  • c# project has a reference for the c++ project with the following lines:

    <ProjectReference Include="projectB.vcxproj">
        <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
        <OutputItemType>Content</OutputItemType>
        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </ProjectReference>
    

This works from withing visual-studio. This works when using devenv from command line. When using msbuild from command line - the output file of the c++ project is not copied over into the output directory of the c# project.

I wasn't able to fix that using msbuild. Read a lot about it, nothing worked. Tried to debug in using diag verbosity - but logs of msbuild and visual-studio are very different... I can't turn to using devenv as the build machine doesn't have valid visual-studio.

In msbuild log with diagnostic verbosity I see: Target "GetCopyToOutputDirectoryItems" skipped. Previously built successfully. This is where in the visual-studio log - it looks different - and actually works on copying the referenced native files to the c# output directory. Perhaps something related with build order?..

In msbuild log - I also see: Target "_CopyOutOfDateSourceItemsToOutputDirectoryAlways" skipped, due to false condition; ( '@(_SourceItemsToCopyToOutputDirectoryAlways)' != '' ) was evaluated as ( '' != '' ). While in the visual-studio build log I see this target executed (it comes right after GetCopyToOutputDirectoryItems target)


Solution

  • Update 3: It seems that previous solutions cause unwanted side-effects such as breaking the build when running multi-threaded builds.

    Current solution, that does seem to work is to add: <Targets>Build;BuiltProjectOutputGroup</Targets> to the ProjectReference section.

    Update 2:

    Changing:

    Targets="%(_MSBuildProjectReferenceExistent.Targets)"

    to

    Targets="%(_MSBuildProjectReferenceExistent.Targets);GetTargetPath"

    in C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.targets, in the MSBuild task preceded by the comment Build referenced projects when building from the command line. - did the trick.

    However, I have no confidence in this solution, as I don't understand the entire build process. This is just a guess.

    Update 1:

    using /p:DesignTimeBuild=true affects dependency build order. Can't work. Continue investigation...

    Possible solution 1:

    After putting a lot of messaged into C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.targets

    I finally got to the following: QUIRKING FOR DEV10

    I'm still not exactly sure what that is all about, and I saw that the developers plan to remove this quirk (see https://github.com/Microsoft/msbuild/issues/1890).

    Suddenly DesignTimeBuild caught my eye in the following line:

    <Output TaskParameter="TargetOutputs" ItemName="_ResolvedProjectReferencePaths" Condition="'%(_MSBuildProjectReferenceExistent.ReferenceOutputAssembly)'=='true' or '$(DesignTimeBuild)' == 'true'"/>

    I know that inside visual-studio this work. Googling got me to https://github.com/Microsoft/msbuild/wiki/MSBuild-Tips-&-Tricks. From there the path was short to adding /p:DesignTimeBuild=true to the msbuild command line.

    That made it work. The referenced assembly was copied over.

    I don't think this should be the solution, but it works, and don't seem to break anything else (yet).

    Any other suggestions would be welcome.