<!--
***********************************************************************************************
BuidAsX.ARM64EC.targets

WARNING:  DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
          created a backup copy.  Incorrect changes to this file will make it
          impossible to load or build your projects from the command-line or the IDE.

Copyright (C) Microsoft Corporation. All rights reserved.
***********************************************************************************************
-->

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <!-- *******************************************************************************************
    These are specific targets that are needed when we are trying to build a binary as ARM64X.
    ARM64X is the resulting binary from linking ARM64 and ARM64EC objs and libs into one.
    When building a project as ARM64EC/ARM64, if the BuildAsX property is set to true, 
    the  BuildOtherXConfiguration is invoked in order to retrieve the objs and extra libs from the
    corresponding ARM64/ARM64EC project needed by the linker to build the project as ARM64X
  ******************************************************************************************* -->

  <!-- *******************************************************************************************
    The BuildAsX process typically involves 2 builds within same msbuild process of a project and it's references in order to build and get
    outputs of the other config's build to combine with current config build and 
    create a single ARM64X binary. This requires setting specific properties to control which targets get invoked depending on which configuration is being built.
    Below is an overview of the key targets and their roles:

    1. SetBuildAsXProperties:
       - Initializes properties for the BuildAsX process.
       - Ensures the correct targets are executed for ARM64X builds.

    2. BuildOtherXConfiguration:
       - Called when BuildasX is set to true and triggers the build of the other configuration (ARM64/ARM64EC) to gather necessary inputs.
       - Retrieves necessary inputs (e.g., objs, libs, resources) for linking into ARM64X.

    3. BuildAsXBuildSteps:
       - Controls target execution of the main build steps for other configuration.

    4. GetResourceCompileInputs, GetLinkInputs, GetLibInputs:
       - Collects inputs (e.g., resources, link files, lib files) when other confiuration is being built.

    5. BuildExtraNonXReferences:
       - Builds nested project references during the other configuration build that have not been set to buildasx.

  ******************************************************************************************* -->

  <PropertyGroup>
    <BuildAsXBuildStepsDependsOn>
      $(BuildAsXBuildStepsDependsOn);
      SetBuildAsXProperties;
      PrepareForBuild;
      InitializeBuildStatus;
      ResolveReferences;
      BuildExtraNonXReferences;
    </BuildAsXBuildStepsDependsOn>
  </PropertyGroup>

  <PropertyGroup>
    <BuildLibTargets Condition="'$(BuildAsX)' == 'true'">
      $(BuildLibTargets);
      BuildOtherXConfiguration;
    </BuildLibTargets>
    
    <!-- Targets to be run when building other configuration during ARM64X build. -->
    <BuildAsXTargets>
      SetBuildAsXProperties;
      BuildAsXBuildSteps;
      GetLinkInputs;
      GetLibInputs;
      GetResourceCompileInputs;
    </BuildAsXTargets>
  </PropertyGroup>


  <PropertyGroup>
    <ARM64XContentsFileDir>$(IntDir)$(ProjectName)\</ARM64XContentsFileDir>
  </PropertyGroup>

  <!-- This target sets properties for building as ARM64X. This is called at the start of the build for other arm64x platform to setup properties written from first configuration build
  and other properties to ensure the right targets are run for other configuration build.
  We also set buildasx to false to prevent recursion if user has this set to true in both arm64 and arm64ec config. -->
  <Target Name="SetBuildAsXProperties">
    <PropertyGroup>
      <BuildProjectReferences>false</BuildProjectReferences>
      <BuildingForX>true</BuildingForX>
      <BuildAsX>false</BuildAsX>
    </PropertyGroup>
  </Target>

  <Target Name="BuildOtherXConfiguration"
          Condition="'$(BuildAsX)' == 'true'"
          DependsOnTargets="$(ComputeLibInputsTargets);$(ComputeLinkInputsTargets);GetResourceCompileInputs"
          Returns="@(LibAndLinkInputs)">
    <PropertyGroup>
      <ARM64XSolutionConfigurationName Condition="'$(ARM64XSolutionConfigurationName)' == ''">$(OtherConfigurationNameForX)</ARM64XSolutionConfigurationName>
    </PropertyGroup>

    <!-- Retrieve solution configuration contents for the current platform. -->
    <MSBuild Projects="$(SolutionPath)"
         Targets="GetSolutionConfigurationContents"
         Condition="'$(SolutionPath)' != '*Undefined*' and '$(SolutionPath)' != ''"
         Properties="Configuration=$(ARM64XSolutionConfigurationName);Platform=$(PlatformForX)">
      <Output TaskParameter="TargetOutputs" PropertyName="_SolutionConfigurationContentsToUse"/>
    </MSBuild>
    
    <!-- Build the other configuration and retrieve its outputs. -->
    <MSBuild
            Projects="$(OtherProjectForX)" 
            Targets="$(BuildAsXTargets)"
            Properties="Configuration=$(OtherConfigurationNameForX);Platform=$(PlatformForX);CurrentSolutionConfigurationContents=$(_SolutionConfigurationContentsToUse)">
            <Output TaskParameter="TargetOutputs" ItemName="LibAndLinkInputs" /> 
    </MSBuild>

    <!-- Extract unique resource files from the other configuration build. -->
    <ItemGroup>
      <OtherConfigurationResourceCompileInputs Include="@(LibAndLinkInputs)" Condition="'%(LibAndLinkInputs.Type)' == 'LinkResource' or '%(LibAndLinkInputs.Type)' == 'LibResource'"/>
      <OtherConfigurationResourceCompileInputs Remove="@(ResourceCompileInputs)"/>
      <UniqueOtherProjectResourceObjs Include="@(OtherConfigurationResourceCompileInputs->Metadata('ResourceOutput'))"/>
    </ItemGroup>

    <ItemGroup>
      <Link Include="$(ModuleDefPrefix)%(Link.ModuleDefinitionFile)" Condition="'$(RemoveModuleDef)' == 'true' and '%(Link.ModuleDefinitionFile)' != ''"/>
      <Lib Include="$(ModuleDefPrefix)%(Lib.ModuleDefinitionFile)" Condition="'$(RemoveModuleDef)' == 'true' and '%(Lib.ModuleDefinitionFile)' != ''"/>
      <Link Include="@(LibAndLinkInputs)" Condition="'%(LibAndLinkInputs.Type)' == 'LinkModuleDefinitionFile'"/>
      <Lib Include="@(LibAndLinkInputs)" Condition="'%(LibAndLinkInputs.Type)' == 'LibModuleDefinitionFile'"/>
      <Link Condition="'%(LibAndLinkInputs.Type)' == 'ForLink'" Include="@(LibAndLinkInputs)"/>
      <Lib Condition="'%(LibAndLinkInputs.Type)' == 'ForLib'" Include="@(LibAndLinkInputs)"/>
      <Link Condition="'%(UniqueOtherProjectResourceObjs.Type)' == 'LinkResource'" Include="@(UniqueOtherProjectResourceObjs)"/>
      <Lib Condition="'%(UniqueOtherProjectResourceObjs.Type)' == 'LibResource'" Include="@(UniqueOtherProjectResourceObjs)"/>
    </ItemGroup>

    <!-- Remove module definition file for arm64 during build as x, as added it to inputs using /defArm64Native -->
    <ItemGroup Condition="'$(RemoveModuleDef)' == 'true'"> 
      <Link>
        <ModuleDefinitionFile></ModuleDefinitionFile>
      </Link>
      <Lib>
        <ModuleDefinitionFile></ModuleDefinitionFile>
      </Lib>
    </ItemGroup>  
  </Target>

  <PropertyGroup>
  	<GetProjectInfoForReferenceDependsOn Condition="'$(DesignTimeBuild)' != 'true' and '$(BuildAsX)' == 'true'">
      $(GetProjectInfoForReferenceDependsOn);
      AddBuildAsXToProjectsInfoForReference;
    </GetProjectInfoForReferenceDependsOn>
  </PropertyGroup>

  <Target Name="AddBuildAsXToProjectsInfoForReference">
    <ItemGroup>
      <ProjectInfoForReference Include="$(MSBuildProjectFullPath)">
        <ProjectType>$(ConfigurationType)</ProjectType>
        <FileType>BuildAsX</FileType>
      </ProjectInfoForReference>
    </ItemGroup>
  </Target>

  <Target Name="BuildAsXBuildSteps"
          DependsOnTargets="$(BuildAsXBuildStepsDependsOn);$(BuildGenerateSourcesTargets);$(BuildCompileTargets)">
  </Target>

    <Target Name="GetResourceCompileInputs"
          Condition="'@(ResObj)' != ''"
          DependsOnTargets="$(ComputeLinkInputsTargets);$(ComputeLibInputsTargets)"
          Returns="@(ResourceCompileInputs)">
          <ItemGroup>
            <ResourceCompileInputs Include="@(ResObj->Metadata('ResourceCompileFilePath')->WithMetadataValue('LinkCompiled', 'true')->ClearMetadata())">
              <Type>LinkResource</Type>
              <ResourceOutput>%(ResObj.FullPath)</ResourceOutput>
            </ResourceCompileInputs>

            <ResourceCompileInputs Include="@(ResObj->Metadata('ResourceCompileFilePath')->WithMetadataValue('LibCompiled', 'true')->ClearMetadata())">
              <Type>LibResource</Type>
              <ResourceOutput>%(ResObj.FullPath)</ResourceOutput>
            </ResourceCompileInputs>
          </ItemGroup>
  </Target>

  <Target Name="GetLinkInputs"
          DependsOnTargets="$(ComputeLinkInputsTargets)"
          Condition="'$(LinkCompiled)' == 'true'"
          Returns="@(LinkInputs)">
          <ItemGroup>
            <LinkInputs Include="@(Link->Metadata('FullPath')->ClearMetadata())">
              <Type>ForLink</Type>
            </LinkInputs>

            <!-- When Building as ARM64X, specify special .def prefix -->
            <LinkInputs Include="$(ModuleDefPrefix)%(Link.ModuleDefinitionFile)" Condition="'%(Link.ModuleDefinitionFile)' != ''">
              <Type>LinkModuleDefinitionFile</Type>
            </LinkInputs>

            <!-- Remove the resource output from the link inputs. -->
            <LinkInputs Remove="@(ResObj->Metadata('FullPath'))"/>
          </ItemGroup>
  </Target>

  <Target Name="GetLibInputs"
          DependsOnTargets="$(ComputeLibInputsTargets)"
          Condition="'$(LibCompiled)' == 'true'"
          Returns="@(LibInputs)">
    <ItemGroup>
      <LibInputs Include="@(Lib->Metadata('FullPath')->ClearMetadata())">
        <Type>ForLib</Type>
      </LibInputs>
      
      <!-- When Building as ARM64X, specify special .def prefix -->
      <LibInputs Include="$(ModuleDefPrefix)%(Lib.ModuleDefinitionFile)" Condition="'%(Lib.ModuleDefinitionFile)' != ''">
        <Type>LibModuleDefinitionFile</Type>
      </LibInputs>

      <!-- Remove the resource output from the lib inputs. -->
      <LibInputs Remove="@(ResObj->Metadata('FullPath'))"/>
    </ItemGroup>
  </Target>
  
  <Target Name="BuildExtraNonXReferences"
          DependsOnTargets="SetBuildAsXProperties"
          Condition="'$(BuildingForX)' == 'true' ">
    
    <!-- Assign the right project configuration to these project references using the solution configuration contents for the current X platform -->
    <AssignProjectConfiguration
       ProjectReferences="@(_MSBuildProjectReferenceExistent)"
       CurrentProject="$(ProjectPath)"
       CurrentProjectConfiguration="$(Configuration)"
       CurrentProjectPlatform="$(Platform)"
       DefaultToVcxPlatformMapping="$(DefaultToVcxPlatformMapping)"
       VcxToDefaultPlatformMapping="$(VcxToDefaultPlatformMapping)"
       OutputType="$(OutputType)"
       ResolveConfigurationPlatformUsingMappings="false"
       SolutionConfigurationContents="$(CurrentSolutionConfigurationContents)"
       AddSyntheticProjectReferencesForSolutionDependencies="$(AddSyntheticProjectReferencesForSolutionDependencies)"
       OnlyReferenceAndBuildProjectsEnabledInSolutionConfiguration = "$(OnlyReferenceAndBuildProjectsEnabledInSolutionConfiguration)"
       ShouldUnsetParentConfigurationAndPlatform = "$(ShouldUnsetParentConfigurationAndPlatform)" >

      <Output TaskParameter="AssignedProjects" ItemName="UniqueNonXProjectReferenceWithConfiguration"/>
      <Output TaskParameter="UnassignedProjects" ItemName="UniqueNonXProjectReferenceWithConfiguration"/>
    </AssignProjectConfiguration>

    <ItemGroup>
      <_TempUniqueNonXProjectReferenceWithConfiguration Include="@(UniqueNonXProjectReferenceWithConfiguration)"/>
      <UniqueNonXProjectReferenceWithConfiguration Remove="@(UniqueNonXProjectReferenceWithConfiguration)"/>
    </ItemGroup>
    <ItemGroup>
      <UniqueNonXProjectReferenceWithConfiguration Include="@(_TempUniqueNonXProjectReferenceWithConfiguration)" Condition="'%(_TempUniqueNonXProjectReferenceWithConfiguration.Extension)' == '.vcxproj'"/>
    </ItemGroup>

    <!-- Build the project references that dont have buildasx as true. 
         First set the properties necessary for the project references to be aware they are building as a part of arm64x other configuration build, and so they act 
         like command line build so they build nested references and not assume IDE handled it -->
    <MSBuild  Projects="@(UniqueNonXProjectReferenceWithConfiguration)"
              Targets="SetBuildAsXTransitiveProperties;Build;GetResolvedLinkLibs"
              BuildInParallel="$(BuildInParallel)"
              Properties="%(UniqueNonXProjectReferenceWithConfiguration.SetConfiguration); %(UniqueNonXProjectReferenceWithConfiguration.SetPlatform)"
              ContinueOnError="$(ContinueOnError)"
              RemoveProperties="%(UniqueNonXProjectReferenceWithConfiguration.GlobalPropertiesToRemove)"
              >
      <Output TaskParameter="TargetOutputs" ItemName="LibFullPath"/>
    </MSBuild>
    
    <!-- Add the arm64ec/arm64 resolvedlinklibs to _ResolvedNativeProjectReferencePaths -->
    <ItemGroup>
      <_ResolvedNativeProjectReferencePaths Include="@(LibFullPath)"/>
    </ItemGroup>
  </Target>
</Project>