From b1896ea0a4fd8f77271a40cdfa53560f2234b72c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 8 Sep 2026 14:45:44 -0700 Subject: [PATCH 1/4] Add target-specific CMake project references Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Microsoft.DotNet.CMake.Sdk.csproj | 1 + src/Microsoft.DotNet.CMake.Sdk/README.md | 12 ++ .../build/Microsoft.DotNet.CMake.Sdk.targets | 9 +- .../sdk/ProjectReference.targets | 59 ++++++- .../src/ExecWithMutex.cs | 68 ++++++++ .../src/GetCMakeArtifactsFromFileApi.cs | 155 ++++++++++-------- 6 files changed, 227 insertions(+), 77 deletions(-) create mode 100644 src/Microsoft.DotNet.CMake.Sdk/src/ExecWithMutex.cs diff --git a/src/Microsoft.DotNet.CMake.Sdk/Microsoft.DotNet.CMake.Sdk.csproj b/src/Microsoft.DotNet.CMake.Sdk/Microsoft.DotNet.CMake.Sdk.csproj index 811150bfc4b..b6ba7ecf35e 100644 --- a/src/Microsoft.DotNet.CMake.Sdk/Microsoft.DotNet.CMake.Sdk.csproj +++ b/src/Microsoft.DotNet.CMake.Sdk/Microsoft.DotNet.CMake.Sdk.csproj @@ -12,6 +12,7 @@ + diff --git a/src/Microsoft.DotNet.CMake.Sdk/README.md b/src/Microsoft.DotNet.CMake.Sdk/README.md index 8c8ef574b36..c913b486b68 100644 --- a/src/Microsoft.DotNet.CMake.Sdk/README.md +++ b/src/Microsoft.DotNet.CMake.Sdk/README.md @@ -118,6 +118,18 @@ All assets that are a direct output of this CMakeLists.txt will be copied to you By default, a NativeProjectReference will not build the native project. It assumes that the project has already been built. To build the project as part of the reference, you can opt-in by setting the `BuildNative="true"` metadata on the `NativeProjectReference`. +#### Referencing CMake targets with CMakeProjectReference + +To build and copy the artifacts for particular CMake targets, add a `CMakeProjectReference` item. Its `CMakeTargets` metadata contains the target names to build: + +```xml + + + +``` + +The target artifacts are read from the CMake File API target descriptions and copied to the consuming project's output folder. Multiple target references to the same CMake SDK project are supported. CMake configure and build executions are serialized per CMake build tree across processes to avoid concurrent native tool access. + ### Generating a raw build script for bringup scenarios This SDK also supports outputting the script that the SDK runs to configure and build your CMake project. This feature can be used to generate a simple script for use in bringup scenarios where we don't have MSBuild available for the device we are building on. This would enable teams to generate bringup build scripts when needed instead of using bringup-style scripts at all times or having unused bringup scripts that quickly bitrot. diff --git a/src/Microsoft.DotNet.CMake.Sdk/build/Microsoft.DotNet.CMake.Sdk.targets b/src/Microsoft.DotNet.CMake.Sdk/build/Microsoft.DotNet.CMake.Sdk.targets index 00ce4d0abcd..e1e4b68c0b7 100644 --- a/src/Microsoft.DotNet.CMake.Sdk/build/Microsoft.DotNet.CMake.Sdk.targets +++ b/src/Microsoft.DotNet.CMake.Sdk/build/Microsoft.DotNet.CMake.Sdk.targets @@ -9,6 +9,7 @@ + @@ -198,7 +199,9 @@ - @@ -208,7 +211,9 @@ - diff --git a/src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets b/src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets index f69c4c0058a..d683a558ba7 100644 --- a/src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets +++ b/src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets @@ -12,12 +12,20 @@ + + + + + + + + @@ -46,20 +54,47 @@ + + + + + + + + + + DependsOnTargets="NormalizeCMakeProjectReferences" + BeforeTargets="Build"> - <_NativeProjectReferenceToBuild Include="%(NativeProjectReferenceNormalized.CMakeProject)" - Condition="'%(NativeProjectReferenceNormalized.BuildNative)' == 'true'" - AdditionalProperties="%(NativeProjectReferenceNormalized.AdditionalProperties)" /> + <_NativeProjectsToBuild Include="%(NativeProjectReferenceNormalized.CMakeProject)" + Condition="'%(NativeProjectReferenceNormalized.BuildNative)' == 'true'"> + %(NativeProjectReferenceNormalized.AdditionalProperties) + + <_CMakeProjectsToBuild Include="%(CMakeProjectReferenceNormalized.Identity)"> + CMakeBuildTarget=%(CMakeProjectReferenceNormalized.CMakeBuildTarget);%(CMakeProjectReferenceNormalized.AdditionalProperties) + - - + + - - + + + @@ -70,6 +105,13 @@ %(NativeProjectReferenceNormalized.AdditionalProperties)" Condition="'@(NativeProjectReference)' != ''" /> + + @@ -93,6 +135,7 @@ diff --git a/src/Microsoft.DotNet.CMake.Sdk/src/ExecWithMutex.cs b/src/Microsoft.DotNet.CMake.Sdk/src/ExecWithMutex.cs new file mode 100644 index 00000000000..c2fba762165 --- /dev/null +++ b/src/Microsoft.DotNet.CMake.Sdk/src/ExecWithMutex.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Build.Framework; +using Microsoft.Build.Tasks; +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using System.Threading; + +namespace Microsoft.DotNet.CMake.Sdk; + +/// +/// Executes a command while holding a named, cross-process mutex. +/// +public sealed class ExecWithMutex : Exec +{ + [Required] + public string MutexName { get; set; } + + public override bool Execute() + { + using var mutex = CreateMutex(MutexName); + bool ownsMutex = false; + + try + { + try + { + mutex.WaitOne(); + } + catch (AbandonedMutexException) + { + } + + ownsMutex = true; + return base.Execute(); + } + finally + { + if (ownsMutex) + { + mutex.ReleaseMutex(); + } + } + } + + private Mutex CreateMutex(string path) + { + string key = TaskEnvironment.GetAbsolutePath(path).GetCanonicalForm().Value; + using var hasher = SHA256.Create(); + string hash = Convert.ToBase64String(hasher.ComputeHash(Encoding.UTF8.GetBytes(key))) + .TrimEnd('=') + .Replace('+', '-') + .Replace('/', '_'); + +#if NETFRAMEWORK + return new Mutex(false, $"Local\\Microsoft.DotNet.CMake.Sdk.{hash}"); +#else + return new Mutex( + false, + $"Microsoft.DotNet.CMake.Sdk.{hash}", + new NamedWaitHandleOptions { CurrentUserOnly = true }, + out _); +#endif + } +} diff --git a/src/Microsoft.DotNet.CMake.Sdk/src/GetCMakeArtifactsFromFileApi.cs b/src/Microsoft.DotNet.CMake.Sdk/src/GetCMakeArtifactsFromFileApi.cs index 7a51dbcd883..fe6886aadbd 100644 --- a/src/Microsoft.DotNet.CMake.Sdk/src/GetCMakeArtifactsFromFileApi.cs +++ b/src/Microsoft.DotNet.CMake.Sdk/src/GetCMakeArtifactsFromFileApi.cs @@ -12,7 +12,7 @@ namespace Microsoft.DotNet.CMake.Sdk; /// -/// Reads CMake File API response to find artifacts for a specific source directory. +/// Reads CMake File API response to find artifacts for a specific source directory or target. /// [MSBuildMultiThreadableTask] public class GetCMakeArtifactsFromFileApi : Task, IMultiThreadableTask @@ -29,9 +29,13 @@ public class GetCMakeArtifactsFromFileApi : Task, IMultiThreadableTask /// /// The source directory of the CMakeLists.txt to find artifacts for. /// - [Required] public string SourceDirectory { get; set; } + /// + /// Semicolon-separated CMake target names to find artifacts for. + /// + public string CMakeTargets { get; set; } + /// /// The configuration name (e.g., Debug, Release). /// @@ -105,12 +109,6 @@ public override bool Execute() // Get the source root from the codemodel string sourceRoot = codeModel.Paths?.Source?.Replace('\\', '/').TrimEnd('/') ?? ""; - // Normalize source directory for comparison - // GetAbsolutePath does not canonicalize, but this value is string-compared against - // dirSource below, and Path.GetFullPath used to resolve the "." and ".." segments that - // CMake's file API routinely emits. - string normalizedSourceDir = TaskEnvironment.GetAbsolutePath(SourceDirectory).GetCanonicalForm().Value.Replace('\\', '/').TrimEnd('/'); - // Find the configuration using LINQ var config = codeModel.Configurations?.FirstOrDefault(c => string.Equals(c.Name, Configuration, StringComparison.OrdinalIgnoreCase)); @@ -129,78 +127,89 @@ public override bool Execute() return false; } - // Find the matching directory using LINQ - var directory = config.Directories.FirstOrDefault(d => + // Get artifacts + var artifacts = new List(); + IEnumerable targets; + if (!string.IsNullOrEmpty(CMakeTargets)) + { + var requestedTargets = CMakeTargets.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + targets = config.Targets.Where(t => requestedTargets.Contains(t.Name, StringComparer.OrdinalIgnoreCase)); + Log.LogMessage(MessageImportance.Low, "Found {0} requested CMake target(s) for configuration '{1}'.", targets.Count(), Configuration); + } + else { - string dirSource = d.Source?.Replace('\\', '/').TrimEnd('/') ?? ""; - - // Make the directory source path absolute - if (!Path.IsPathRooted(dirSource)) + if (string.IsNullOrEmpty(SourceDirectory)) { - dirSource = Path.Combine(sourceRoot, dirSource); - dirSource = TaskEnvironment.GetAbsolutePath(dirSource).GetCanonicalForm().Value.Replace('\\', '/').TrimEnd('/'); + Log.LogError("Either SourceDirectory or CMakeTargets must be specified."); + return false; } - - return string.Equals(dirSource, normalizedSourceDir, StringComparison.OrdinalIgnoreCase); - }); - if (directory == null) - { - Log.LogError("Source directory '{0}' not found in CMake File API response.", SourceDirectory); - return false; - } + // GetAbsolutePath does not canonicalize, but this value is string-compared against + // dirSource below, and Path.GetFullPath used to resolve the "." and ".." segments that + // CMake's file API routinely emits. + string normalizedSourceDir = TaskEnvironment.GetAbsolutePath(SourceDirectory).GetCanonicalForm().Value.Replace('\\', '/').TrimEnd('/'); + var directory = config.Directories.FirstOrDefault(d => + { + string dirSource = d.Source?.Replace('\\', '/').TrimEnd('/') ?? ""; + if (!Path.IsPathRooted(dirSource)) + { + dirSource = Path.Combine(sourceRoot, dirSource); + dirSource = TaskEnvironment.GetAbsolutePath(dirSource).GetCanonicalForm().Value.Replace('\\', '/').TrimEnd('/'); + } - Log.LogMessage(MessageImportance.Low, "Found matching directory: {0}", SourceDirectory); + return string.Equals(dirSource, normalizedSourceDir, StringComparison.OrdinalIgnoreCase); + }); - // Get artifacts - var artifacts = new List(); + if (directory == null) + { + Log.LogError("Source directory '{0}' not found in CMake File API response.", SourceDirectory); + return false; + } - if (directory.TargetIndexes != null) + Log.LogMessage(MessageImportance.Low, "Found matching directory: {0}", SourceDirectory); + targets = directory.TargetIndexes? + .Where(targetIndex => targetIndex >= 0 && targetIndex < config.Targets.Count) + .Select(targetIndex => config.Targets[targetIndex]) + ?? Enumerable.Empty(); + } + + foreach (var target in targets) { - foreach (int targetIndex in directory.TargetIndexes) + if (string.IsNullOrEmpty(target.JsonFile)) { - if (targetIndex < 0 || targetIndex >= config.Targets.Count) - { - continue; - } - - var target = config.Targets[targetIndex]; - if (string.IsNullOrEmpty(target.JsonFile)) - { - continue; - } + continue; + } - string targetFile = Path.Combine(replyDir, target.JsonFile); - AbsolutePath targetFilePath = TaskEnvironment.GetAbsolutePath(targetFile); - if (!File.Exists(targetFilePath)) - { - continue; - } + string targetFile = Path.Combine(replyDir, target.JsonFile); + AbsolutePath targetFilePath = TaskEnvironment.GetAbsolutePath(targetFile); + if (!File.Exists(targetFilePath)) + { + continue; + } - Log.LogMessage(MessageImportance.Low, "Reading target file: {0}", targetFile); + Log.LogMessage(MessageImportance.Low, "Reading target file: {0}", targetFile); - // Read target details - string targetJson = File.ReadAllText(targetFilePath); - var targetDetails = JsonSerializer.Deserialize(targetJson, options); + // Read target details + string targetJson = File.ReadAllText(targetFilePath); + var targetDetails = JsonSerializer.Deserialize(targetJson, options); - // Get artifacts - if (targetDetails?.Artifacts != null) + // Get artifacts + if (targetDetails?.Artifacts != null) + { + foreach (var artifact in targetDetails.Artifacts) { - foreach (var artifact in targetDetails.Artifacts) + if (!string.IsNullOrEmpty(artifact.Path)) { - if (!string.IsNullOrEmpty(artifact.Path)) - { - string fullPath = Path.Combine(CMakeOutputDir, artifact.Path); - // Emitted as an item spec, and combining the output dir with a - // CMake-relative artifact path routinely produces ".." segments - // that Path.GetFullPath used to resolve. - fullPath = TaskEnvironment.GetAbsolutePath(fullPath).GetCanonicalForm(); - - var item = new TaskItem(fullPath); - artifacts.Add(item); - - Log.LogMessage(MessageImportance.Low, "Found artifact: {0}", fullPath); - } + string fullPath = Path.Combine(CMakeOutputDir, artifact.Path); + // Emitted as an item spec, and combining the output dir with a + // CMake-relative artifact path routinely produces ".." segments + // that Path.GetFullPath used to resolve. + fullPath = TaskEnvironment.GetAbsolutePath(fullPath).GetCanonicalForm(); + + var item = new TaskItem(fullPath); + artifacts.Add(item); + + Log.LogMessage(MessageImportance.Low, "Found artifact: {0}", fullPath); } } } @@ -208,11 +217,23 @@ public override bool Execute() if (artifacts.Count == 0) { - Log.LogWarning("No artifacts found for source directory '{0}' in configuration '{1}'.", SourceDirectory, Configuration); + Log.LogWarning( + string.IsNullOrEmpty(CMakeTargets) + ? "No artifacts found for source directory '{0}' in configuration '{1}'." + : "No artifacts found for CMake target(s) '{0}' in configuration '{1}'.", + string.IsNullOrEmpty(CMakeTargets) ? SourceDirectory : CMakeTargets, + Configuration); } Artifacts = artifacts.ToArray(); - Log.LogMessage(MessageImportance.Normal, "Found {0} artifact(s) for source directory '{1}' in configuration '{2}'", Artifacts.Length, SourceDirectory, Configuration); + Log.LogMessage( + MessageImportance.Normal, + string.IsNullOrEmpty(CMakeTargets) + ? "Found {0} artifact(s) for source directory '{1}' in configuration '{2}'" + : "Found {0} artifact(s) for CMake target(s) '{1}' in configuration '{2}'", + Artifacts.Length, + string.IsNullOrEmpty(CMakeTargets) ? SourceDirectory : CMakeTargets, + Configuration); return true; } From 089ac24ae56d6215a7de4c30966497bc6ee2841c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 9 Sep 2026 10:30:41 -0700 Subject: [PATCH 2/4] Address CMake SDK review feedback Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../build/Microsoft.DotNet.CMake.Sdk.targets | 5 ++++- src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets | 6 ++---- src/Microsoft.DotNet.CMake.Sdk/src/ExecWithMutex.cs | 1 - .../src/GetCMakeArtifactsFromFileApi.cs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.DotNet.CMake.Sdk/build/Microsoft.DotNet.CMake.Sdk.targets b/src/Microsoft.DotNet.CMake.Sdk/build/Microsoft.DotNet.CMake.Sdk.targets index e1e4b68c0b7..8b4d2334c36 100644 --- a/src/Microsoft.DotNet.CMake.Sdk/build/Microsoft.DotNet.CMake.Sdk.targets +++ b/src/Microsoft.DotNet.CMake.Sdk/build/Microsoft.DotNet.CMake.Sdk.targets @@ -183,10 +183,13 @@ install <_CMakeParallelizationArgument Condition="'$(CMakeParallelization)' != ''">-g $(CMakeParallelization) + + <_CMakeBuildTargets Include="$(CMakeBuildTarget)" /> + - cmake --build "$(CMakeOutputDir)" --target $(CMakeBuildTarget) $(_CMakeParallelizationArgument) --config $(Configuration) -- @(CMakeNativeToolArguments->'%(Identity)',' ') + cmake --build "$(CMakeOutputDir)" --target @(_CMakeBuildTargets->'%(Identity)',' ') $(_CMakeParallelizationArgument) --config $(Configuration) -- @(CMakeNativeToolArguments->'%(Identity)',' ') diff --git a/src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets b/src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets index d683a558ba7..ec13719a50a 100644 --- a/src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets +++ b/src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets @@ -60,8 +60,6 @@ @@ -81,7 +79,7 @@ %(NativeProjectReferenceNormalized.AdditionalProperties) <_CMakeProjectsToBuild Include="%(CMakeProjectReferenceNormalized.Identity)"> - CMakeBuildTarget=%(CMakeProjectReferenceNormalized.CMakeBuildTarget);%(CMakeProjectReferenceNormalized.AdditionalProperties) + CMakeBuildTarget=%(CMakeProjectReferenceNormalized.CMakeTargets);%(CMakeProjectReferenceNormalized.AdditionalProperties) @@ -108,7 +106,7 @@ diff --git a/src/Microsoft.DotNet.CMake.Sdk/src/ExecWithMutex.cs b/src/Microsoft.DotNet.CMake.Sdk/src/ExecWithMutex.cs index c2fba762165..ca90a04aa10 100644 --- a/src/Microsoft.DotNet.CMake.Sdk/src/ExecWithMutex.cs +++ b/src/Microsoft.DotNet.CMake.Sdk/src/ExecWithMutex.cs @@ -4,7 +4,6 @@ using Microsoft.Build.Framework; using Microsoft.Build.Tasks; using System; -using System.IO; using System.Security.Cryptography; using System.Text; using System.Threading; diff --git a/src/Microsoft.DotNet.CMake.Sdk/src/GetCMakeArtifactsFromFileApi.cs b/src/Microsoft.DotNet.CMake.Sdk/src/GetCMakeArtifactsFromFileApi.cs index fe6886aadbd..55693ae9511 100644 --- a/src/Microsoft.DotNet.CMake.Sdk/src/GetCMakeArtifactsFromFileApi.cs +++ b/src/Microsoft.DotNet.CMake.Sdk/src/GetCMakeArtifactsFromFileApi.cs @@ -121,7 +121,7 @@ public override bool Execute() Log.LogMessage(MessageImportance.Low, "Found configuration: {0}", Configuration); - if (config.Directories == null || config.Targets == null) + if (config.Targets == null || (string.IsNullOrEmpty(CMakeTargets) && config.Directories == null)) { Log.LogError("Configuration '{0}' has no directories or targets.", Configuration); return false; From 4ab41bb68fe9ada1b789734b1bf1f0bdee4d85ef Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 9 Sep 2026 15:07:58 -0700 Subject: [PATCH 3/4] Address target reference review feedback Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../sdk/ProjectReference.targets | 6 +++--- .../src/GetCMakeArtifactsFromFileApi.cs | 14 +++++++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets b/src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets index ec13719a50a..da7be5d427c 100644 --- a/src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets +++ b/src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets @@ -79,7 +79,7 @@ %(NativeProjectReferenceNormalized.AdditionalProperties) <_CMakeProjectsToBuild Include="%(CMakeProjectReferenceNormalized.Identity)"> - CMakeBuildTarget=%(CMakeProjectReferenceNormalized.CMakeTargets);%(CMakeProjectReferenceNormalized.AdditionalProperties) + %(CMakeProjectReferenceNormalized.AdditionalProperties) @@ -91,7 +91,7 @@ Properties="%(AdditionalProperties)" BuildInParallel="false" /> @@ -106,7 +106,7 @@ diff --git a/src/Microsoft.DotNet.CMake.Sdk/src/GetCMakeArtifactsFromFileApi.cs b/src/Microsoft.DotNet.CMake.Sdk/src/GetCMakeArtifactsFromFileApi.cs index 55693ae9511..7b6c3ba51f0 100644 --- a/src/Microsoft.DotNet.CMake.Sdk/src/GetCMakeArtifactsFromFileApi.cs +++ b/src/Microsoft.DotNet.CMake.Sdk/src/GetCMakeArtifactsFromFileApi.cs @@ -133,6 +133,18 @@ public override bool Execute() if (!string.IsNullOrEmpty(CMakeTargets)) { var requestedTargets = CMakeTargets.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + var missingTargets = requestedTargets + .Where(requestedTarget => !config.Targets.Any(target => + string.Equals(target.Name, requestedTarget, StringComparison.OrdinalIgnoreCase))) + .ToArray(); + if (missingTargets.Length > 0) + { + Log.LogError( + "The requested CMake target(s) were not found in the File API response: {0}.", + string.Join(", ", missingTargets)); + return false; + } + targets = config.Targets.Where(t => requestedTargets.Contains(t.Name, StringComparer.OrdinalIgnoreCase)); Log.LogMessage(MessageImportance.Low, "Found {0} requested CMake target(s) for configuration '{1}'.", targets.Count(), Configuration); } @@ -154,8 +166,8 @@ public override bool Execute() if (!Path.IsPathRooted(dirSource)) { dirSource = Path.Combine(sourceRoot, dirSource); - dirSource = TaskEnvironment.GetAbsolutePath(dirSource).GetCanonicalForm().Value.Replace('\\', '/').TrimEnd('/'); } + dirSource = TaskEnvironment.GetAbsolutePath(dirSource).GetCanonicalForm().Value.Replace('\\', '/').TrimEnd('/'); return string.Equals(dirSource, normalizedSourceDir, StringComparison.OrdinalIgnoreCase); }); From 541be9fd6ae8cb16e8f230e9d08b55326972dad5 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 24 Sep 2026 10:07:15 -0700 Subject: [PATCH 4/4] Fix CMake project reference batching Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets b/src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets index da7be5d427c..231d697b9f9 100644 --- a/src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets +++ b/src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets @@ -79,6 +79,7 @@ %(NativeProjectReferenceNormalized.AdditionalProperties) <_CMakeProjectsToBuild Include="%(CMakeProjectReferenceNormalized.Identity)"> + %(CMakeProjectReferenceNormalized.CMakeTargets) %(CMakeProjectReferenceNormalized.AdditionalProperties) @@ -91,7 +92,7 @@ Properties="%(AdditionalProperties)" BuildInParallel="false" />