Skip to content

Commit 3a1709a

Browse files
committed
Merge branch 'main' into marquee
2 parents 86534c7 + f5b2388 commit 3a1709a

12 files changed

Lines changed: 293 additions & 29 deletions

GenerateVSCodeLaunchConfig.ps1

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
Param (
2+
[Parameter(HelpMessage = "Disables suppressing changes to the ./.vscode/launch.json file in git, allowing changes to be committed.")]
3+
[switch]$allowGitChanges = $false
4+
)
5+
6+
function CreateVsCodeLaunchConfigJson {
7+
param (
8+
[string]$projectName
9+
)
10+
11+
return "{
12+
`"name`": `"$projectName`",
13+
`"type`": `"coreclr`",
14+
`"request`": `"launch`",
15+
`"program`": `"dotnet`",
16+
`"args`": [
17+
`"run`",
18+
`"build`",
19+
`"/r`",
20+
`"/p:UnoSourceGeneratorUseGenerationHost=true`",
21+
`"/p:UnoSourceGeneratorUseGenerationController=false`",
22+
`"/p:UnoRemoteControlPort=443`",
23+
`"--project=`$`{workspaceFolder`}/labs/$projectName/samples/$projectName.Wasm/$projectName.Wasm.csproj`"
24+
],
25+
`"presentation`": {
26+
`"group`": `"2`"
27+
},
28+
`"cwd`": `"`$`{workspaceFolder`}/labs/$projectName/samples/$projectName.Wasm`"
29+
}";
30+
}
31+
32+
$launchConfigJson = Get-Content -Path "$PSScriptRoot/../.vscode/launch.json" -ErrorAction Stop;
33+
$launchConfig = $launchConfigJson | ConvertFrom-Json;
34+
35+
# Remove all non-generated configurations
36+
$originalConfigurations = $launchConfig.configurations;
37+
$launchConfig.configurations = @();
38+
$launchConfig.configurations += $originalConfigurations[0];
39+
$launchConfig.configurations += $originalConfigurations[1];
40+
41+
foreach ($wasmProjectPath in Get-ChildItem -Recurse -Path "$PSScriptRoot/../*/*/samples/*.Wasm/*.Wasm.csproj") {
42+
$projectName = [System.IO.Path]::GetFileNameWithoutExtension($wasmProjectPath) -Replace ".Wasm", "";
43+
Write-Host "Generating VSCode launch config for $projectName";
44+
45+
$configJson = CreateVsCodeLaunchConfigJson $projectName;
46+
$config = $configJson | ConvertFrom-Json;
47+
48+
$launchConfig.configurations += $config;
49+
}
50+
51+
if ($allowGitChanges.IsPresent) {
52+
Write-Warning "Changes to the default launch.json can now be committed. Run this command again without the -allowGitChanges flag to disable committing further changes.";
53+
git update-index --no-assume-unchanged $PSScriptRoot/../.vscode/launch.json
54+
}
55+
else {
56+
Write-Output "Changes to the default launch.json are now suppressed. To switch branches, run git reset --hard with a clean working tree. Include the -allowGitChanges flag to enable committing changes.";
57+
git update-index --assume-unchanged $PSScriptRoot/../.vscode/launch.json
58+
}
59+
60+
# Save
61+
Set-Content -Path "$PSScriptRoot/../.vscode/launch.json" -Value ($launchConfig | ConvertTo-Json -Depth 9);
62+
Write-Output "Saved VSCode launch configs to $(Resolve-Path $PSScriptRoot/../.vscode/launch.json)";

Labs.Head.props

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
<IsAllExperimentHead Condition="$(MSBuildProjectName.StartsWith('CommunityToolkit.Labs.')) == 'true'">true</IsAllExperimentHead>
3030
<IsProjectTemplateHead Condition="$(MSBuildProjectName.StartsWith('ProjectTemplate')) == 'true'">true</IsProjectTemplateHead>
3131
<IsSingleExperimentHead Condition="'$(IsAllExperimentHead)' != 'true' AND '$(IsProjectTemplateHead)' != 'true'">true</IsSingleExperimentHead>
32+
33+
<DefineConstants Condition="$(IsAllExperimentHead) == 'true'">$(DefineConstants);LABS_ALL_SAMPLES</DefineConstants>
3234
</PropertyGroup>
3335

3436
<!-- See https://github.com/CommunityToolkit/Labs-Windows/issues/142 -->

Labs.SampleRefs.props.template

Lines changed: 0 additions & 11 deletions
This file was deleted.

MultiTarget/Defaults.props

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<Project>
2+
<PropertyGroup>
3+
<MultiTarget>uwp;wasdk;wpf;wasm;linuxgtk;macos;ios;android;</MultiTarget>
4+
</PropertyGroup>
5+
</Project>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
Param (
2+
[Parameter(HelpMessage = "The directory where props files for discovered projects should be saved.")]
3+
[string]$projectPropsOutputDir = "$PSScriptRoot/Generated"
4+
)
5+
6+
$preWorkingDir = $pwd;
7+
Set-Location $PSScriptRoot;
8+
9+
# Delete and recreate output folder.
10+
Remove-Item -Path $projectPropsOutputDir -Recurse -Force -ErrorAction SilentlyContinue | Out-Null;
11+
New-Item -ItemType Directory -Force -Path $projectPropsOutputDir -ErrorAction SilentlyContinue | Out-Null;
12+
13+
# Discover projects in provided paths
14+
foreach ($projectPath in Get-ChildItem -Directory -Depth 0 -Path "$PSScriptRoot/../../labs/") {
15+
# Normalize project path
16+
$projectName = $projectPath.Name;
17+
18+
# Folder layout is expected to match the Community Toolkit.
19+
# Uses the <MultiTarget> values from the source library project as the fallback for the sample project.
20+
# This behavior also implemented in MultiTarget.props for TargetFramework evaluation.
21+
$srcPath = Resolve-Path "$($projectPath.FullName)\src";
22+
$srcProjectPath = Get-ChildItem -File "$srcPath\*.csproj";
23+
24+
$samplePath = Resolve-Path "$($projectPath.FullName)\samples\$projectName.Samples";
25+
$sampleProjectPath = Get-ChildItem -File "$samplePath\*.csproj";
26+
27+
if ($srcProjectPath.Length -eq 0) {
28+
Write-Error "Could not locate source csproj for $projectName";
29+
exit(-1);
30+
}
31+
32+
if ($sampleProjectPath.Length -eq 0) {
33+
Write-Error "Could not locate sample csproj for $projectName";
34+
exit(-1);
35+
}
36+
37+
# Generate <ProjectReference>s for sample project
38+
# Use source project MultiTarget as first fallback.
39+
& $PSScriptRoot\GenerateMultiTargetAwareProjectReferenceProps.ps1 -projectPath $sampleProjectPath -outputPath "$projectPropsOutputDir/$($sampleProjectPath.BaseName).props" -multiTargetFallbackPropsPath @("$srcPath/MultiTarget.props", "$samplePath/MultiTarget.props", "$PSScriptRoot/Defaults.props");
40+
41+
# Generate <ProjectReference>s for src project
42+
& $PSScriptRoot\GenerateMultiTargetAwareProjectReferenceProps.ps1 -projectPath $srcProjectPath -outputPath "$projectPropsOutputDir/$($srcProjectPath.BaseName).props" -multiTargetFallbackPropsPath @("$srcPath/MultiTarget.props", "$PSScriptRoot/Defaults.props");
43+
}
44+
45+
46+
Set-Location $preWorkingDir;
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
Param (
2+
[Parameter(HelpMessage = "The full path of the csproj to generated references to.", Mandatory = $true)]
3+
[string]$projectPath,
4+
5+
[Parameter(HelpMessage = "A path to a .props file where generated content should be saved to.", Mandatory = $true)]
6+
[string]$outputPath,
7+
8+
[Parameter(HelpMessage = "The path to the template used to generate the props file.")]
9+
[string]$templatePath = "$PSScriptRoot/MultiTargetAwareProjectReference.props.template",
10+
11+
[Parameter(HelpMessage = "The path to the props file that contains the default MultiTarget values.")]
12+
[string[]]$multiTargetFallbackPropsPath = @("$PSScriptRoot/Defaults.props"),
13+
14+
[Parameter(HelpMessage = "The placeholder text to replace when inserting the project file name into the template.")]
15+
[string]$projectFileNamePlaceholder = "[ProjectFileName]",
16+
17+
[Parameter(HelpMessage = "The placeholder text to replace when inserting the project path into the template.")]
18+
[string]$projectRootPlaceholder = "[ProjectRoot]"
19+
)
20+
21+
$preWorkingDir = $pwd;
22+
Set-Location $PSScriptRoot;
23+
24+
$relativeProjectPath = Invoke-Expression -C "(Resolve-Path -Relative -Path $projectPath)";
25+
$templateContents = Get-Content -Path $templatePath;
26+
27+
Set-Location $preWorkingDir;
28+
29+
# Insert csproj file name.
30+
$csprojFileName = [System.IO.Path]::GetFileName($relativeProjectPath);
31+
$templateContents = $templateContents -replace [regex]::escape($projectFileNamePlaceholder), $csprojFileName;
32+
33+
# Insert project directory
34+
$relativeProjectDirectory = [System.IO.Path]::GetDirectoryName($relativeProjectPath);
35+
$templateContents = $templateContents -replace [regex]::escape($projectRootPlaceholder), "$relativeProjectDirectory";
36+
37+
function LoadMultiTargetsFrom([string] $path) {
38+
$fileContents = "";
39+
40+
# If file does not exist
41+
if ($false -eq (Test-Path -Path $path -PathType Leaf)) {
42+
# Load first available default
43+
foreach ($fallbackPath in $multiTargetFallbackPropsPath) {
44+
if (Test-Path $fallbackPath) {
45+
$fileContents = Get-Content $fallbackPath -ErrorAction Stop;
46+
break;
47+
}
48+
}
49+
}
50+
else {
51+
# Load requested file
52+
$fileContents = Get-Content $path -ErrorAction Stop;
53+
}
54+
55+
# Parse file contents
56+
$regex = Select-String -Pattern '<MultiTarget>(.+?)<\/MultiTarget>' -InputObject $fileContents;
57+
58+
if ($null -eq $regex -or $null -eq $regex.Matches -or $null -eq $regex.Matches.Groups -or $regex.Matches.Groups.Length -lt 2) {
59+
Write-Error "Couldn't get MultiTarget property from $path";
60+
exit(-1);
61+
}
62+
63+
return $regex.Matches.Groups[1].Value;
64+
}
65+
66+
# Load multitarget preferences for project
67+
$multiTargets = LoadMultiTargetsFrom("$([System.IO.Path]::GetDirectoryName($projectPath))\MultiTarget.props");
68+
69+
$templateContents = $templateContents -replace [regex]::escape("[IntendedTargets]"), $multiTargets;
70+
71+
$multiTargets = $multiTargets.Split(';');
72+
Write-Host "Generating project references for $([System.IO.Path]::GetFileNameWithoutExtension($csprojFileName)): $($multiTargets -Join ', ')"
73+
74+
$templateContents = $templateContents -replace [regex]::escape("[CanTargetWasm]"), "'$($multiTargets.Contains("wasm").ToString().ToLower())'";
75+
$templateContents = $templateContents -replace [regex]::escape("[CanTargetUwp]"), "'$($multiTargets.Contains("uwp").ToString().ToLower())'";
76+
$templateContents = $templateContents -replace [regex]::escape("[CanTargetWasdk]"), "'$($multiTargets.Contains("wasdk").ToString().ToLower())'";
77+
$templateContents = $templateContents -replace [regex]::escape("[CanTargetWpf]"), "'$($multiTargets.Contains("wpf").ToString().ToLower())'";
78+
$templateContents = $templateContents -replace [regex]::escape("[CanTargetLinuxGtk]"), "'$($multiTargets.Contains("linuxgtk").ToString().ToLower())'";
79+
$templateContents = $templateContents -replace [regex]::escape("[CanTargetMacOS]"), "'$($multiTargets.Contains("macos").ToString().ToLower())'";
80+
$templateContents = $templateContents -replace [regex]::escape("[CanTargetiOS]"), "'$($multiTargets.Contains("ios").ToString().ToLower())'";
81+
$templateContents = $templateContents -replace [regex]::escape("[CanTargetDroid]"), "'$($multiTargets.Contains("android").ToString().ToLower())'";
82+
83+
# Save to disk
84+
Set-Content -Path $outputPath -Value $templateContents;
Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,30 @@
11
<Project>
22
<PropertyGroup>
3-
<!--
4-
TargetFramework for WasmLibTargetFramework, WpfLibTargetFramework, and LinuxLibTargetFramework all use the same value.
3+
<MultiTargetIsSampleProject Condition="$(MSBuildProjectName.EndsWith('.Samples')) == 'true'">true</MultiTargetIsSampleProject>
4+
</PropertyGroup>
5+
6+
<Import Project="$(MSBuildThisFileDirectory)\Defaults.props" Condition="'$(MultiTarget)' == ''" />
7+
<Import Project="$(MSBuildProjectDirectory)\MultiTarget.props" Condition="Exists('$(MSBuildProjectDirectory)\MultiTarget.props') AND '$(MultiTarget)' == ''" />
58

6-
This can't be removed during the evaluation phase without breaking things, so we omit them entirely
7-
and use the value directly.
9+
<!-- If in a sample project, pull the MultiTarget settings from the source project. -->
10+
<!-- This behavior is also implemented in GeneratedAllProjectReferences.ps1 for <ProjectReference> generation. -->
11+
<Import Project="../../src/MultiTarget.props" Condition="$(MultiTargetIsSampleProject) == 'true' AND !Exists('$(MSBuildProjectDirectory)\MultiTarget.props') AND Exists('../../src/MultiTarget.props')" />
12+
13+
<PropertyGroup>
14+
<!--
15+
MultiTarget is a custom property that indicates which target a project is designed to be built for / run on.
16+
Used to create project references, generate solution files, enable/disable TargetFrameworks, and build nuget packages.
817
-->
9-
<TargetFrameworks>
10-
$(NetStandardCommonTargetFramework);
11-
$(UwpTargetFramework);
12-
$(WinAppSdkTargetFramework);
13-
$(MacOSLibTargetFramework);
14-
$(iOSLibTargetFramework);
15-
$(AndroidLibTargetFramework);
16-
</TargetFrameworks>
18+
<TargetFrameworks Condition="$(MultiTarget.Contains('uwp')) == 'true'">$(TargetFrameworks);$(UwpTargetFramework)</TargetFrameworks>
19+
<TargetFrameworks Condition="$(MultiTarget.Contains('wasdk')) == 'true'">$(TargetFrameworks);$(WinAppSdkTargetFramework)</TargetFrameworks>
20+
<!--
21+
TargetFrameworks for WasmLibTargetFramework, WpfLibTargetFramework, and LinuxLibTargetFramework all use the same value.
22+
Duplicate values can't be removed during the evaluation phase without breaking things, so we use the value directly.
23+
-->
24+
<TargetFrameworks Condition="$(MultiTarget.Contains('wasm')) == 'true' OR $(MultiTarget.Contains('wpf')) == 'true' OR $(MultiTarget.Contains('linuxgtk')) == 'true'">$(TargetFrameworks);$(NetStandardCommonTargetFramework)</TargetFrameworks>
25+
<TargetFrameworks Condition="$(MultiTarget.Contains('macos')) == 'true'">$(TargetFrameworks);$(MacOSLibTargetFramework)</TargetFrameworks>
26+
<TargetFrameworks Condition="$(MultiTarget.Contains('ios')) == 'true'">$(TargetFrameworks);$(iOSLibTargetFramework)</TargetFrameworks>
27+
<TargetFrameworks Condition="$(MultiTarget.Contains('android')) == 'true'">$(TargetFrameworks);$(AndroidLibTargetFramework)</TargetFrameworks>
1728

1829
<EnableWindowsTargeting>true</EnableWindowsTargeting>
1930
<GenerateLibraryLayout>true</GenerateLibraryLayout>
@@ -25,10 +36,12 @@
2536
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
2637
<TargetPlatformVersion>10.0.19041.0</TargetPlatformVersion>
2738
</PropertyGroup>
39+
2840
<PropertyGroup>
2941
<!-- These suppressions are for references between generated assemblies and that VS can keep in the Error List once resolved -->
3042
<NoWarn>WMC1006;CS8034;</NoWarn>
3143
</PropertyGroup>
44+
3245
<ItemGroup>
3346
<PackageReference Condition="'$(TargetFramework)' == '$(UwpTargetFramework)'" Include="Microsoft.UI.Xaml" Version="2.7.0" />
3447
<PackageReference Condition="'$(TargetFramework)' == '$(WinAppSdkTargetFramework)'" Include="Microsoft.WindowsAppSDK" Version="1.0.3" />
@@ -46,6 +59,7 @@
4659
</Compile>
4760
<UpToDateCheckInput Include="**\*.xaml" Exclude="bin\**\*.xaml;obj\**\*.xaml" />
4861
</ItemGroup>
62+
4963
<ItemGroup>
5064
<None Remove="obj\bin\**" />
5165
<Page Remove="obj\bin\**" />
@@ -64,4 +78,4 @@
6478
<HintPath Condition="Exists('c:\Program Files\Microsoft Visual Studio\2022\Preview\Common7\IDE\Extensions\Xamarin.VisualStudio')">c:\Program Files\Microsoft Visual Studio\2022\Preview\Common7\IDE\Extensions\Xamarin.VisualStudio\Xamarin.Mac.dll</HintPath>
6579
</Reference>
6680
</ItemGroup>
67-
</Project>
81+
</Project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<!--
2+
This file was generated by a tool.
3+
Adds a ProjectReference only if the referenced project indicates it can target the referencing project.
4+
-->
5+
<Project>
6+
<!-- Before importing, check that the imported project works on the current framework -->
7+
<!-- Condition must be on a <When> statement for this to work in Visual Studio. https://stackoverflow.com/a/16557059 -->
8+
<Choose>
9+
<!-- [IntendedTargets] -->
10+
<When Condition="($(IsWasm) == 'true' AND [CanTargetWasm] == 'true') OR
11+
($(IsUwp) == 'true' AND [CanTargetUwp] == 'true') OR
12+
($(IsWinAppSdk) == 'true' AND [CanTargetWasdk] == 'true') OR
13+
($(IsWpf) == 'true' AND [CanTargetWpf] == 'true') OR
14+
($(IsGtk) == 'true' AND [CanTargetLinuxGtk] == 'true') OR
15+
($(IsMacOS) == 'true' AND [CanTargetMacOS] == 'true') OR
16+
($(IsiOS) == 'true' AND [CanTargetiOS] == 'true') OR
17+
($(IsDroid) == 'true' AND [CanTargetDroid] == 'true')">
18+
<ItemGroup>
19+
<ProjectReference Include="[ProjectRoot]\[ProjectFileName]" />
20+
</ItemGroup>
21+
</When>
22+
<Otherwise>
23+
</Otherwise>
24+
</Choose>
25+
</Project>

MultiTarget/ReadMe.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# MultiTarget
2+
3+
`<MultiTarget>` is a custom property that indicates which target a component is designed to be built for / run on.
4+
5+
The supplied targets are used to create project references, generate solution files, enable/disable TargetFrameworks, and build nuget packages.
6+
7+
## Basic usage
8+
9+
Create a `MultiTarget.props` file in the root of your source project to change the platform targets for your component. This will be picked up automatically by your sample project, unless it has a `MultiTarget.props` of its own defined.
10+
11+
By default, all available targets are enabled:
12+
```xml
13+
<Project>
14+
<PropertyGroup>
15+
<MultiTarget>uwp;wasdk;wpf;wasm;linuxgtk;macos;ios;android;</MultiTarget>
16+
</PropertyGroup>
17+
</Project>
18+
```
19+
20+
For example, to only target UWP, WASM and Android:
21+
22+
```xml
23+
<Project>
24+
<PropertyGroup>
25+
<MultiTarget>uwp;wasm;android</MultiTarget>
26+
</PropertyGroup>
27+
</Project>
28+
```
29+
30+
31+
## ProjectReference Generation
32+
33+
The script `GenerateAllProjectReferences.ps1` will scan for toolkit components and generate `.props` files for each.
34+
35+
## NuGet Packages
36+
37+
The `<MultiTarget>` property is used to define the `TargetFrameworks` supported by that project. Projects packed into a NuGet packages will reflect this.

0 commit comments

Comments
 (0)