/LinkResource in Visual Studio 2010

I managed to get this to work in vs2010 albeit with warnings.

1) Override CoreCompile to add LinkResources

2) Add a target to get the equivalent of Copy Local on anything added as <LinkResource Include="native.dll" />

3) If the project containing the native libraries/file only contains those files, add a type/interface. Use this type in the project using the references. I.e. the project where you use DllImport. The prevents the compiler optimising out the project dependency.

4) Tack on Alex Yakunin’s trick to copy project dependencies.

This pulls all the dependencies into my $(TargetDir) ready for debugging.

In the project files I import the magic after the regular targets files ala

  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <Import Project="$(ProjectDir)..\..\External\CopyDependencies.targets" />

And I have a CopyDependencies.targets as follows:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <Target
    Name="CoreCompile"
    Inputs="$(MSBuildAllProjects);
                @(Compile);                               
                @(_CoreCompileResourceInputs);
                $(ApplicationIcon);
                $(AssemblyOriginatorKeyFile);
                @(ReferencePath);
                @(CompiledLicenseFile);
                @(EmbeddedDocumentation); 
                $(Win32Resource);
                $(Win32Manifest);
                @(LinkResource);
                @(CustomAdditionalCompileInputs)"
    Outputs="@(DocFileItem);
                 @(IntermediateAssembly);
                 @(_DebugSymbolsIntermediatePath);                 
                 $(NonExistentFile);
                 @(CustomAdditionalCompileOutputs)"
    Returns=""
    DependsOnTargets="$(CoreCompileDependsOn)"
    >
    <!-- These two compiler warnings are raised when a reference is bound to a different version
             than specified in the assembly reference version number.  MSBuild raises the same warning in this case,
             so the compiler warning would be redundant. -->
    <PropertyGroup Condition="('$(TargetFrameworkVersion)' != 'v1.0') and ('$(TargetFrameworkVersion)' != 'v1.1')">
      <NoWarn>$(NoWarn);1701;1702</NoWarn>
    </PropertyGroup>

    <PropertyGroup>
      <!-- If we are building in visual studio setting the CscToolPath will prevent the inproc compiler from being used during compile-->
      <CscToolPath Condition="'$(CscToolPath)' == '' and '$(BuildingInsideVisualStudio)' != 'true'" >$(MsBuildToolsPath)</CscToolPath>
    </PropertyGroup>

    <ItemGroup Condition="'$(TargetingClr2Framework)'=='true'">
      <ReferencePath>
        <EmbedInteropTypes/>
      </ReferencePath>
    </ItemGroup>

    <PropertyGroup>
      <!-- If the user has specified AppConfigForCompiler, we'll use it. If they have not, but they set UseAppConfigForCompiler,
                 then we'll use AppConfig -->
      <AppConfigForCompiler Condition="'$(AppConfigForCompiler)' == '' and '$(UseAppConfigForCompiler)' == 'true'">$(AppConfig)</AppConfigForCompiler>
    </PropertyGroup>

    <!-- Condition is to filter out the _CoreCompileResourceInputs so that it doesn't pass in culture resources to the compiler -->
    <Csc  Condition=" '%(_CoreCompileResourceInputs.WithCulture)' != 'true' "
          AdditionalLibPaths="$(AdditionalLibPaths)"
          AddModules="@(AddModules)"
          AllowUnsafeBlocks="$(AllowUnsafeBlocks)"
          ApplicationConfiguration="$(AppConfigForCompiler)"
          BaseAddress="$(BaseAddress)"
          CheckForOverflowUnderflow="$(CheckForOverflowUnderflow)"
          CodePage="$(CodePage)"
          DebugType="$(DebugType)"
          DefineConstants="$(DefineConstants)"
          DelaySign="$(DelaySign)"
          DisabledWarnings="$(NoWarn)"
          DocumentationFile="@(DocFileItem)"
          EmitDebugInformation="$(DebugSymbols)"
          ErrorReport="$(ErrorReport)"
          FileAlignment="$(FileAlignment)"
          GenerateFullPaths="$(GenerateFullPaths)"
          KeyContainer="$(KeyContainerName)"
          KeyFile="$(KeyOriginatorFile)"
          LangVersion="$(LangVersion)"
          LinkResources="@(LinkResource)"
          MainEntryPoint="$(StartupObject)"
          ModuleAssemblyName="$(ModuleAssemblyName)"
          NoConfig="true"
          NoLogo="$(NoLogo)"
          NoStandardLib="$(NoCompilerStandardLib)"
          NoWin32Manifest="$(NoWin32Manifest)"
          Optimize="$(Optimize)"
          OutputAssembly="@(IntermediateAssembly)"
          PdbFile="$(PdbFile)"
          Platform="$(PlatformTarget)"
          References="@(ReferencePath)"
          Resources="@(_CoreCompileResourceInputs);@(CompiledLicenseFile)"
          ResponseFiles="$(CompilerResponseFile)"
          Sources="@(Compile)"
          TargetType="$(OutputType)"
          ToolExe="$(CscToolExe)"
          ToolPath="$(CscToolPath)"
          TreatWarningsAsErrors="$(TreatWarningsAsErrors)"
          UseHostCompilerIfAvailable="$(UseHostCompilerIfAvailable)"
          Utf8Output="$(Utf8Output)"
          WarningLevel="$(WarningLevel)"
          WarningsAsErrors="$(WarningsAsErrors)"
          WarningsNotAsErrors="$(WarningsNotAsErrors)"
          Win32Icon="$(ApplicationIcon)"
          Win32Manifest="$(Win32Manifest)"
          Win32Resource="$(Win32Resource)"

              />

    <ItemGroup>
      <_CoreCompileResourceInputs Remove="@(_CoreCompileResourceInputs)" />
    </ItemGroup>

    <CallTarget Targets="$(TargetsTriggeredByCompilation)" Condition="'$(TargetsTriggeredByCompilation)' != ''"/>

  </Target>


  <PropertyGroup>
    <CopyLinkedResources Condition="'$(CopyLinkedResources)'==''">true</CopyLinkedResources>
  </PropertyGroup>

  <PropertyGroup>
    <CopyDependencies
      Condition="'$(CopyDependencies)'==''">true</CopyDependencies>
    <CopyDependenciesPdb
      Condition="'$(CopyDependenciesPdb)'==''">true</CopyDependenciesPdb>
    <CopyDependenciesXml
      Condition="'$(CopyDependenciesXml)'==''">true</CopyDependenciesXml>
  </PropertyGroup>


  <Target Name="CopyLinkedResources">
    <Message Text="Copy Linked Resources"></Message>
    <Copy SourceFiles="@(LinkResource->'%(FullPath)')"
          DestinationFolder="$(OutputPath)"
          SkipUnchangedFiles="true">
      <Output TaskParameter="CopiedFiles"
        ItemName="LinkResourceCopied" />
    </Copy>
    <Message Text="Copy Linked Resource: %(LinkResourceCopied.FullPath)" Importance="low"/>
  </Target>



<Target Name="CopyIndirectDependencies"
          Condition="'$(CopyIndirectDependencies)'=='true'"
          DependsOnTargets="DetectIndirectDependencies"
          Inputs="@(IndirectDependencyToCopy)"
          Outputs="@(MatchingOutputDependency)">
    <Copy SourceFiles="@(IndirectDependencyToCopy)"
          DestinationFiles="@(MatchingOutputDependency)"
          SkipUnchangedFiles="true">
      <Output TaskParameter="CopiedFiles"
              ItemName="IndirectDependencyCopied" />
    </Copy>
    <Message Importance="low"
             Condition="'%(IndirectDependencyCopied.FullPath)'!=''
                         and '%(IndirectDependencyCopied.Extension)'!='.pdb'
                         and '%(IndirectDependencyCopied.Extension)'!='.xml'"
             Text="Indirect dependency copied: %(IndirectDependencyCopied.FullPath)" />
  </Target>

  <Target Name="DetectIndirectDependencies"
          DependsOnTargets="ResolveAssemblyReferences">

    <Message Importance="low"
             Text="Direct dependency: %(ReferencePath.Filename)%(ReferencePath.Extension)" />
    <Message Importance="low"
             Text="Indirect dependency: %(ReferenceDependencyPaths.Filename)%(ReferenceDependencyPaths.Extension)" />

    <!-- Creating indirect dependency list -->
    <ItemGroup>
        <DetectedIndirectDependency Include="%(ReferenceDependencyPaths.FullPath)" 
                                    Condition="'%(ReferenceDependencyPaths.CopyLocal)'=='true'"/>
    </ItemGroup>

    <ItemGroup Condition="'$(CopyIndirectDependenciesXml)'=='true'">
        <DetectedIndirectDependency Include="%(ReferenceDependencyPaths.RootDir)%(ReferenceDependencyPaths.Directory)%(ReferenceDependencyPaths.Filename).xml"
                                    Condition="'%(ReferenceDependencyPaths.CopyLocal)'=='true'" />
    </ItemGroup>  

    <ItemGroup Condition="'$(CopyIndirectDependenciesPdb)'=='true'">
        <DetectedIndirectDependency Include="%(ReferenceDependencyPaths.RootDir)%(ReferenceDependencyPaths.Directory)%(ReferenceDependencyPaths.Filename).pdb"
                                    Condition="'%(ReferenceDependencyPaths.CopyLocal)'=='true'"/>
    </ItemGroup>


    <!-- Work out which dependencies actually exist in the file system -->
    <ItemGroup>
        <IndirectDependencyToCopy Include="@(DetectedIndirectDependency)" 
                                  Condition="Exists('%(DetectedIndirectDependency.Identity)')" />          

        <MatchingOutputDependency Include="@(IndirectDependencyToCopy->'$(OutputPath)%(Filename)%(Extension)')" />
    </ItemGroup>

  </Target>

  <!-- Build sequence modification -->

  <PropertyGroup>
    <CoreBuildDependsOn>
      $(CoreBuildDependsOn);
      CopyDependencies;
      CopyLinkedResources
    </CoreBuildDependsOn>
  </PropertyGroup>
</Project>

Leave a Comment