Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Build.Utilities.Core" />
<PackageReference Include="Microsoft.Build.Framework" />
<PackageReference Include="Microsoft.Build.Tasks.Core" />
</ItemGroup>

<ItemGroup>
Expand Down
12 changes: 12 additions & 0 deletions src/Microsoft.DotNet.CMake.Sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
<ItemGroup>
<CMakeProjectReference Include="path/to/CMake/Project/UsingThisSdk.proj" CMakeTargets="my-library" />
</ItemGroup>
```

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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<!-- TODO: Remove Architecture="*" on Runtime="NET" UsingTask declarations below when https://github.com/dotnet/msbuild/issues/13739 is fixed and a new msbuild is consumed with the fix. -->
<UsingTask TaskName="Microsoft.DotNet.CMake.Sdk.CreateCMakeFileApiQuery" AssemblyFile="$(MicrosoftDotNetCMakeSdkTaskAssembly)" Runtime="NET" Architecture="*" />
<UsingTask TaskName="Microsoft.DotNet.CMake.Sdk.GetCMakeArtifactsFromFileApi" AssemblyFile="$(MicrosoftDotNetCMakeSdkTaskAssembly)" Runtime="NET" Architecture="*" />
<UsingTask TaskName="Microsoft.DotNet.CMake.Sdk.ExecWithMutex" AssemblyFile="$(MicrosoftDotNetCMakeSdkTaskAssembly)" Runtime="NET" Architecture="*" />

<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />

Expand Down Expand Up @@ -182,10 +183,13 @@
<CMakeBuildTarget Condition="'$(CMakeBuildTarget)' == ''">install</CMakeBuildTarget>
<_CMakeParallelizationArgument Condition="'$(CMakeParallelization)' != ''">-g $(CMakeParallelization)</_CMakeParallelizationArgument>
</PropertyGroup>
<ItemGroup>
<_CMakeBuildTargets Include="$(CMakeBuildTarget)" />
</ItemGroup>

<PropertyGroup>
<CMakeBuildScript>
cmake --build &quot;$(CMakeOutputDir)&quot; --target $(CMakeBuildTarget) $(_CMakeParallelizationArgument) --config $(Configuration) -- @(CMakeNativeToolArguments->'%(Identity)',' ')
cmake --build &quot;$(CMakeOutputDir)&quot; --target @(_CMakeBuildTargets->'%(Identity)',' ') $(_CMakeParallelizationArgument) --config $(Configuration) -- @(CMakeNativeToolArguments->'%(Identity)',' ')
</CMakeBuildScript>
</PropertyGroup>
</Target>
Expand All @@ -198,7 +202,9 @@

<Target Name="Configure"
DependsOnTargets="GetConfigScript;CreateFileApiQuery">
<Exec WorkingDirectory="$(MSBuildProjectDirectory)" Command="
<ExecWithMutex WorkingDirectory="$(MSBuildProjectDirectory)"
MutexName="$(CMakeOutputDir)"
Command="
$(CMakeCompilerSearchScript)
$(CMakeConfigScript)" />
</Target>
Expand All @@ -208,7 +214,9 @@
<!-- Don't set IntermediateAssembly since this is not produced -->
<IntermediateAssembly Remove="@(IntermediateAssembly)" />
</ItemGroup>
<Exec WorkingDirectory="$(MSBuildProjectDirectory)" Command="
<ExecWithMutex WorkingDirectory="$(MSBuildProjectDirectory)"
MutexName="$(CMakeOutputDir)"
Command="
$(CMakeCompilerSearchScript)
$(CMakeBuildScript)" />
Comment thread
jkoritzinsky marked this conversation as resolved.
</Target>
Expand Down
58 changes: 50 additions & 8 deletions src/Microsoft.DotNet.CMake.Sdk/sdk/ProjectReference.targets
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,20 @@
<NativeProjectReference>
<CMakeProject></CMakeProject>
</NativeProjectReference>
<CMakeProjectReference>
<CMakeTargets></CMakeTargets>
<AdditionalProperties></AdditionalProperties>
</CMakeProjectReference>
</ItemDefinitionGroup>

<Target Name="_RestoreNativeProjectReferences" DependsOnTargets="NormalizeNativeProjectReferences" BeforeTargets="_GenerateRestoreGraphProjectEntry">
<MSBuild Projects="%(NativeProjectReferenceNormalized.CMakeProject)" Targets="Restore" RemoveProperties="RestoreGraphProjectInput" />
</Target>

<Target Name="_RestoreCMakeProjectReferences" DependsOnTargets="NormalizeCMakeProjectReferences" BeforeTargets="_GenerateRestoreGraphProjectEntry">
<MSBuild Projects="@(CMakeProjectReferenceNormalized)" Targets="Restore" RemoveProperties="RestoreGraphProjectInput" />
</Target>

<Target Name="NormalizeNativeProjectReferences"
Condition="'@(NativeProjectReference)' != ''"
BeforeTargets="ConsolidateNativeProjectReference" >
Expand Down Expand Up @@ -46,20 +54,46 @@
</ItemGroup>
</Target>

<Target Name="NormalizeCMakeProjectReferences"
Condition="'@(CMakeProjectReference)' != ''"
BeforeTargets="ConsolidateNativeProjectReference">
<ItemGroup>
<CMakeProjectReferenceNormalized Include="@(CMakeProjectReference -> '%(FullPath)')"
CMakeTargets="%(CMakeProjectReference.CMakeTargets)"
AdditionalProperties="%(CMakeProjectReference.AdditionalProperties)" />
</ItemGroup>

<Error Condition="!Exists('%(CMakeProjectReferenceNormalized.Identity)')"
Text="The CMake SDK project at '%(CMakeProjectReferenceNormalized.Identity)' does not exist." />
<Error Condition="'%(CMakeProjectReferenceNormalized.CMakeTargets)' == ''"
Text="The CMakeTargets metadata must specify at least one target for the CMake project reference to '%(CMakeProjectReferenceNormalized.Identity)'." />
</Target>

<Target Name="ConsolidateNativeProjectReference"
BeforeTargets="Build" >
DependsOnTargets="NormalizeCMakeProjectReferences"
BeforeTargets="Build">

<ItemGroup>
<_NativeProjectReferenceToBuild Include="%(NativeProjectReferenceNormalized.CMakeProject)"
Condition="'%(NativeProjectReferenceNormalized.BuildNative)' == 'true'"
AdditionalProperties="%(NativeProjectReferenceNormalized.AdditionalProperties)" />
<_NativeProjectsToBuild Include="%(NativeProjectReferenceNormalized.CMakeProject)"
Condition="'%(NativeProjectReferenceNormalized.BuildNative)' == 'true'">
<AdditionalProperties>%(NativeProjectReferenceNormalized.AdditionalProperties)</AdditionalProperties>
</_NativeProjectsToBuild>
<_CMakeProjectsToBuild Include="%(CMakeProjectReferenceNormalized.Identity)">
<CMakeBuildTarget>%(CMakeProjectReferenceNormalized.CMakeTargets)</CMakeBuildTarget>
<AdditionalProperties>%(CMakeProjectReferenceNormalized.AdditionalProperties)</AdditionalProperties>
</_CMakeProjectsToBuild>
</ItemGroup>

<RemoveDuplicates Inputs="@(_NativeProjectReferenceToBuild)">
<Output TaskParameter="Filtered" ItemName="_UniqueNativeProjectReferenceToBuild" />
<RemoveDuplicates Inputs="@(_NativeProjectsToBuild)">
<Output TaskParameter="Filtered" ItemName="_UniqueNativeProjectsToBuild" />
</RemoveDuplicates>

<MSBuild Projects="@(_UniqueNativeProjectReferenceToBuild)" />

<MSBuild Projects="@(_UniqueNativeProjectsToBuild)"
Properties="%(AdditionalProperties)"
BuildInParallel="false" />
<MSBuild Projects="@(_CMakeProjectsToBuild)"
Properties="CMakeBuildTarget=$([MSBuild]::Escape('%(_CMakeProjectsToBuild.CMakeBuildTarget)'));%(_CMakeProjectsToBuild.AdditionalProperties)"
BuildInParallel="false" />
Comment thread
jkoritzinsky marked this conversation as resolved.

<Message Text= "Full native project references are :%(NativeProjectReferenceNormalized.Identity)" />

Expand All @@ -70,6 +104,13 @@
%(NativeProjectReferenceNormalized.AdditionalProperties)"
Condition="'@(NativeProjectReference)' != ''" />

<MSBuild Projects="$(MSBuildProjectFile)"
Targets="CopyNativeProjectBinariesFromFileApi"
Properties="ReferencedCMakeProject=%(CMakeProjectReferenceNormalized.Identity);
ReferencedCMakeTargets=$([MSBuild]::Escape('%(CMakeProjectReferenceNormalized.CMakeTargets)'));
%(CMakeProjectReferenceNormalized.AdditionalProperties)"
Condition="'@(CMakeProjectReference)' != ''" />

</Target>

<Target Name="CopyNativeProjectBinariesFromFileApi">
Expand All @@ -93,6 +134,7 @@
<GetCMakeArtifactsFromFileApi
CMakeOutputDir="$(_CMakeOutputDir)"
SourceDirectory="$(_SourceDirectory)"
CMakeTargets="$(ReferencedCMakeTargets)"
Configuration="$(_Configuration)">
<Output TaskParameter="Artifacts" ItemName="NativeProjectBinaries" />
</GetCMakeArtifactsFromFileApi>
Expand Down
67 changes: 67 additions & 0 deletions src/Microsoft.DotNet.CMake.Sdk/src/ExecWithMutex.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// 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.Security.Cryptography;
using System.Text;
Comment thread
Copilot marked this conversation as resolved.
using System.Threading;

namespace Microsoft.DotNet.CMake.Sdk;

/// <summary>
/// Executes a command while holding a named, cross-process mutex.
/// </summary>
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
}
}
Loading
Loading