<!--

Copyright 2020 Aaron R Robinson

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

DNNE.targets

-->
<Project>
  <Import Project="DNNE.Analyzers.targets"/>

  <PropertyGroup>
    <!-- Let the user define the name of the native binary -->
    <DnneNativeExportsBinaryName>$(DnneNativeBinaryName)</DnneNativeExportsBinaryName>

    <DnneLanguage Condition="'$(DnneLanguage)' == ''">c99</DnneLanguage>

    <!-- If the user didn't define the name compute one using the legacy mechanism -->
    <DnneNativeBinarySuffix Condition="'$(DnneNativeBinarySuffix)'==''">NE</DnneNativeBinarySuffix>
    <DnneNativeExportsBinaryName Condition="'$(DnneNativeExportsBinaryName)'==''">$(TargetName)$(DnneNativeBinarySuffix)</DnneNativeExportsBinaryName>
    <DnneNativeExportsBinaryPath>$(TargetDir)</DnneNativeExportsBinaryPath>
    <DnneGeneratedOutputPath>$(IntermediateOutputPath)dnne</DnneGeneratedOutputPath>
    <DnneGeneratedBinPath>$(DnneGeneratedOutputPath)/bin</DnneGeneratedBinPath>

    <!-- If build was disabled, change the generated output directory to
        where we would have put the compiled binary -->
    <DnneGeneratedOutputPath Condition="'$(DnneBuildExports)' != 'true'">$(DnneNativeExportsBinaryPath)</DnneGeneratedOutputPath>

    <DnneGenRollForwardFlag Condition="'$(DnneGenRollForward)' != ''" >--roll-forward $(DnneGenRollForward)</DnneGenRollForwardFlag>
    <!-- Respect the official way to define the host path -->
    <DnneDotnetHostPath>$(DOTNET_HOST_PATH)</DnneDotnetHostPath>
    <DnneDotnetHostPath Condition="'$(DnneDotnetHostPath)' == ''">dotnet</DnneDotnetHostPath>
    <DnneGenExe>"$(DnneDotnetHostPath)" $(DnneGenRollForwardFlag) "$(MSBuildThisFileDirectory)../tools/dnne-gen.dll"</DnneGenExe>
    <DnnePlatformSourcePath>$(MSBuildThisFileDirectory)../tools/platform</DnnePlatformSourcePath>

    <!-- Compute the extension for the export binary. -->
    <DnneNativeBinaryExt Condition="$([MSBuild]::IsOsPlatform('Windows'))">.dll</DnneNativeBinaryExt>
    <DnneNativeBinaryExt Condition="$([MSBuild]::IsOsPlatform('OSX'))">.dylib</DnneNativeBinaryExt>
    <DnneNativeBinaryExt Condition="'$(DnneNativeBinaryExt)' == '' OR $([MSBuild]::IsOsPlatform('Linux'))">.so</DnneNativeBinaryExt>

    <!-- Rust export binary extension -->
    <DnneNativeBinaryExt Condition="'$(DnneLanguage)' == 'rust'"></DnneNativeBinaryExt>

    <!-- Compute details about TFM -->
    <DnneIsNetFramework>false</DnneIsNetFramework>
    <DnneIsNetFramework Condition="$(TargetFramework.StartsWith('net4'))">true</DnneIsNetFramework>
    <DnneSupportedTFM>true</DnneSupportedTFM>
    <DnneSupportedTFM Condition="!$([MSBuild]::IsOsPlatform('Windows')) AND '$(DnneIsNetFramework)' == 'true'">false</DnneSupportedTFM>

    <!-- Define the target RID for the nethost needed during DNNE compilation -->
    <DnneRuntimeIdentifier Condition="'$(DnneRuntimeIdentifier)' == ''">$(RuntimeIdentifier)</DnneRuntimeIdentifier>
    <DnneRuntimeIdentifier Condition="'$(DnneRuntimeIdentifier)' == ''">$(NETCoreSdkRuntimeIdentifier)</DnneRuntimeIdentifier>

    <!-- Explicitly define an AppHost RID to trigger AppHost package resolution and download.
        This is only set if build is needed. -->
    <AppHostRuntimeIdentifier Condition="'$(DnneBuildExports)' == 'true'">$(DnneRuntimeIdentifier)</AppHostRuntimeIdentifier>

    <DnneCompiledToBinPath>$(DnneGeneratedBinPath)/$(DnneNativeExportsBinaryName)$(DnneNativeBinaryExt)</DnneCompiledToBinPath>

    <!-- Compute generated source file extension based on language -->
    <DnneGeneratedSourceFileExt Condition="'$(DnneLanguage)' == 'rust'">.g.rs</DnneGeneratedSourceFileExt>
    <DnneGeneratedSourceFileExt Condition="'$(DnneGeneratedSourceFileExt)' == ''">.g.c</DnneGeneratedSourceFileExt>
    <DnneGeneratedSourceFileName>$(DnneGeneratedOutputPath)/$(TargetName)$(DnneGeneratedSourceFileExt)</DnneGeneratedSourceFileName>
  </PropertyGroup>

  <ItemGroup>
    <DnneGeneratedSourceFile
        Include="$(DnneGeneratedSourceFileName)"
        Condition="'$(DnneGenerateExports)' == 'true'" />

    <!-- C99: generated .g.c acts as header, deploy with .h extension -->
    <DnneNativeExportsInput
        Include="$(DnneGeneratedSourceFileName)"
        Condition="'$(DnneGenerateExports)' == 'true' AND '$(DnneLanguage)' == 'c99'" >
      <OutputFileName>$(DnneNativeExportsBinaryName).h</OutputFileName>
    </DnneNativeExportsInput>

    <DnneNativeExportsInput
        Include="$(DnnePlatformSourcePath)/dnne.h"
        Condition="'$(DnneGenerateExports)' == 'true' AND '$(DnneLanguage)' == 'c99'" >
      <OutputFileName>dnne.h</OutputFileName>
    </DnneNativeExportsInput>

    <DnneNativeExportsInput
        Include="$(DnneCompiledToBinPath)"
        Condition="'$(DnneBuildExports)' == 'true' AND '$(DnneLanguage)' == 'c99'" >
      <OutputFileName>$(DnneNativeExportsBinaryName)$(DnneNativeBinaryExt)</OutputFileName>
    </DnneNativeExportsInput>

    <!-- Import libs exist only on the Windows platform for C99 builds -->
    <DnneNativeExportsInput
        Include="$(DnneGeneratedBinPath)/$(DnneNativeExportsBinaryName).lib"
        Condition="$([MSBuild]::IsOsPlatform('Windows')) AND '$(DnneBuildExports)' == 'true' AND '$(DnneLanguage)' == 'c99'" >
      <OutputFileName>$(DnneNativeExportsBinaryName).lib</OutputFileName>
    </DnneNativeExportsInput>

    <!-- Rust: intermediate generated crate files used as inputs for incremental builds -->
    <DnneNativeExportsInput
        Include="$(DnneGeneratedOutputPath)/Cargo.toml"
        Condition="'$(DnneBuildExports)' == 'true' AND '$(DnneLanguage)' == 'rust'" >
      <OutputFileName>Cargo.toml</OutputFileName>
    </DnneNativeExportsInput>

    <DnneNativeExportsInput
        Include="$(DnneGeneratedOutputPath)/lib.rs"
        Condition="'$(DnneBuildExports)' == 'true' AND '$(DnneLanguage)' == 'rust'" >
      <OutputFileName>lib.rs</OutputFileName>
    </DnneNativeExportsInput>

    <DnneNativeExportsInput
        Include="$(DnneGeneratedOutputPath)/build.rs"
        Condition="'$(DnneBuildExports)' == 'true' AND '$(DnneLanguage)' == 'rust'" >
      <OutputFileName>build.rs</OutputFileName>
    </DnneNativeExportsInput>

    <DnneNativeExportsInput
        Include="$(DnneGeneratedOutputPath)/platform.rs"
        Condition="'$(DnneBuildExports)' == 'true' AND '$(DnneLanguage)' == 'rust'" >
      <OutputFileName>platform.rs</OutputFileName>
    </DnneNativeExportsInput>

    <DnneNativeExportsInput
        Include="$(DnneGeneratedSourceFileName)"
        Condition="'$(DnneBuildExports)' == 'true' AND '$(DnneLanguage)' == 'rust'" >
      <OutputFileName>$(TargetName)$(DnneGeneratedSourceFileExt)</OutputFileName>
    </DnneNativeExportsInput>

    <!-- Add outputs and general glob to help with project cleanup -->
    <Clean Include="@(DnneNativeExportsInput->'$(DnneNativeExportsBinaryPath)%(OutputFileName)');$(DnneNativeExportsBinaryPath)$(DnneNativeExportsBinaryName).*"/>
    <Clean Include="$(DnneNativeExportsBinaryPath)dnne-rust-crate/**" />
  </ItemGroup>

  <Target
    Name="DnneGenerateNativeExports"
    Condition="('$(DesignTimeBuild)' != 'true' OR '$(BuildingProject)' == 'true') AND '$(DnneSupportedTFM)' == 'true' AND '$(DnneGenerateExports)' == 'true'"
    Inputs="@(IntermediateAssembly)"
    Outputs="@(DnneGeneratedSourceFile)"
    AfterTargets="CoreCompile">
    <Message Text="Generating source for @(IntermediateAssembly) into @(DnneGeneratedSourceFile)" Importance="$(DnneMSBuildLogging)" />

    <!-- Ensure the output directory exists -->
    <MakeDir Directories="$(DnneGeneratedOutputPath)" />

    <PropertyGroup>
      <DocFlag Condition="Exists($(DocumentationFile))">-d &quot;$(DocumentationFile)&quot;</DocFlag>
    </PropertyGroup>

    <Exec Command="$(DnneGenExe) @(IntermediateAssembly) $(DocFlag) -l $(DnneLanguage) -o @(DnneGeneratedSourceFile)" />
  </Target>

  <PropertyGroup>
    <DnneBuildTasksTFM>netstandard2.1</DnneBuildTasksTFM>
    <DnneBuildTasksTFM Condition="'$(MSBuildRuntimeType)' == 'Full'">net472</DnneBuildTasksTFM>
  </PropertyGroup>

  <!--
      Include all output artifacts in the project's None Items so
      they flow through project references.
  -->
  <ItemGroup Condition="'$(DnneBuildExports)' == 'true' AND '$(DnneAddGeneratedBinaryToProject)' == 'true'">
    <None Include="@(DnneNativeExportsInput->'$(DnneNativeExportsBinaryPath)%(OutputFileName)')">
      <Link>%(Filename)%(Extension)</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <Visible>false</Visible>
    </None>
    <None Include="$(DnneGeneratedBinPath)/$(DnneNativeExportsBinaryName).pdb" Condition="$([MSBuild]::IsOsPlatform('Windows'))">
      <Link>%(Filename)%(Extension)</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <Visible>false</Visible>
    </None>
  </ItemGroup>

  <UsingTask
    TaskName="DNNE.BuildTasks.CreateCompileCommand"
    AssemblyFile = "./$(DnneBuildTasksTFM)/DNNE.BuildTasks.dll"
    Condition="'$(DnneBuildExports)' == 'true'" />

  <Target
    Name="DnneBuildNativeExports"
    Condition="('$(DesignTimeBuild)' != 'true' OR '$(BuildingProject)' == 'true') AND '$(DnneSupportedTFM)' == 'true' AND '$(DnneBuildExports)' == 'true'"
    Inputs="@(DnneNativeExportsInput)"
    Outputs="@(DnneNativeExportsInput->'$(DnneNativeExportsBinaryPath)%(OutputFileName)')"
    AfterTargets="DnneGenerateNativeExports"
    DependsOnTargets="ResolvePackageAssets;ResolveFrameworkReferences">
    <Message Text="Building native exports binary from @(DnneGeneratedSourceFile)" Importance="$(DnneMSBuildLogging)" />

    <Error
      Condition="!Exists(@(DnneGeneratedSourceFile))"
      Text="Generated source file(s), @(DnneGeneratedSourceFile), not found. In order to generate source an export must exist. Create an export by adorning a function with UnmanagedCallersOnly." />

    <!-- Ensure the output directory exists -->
    <MakeDir Directories="$(DnneGeneratedBinPath)" />

    <PropertyGroup>
      <DnneAssemblyName>$(TargetName)</DnneAssemblyName>
      <DnneNetHostDir Condition="'$(DnneNetHostDir)' == ''">%(ResolvedAppHostPack.PackageDirectory)/runtimes/$(DnneRuntimeIdentifier)/native</DnneNetHostDir>
      <DnneFindVcvarsallScript>$([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)', 'findvcvarsall.bat'))</DnneFindVcvarsallScript>
      <__DnneGeneratedSourceFile>@(DnneGeneratedSourceFile)</__DnneGeneratedSourceFile>
    </PropertyGroup>

    <ItemGroup>
      <__DnneAdditionalIncludeDirectories Include="$(DnneAdditionalIncludeDirectories)" />
    </ItemGroup>

    <CreateCompileCommand
        Language="$(DnneLanguage)"
        AssemblyName="$(DnneAssemblyName)"
        AssemblyVersion="$(Version)"
        NetHostPath="$([MSBuild]::NormalizePath($(DnneNetHostDir)))"
        PlatformPath="$([MSBuild]::NormalizePath($(DnnePlatformSourcePath)))"
        Source="$([MSBuild]::NormalizePath($(__DnneGeneratedSourceFile)))"
        OutputName="$(DnneNativeExportsBinaryName)$(DnneNativeBinaryExt)"
        OutputPath="$([MSBuild]::NormalizePath($(DnneGeneratedBinPath)))"
        RuntimeID="$(DnneRuntimeIdentifier)"
        Architecture="$(TargetedSDKArchitecture)"
        Configuration="$(Configuration)"
        TargetFramework="$(TargetFramework)"
        FindVcvarsallPath="$(DnneFindVcvarsallScript)"
        ExportsDefFile="$(DnneWindowsExportsDef)"
        IsSelfContained="$(DnneSelfContained_Experimental)"
        UserDefinedCompilerFlags="$(DnneCompilerUserFlags)"
        UserDefinedLinkerFlags="$(DnneLinkerUserFlags)"
        ForceGnuToolchain="$(DnneForceGnuToolchain)"
        AdditionalIncludeDirectories="@(__DnneAdditionalIncludeDirectories)">
      <Output TaskParameter="Command" PropertyName="CompilerCmd" />
      <Output TaskParameter="CommandArguments" PropertyName="CompilerArgs" />
    </CreateCompileCommand>

    <!-- The next target should be DnneBuildXXXXExports -->
  </Target>

<Target
  Condition="'$(DnneLanguage)' == 'c99'"
  Name="DnneBuildC99Exports"
  Inputs="@(DnneNativeExportsInput)"
  Outputs="@(DnneNativeExportsInput->'$(DnneNativeExportsBinaryPath)%(OutputFileName)')"
  AfterTargets="DnneBuildNativeExports">

    <PropertyGroup>
      <CompilerCmd Condition="'$(DnneCompilerCommand)' != ''">$(DnneCompilerCommand)</CompilerCmd>
    </PropertyGroup>

    <Message Text="Building native export: &quot;$(CompilerCmd)&quot; $(CompilerArgs)" Importance="high" />
    <Exec Command="&quot;$(CompilerCmd)&quot; $(CompilerArgs)"
        WorkingDirectory="$(DnneGeneratedOutputPath)"
        Outputs="$(DnneCompiledToBinPath)"
        ConsoleToMSBuild="true" />
    <!--
        Copy the binary to the project output directory.
        The dnne-gen tool generates a C99 file that can act as both compilation unit and header.
        Deploy the official 'dnne.h' header.
    -->
    <Copy SourceFiles="@(DnneNativeExportsInput)"
        DestinationFiles="@(DnneNativeExportsInput->'$(DnneNativeExportsBinaryPath)%(OutputFileName)')" />
</Target>

<Target
  Condition="'$(DnneLanguage)' == 'rust'"
  Name="DnneBuildRustExports"
  Inputs="@(DnneNativeExportsInput)"
  Outputs="@(DnneNativeExportsInput->'$(DnneNativeExportsBinaryPath)dnne-rust-crate/%(OutputFileName)')"
  AfterTargets="DnneBuildNativeExports">

    <!-- Rust: Copy over platform.rs to crate, other files were emitted by dnne-gen or CreateCompileCommand -->
    <Copy SourceFiles="$(DnnePlatformSourcePath)/platform.rs"
        DestinationFolder="$(DnneGeneratedOutputPath)" />

    <Message Text="Generated Rust crate at $(DnneGeneratedOutputPath)" Importance="high" />

    <ItemGroup >
      <DnneRustCrateFiles Include="$(DnneGeneratedOutputPath)/Cargo.toml" />
      <DnneRustCrateFiles Include="$(DnneGeneratedOutputPath)/build.rs" />
      <DnneRustCrateFiles Include="$(DnneGeneratedOutputPath)/lib.rs" />
      <DnneRustCrateFiles Include="$(DnneGeneratedOutputPath)/platform.rs" />
      <DnneRustCrateFiles Include="$(DnneGeneratedOutputPath)/$(TargetName)$(DnneGeneratedSourceFileExt)" />
    </ItemGroup>

    <Copy SourceFiles="@(DnneRustCrateFiles)"
        DestinationFolder="$(DnneNativeExportsBinaryPath)dnne-rust-crate" />
</Target>

  <!--
      This target is used to deploy the header file assets when DNNE is _not_ building an export binary.
  -->
  <Target
    Name="DnneCopyGeneratedHeaders"
    Condition="('$(DesignTimeBuild)' != 'true' OR '$(BuildingProject)' == 'true') AND '$(DnneSupportedTFM)' == 'true' AND '$(DnneGenerateExports)' == 'true' AND '$(DnneBuildExports)' != 'true'"
    Inputs="@(DnneNativeExportsInput)"
    Outputs="@(DnneNativeExportsInput->'$(DnneNativeExportsBinaryPath)%(OutputFileName)')"
    AfterTargets="DnneGenerateNativeExports" >

    <!--
        The dnne-gen tool generates a C99 file that can act as both compilation unit and header.
        Deploy the official 'dnne.h' header.
    -->
    <Copy
        SourceFiles="@(DnneNativeExportsInput)"
        DestinationFiles="@(DnneNativeExportsInput->'$(DnneNativeExportsBinaryPath)%(OutputFileName)')" />

  </Target>

  <!--
    The Target below is used to mitigate a limitation when referencing
    application projects. The work to improve this is tracked with:
        https://github.com/dotnet/sdk/issues/1675
    See https://github.com/dotnet/sdk/issues/1675#issuecomment-658779827
  -->
  <Target Name="DnneAddRuntimeDependenciesToContent"
        BeforeTargets="GetCopyToOutputDirectoryItems"
        DependsOnTargets="GenerateBuildDependencyFile;GenerateBuildRuntimeConfigurationFiles"
        Condition="'$(DnneBuildExports)' == 'true' AND '$(DnneAddGeneratedBinaryToProject)' == 'true' AND '$(DnneWorkAroundSdk1675)' == 'true'">
    <ItemGroup>
      <ContentWithTargetPath Include="$(ProjectDepsFilePath)"
                            Condition="'$(GenerateDependencyFile)' == 'true'"
                            CopyToOutputDirectory="PreserveNewest"
                            TargetPath="$(ProjectDepsFileName)" />
      <ContentWithTargetPath Include="$(ProjectRuntimeConfigFilePath)"
                            Condition="'$(GenerateRuntimeConfigurationFiles)' == 'true'"
                            CopyToOutputDirectory="PreserveNewest"
                            TargetPath="$(ProjectRuntimeConfigFileName)" />
    </ItemGroup>
  </Target>
</Project>
