Skip to content

Commit

Permalink
Implemented new AddMultiTargetCompatibleSampleDocs target (#141)
Browse files Browse the repository at this point in the history
* Added AddMultiTargetCompatibleSampleDocs target.
Fixes #5.

* Fixed a lowercase T

* Fixed target execution order

* Use RoslynCodeTaskFactory

* Fix typo

* Fixed MultiTarget Identifiers
  • Loading branch information
Arlodotexe authored Sep 29, 2023
1 parent ef77ff0 commit 1b6a0a6
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 3 deletions.
9 changes: 6 additions & 3 deletions ProjectHeads/App.Head.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
<!-- Shared project -->
<Import Project="$(ToolingDirectory)\CommunityToolkit.App.Shared\CommunityToolkit.App.Shared.projitems" Label="Shared" />

<!-- Gather sample docs from all components -->
<Import Project="$(ToolingDirectory)\ProjectHeads\Targets\AddMultiTargetCompatibleSampleDocs.targets" />

<!-- Shared project dependencies -->
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
Expand Down Expand Up @@ -64,7 +67,7 @@
</ItemGroup>
</When>
</Choose>
<Choose>
<Choose>
<When Condition="'$(ToolkitTriggersSourceProject)' == ''">
<ItemGroup>
<PackageReference Include="CommunityToolkit.$(DependencyVariant).Triggers" Version="8.0.230823-rc"/>
Expand All @@ -76,6 +79,8 @@
</ItemGroup>
</When>
</Choose>

<!-- Content inclusion -->
<!-- See https://github.com/CommunityToolkit/Labs-Windows/issues/142 -->
<ItemGroup Condition="'$(IsAllExperimentHead)' == 'true'">
<!-- These are also included in the Samples props file, but added here to workaround https://github.com/unoplatform/uno/issues/2502 -->
Expand All @@ -92,8 +97,6 @@
<Link>SourceAssets/%(RecursiveDir)%(FileName)%(Extension).dat</Link>
</Content>

<!-- Include markdown files from all samples so the head can access them in the source generator -->
<AdditionalFiles Include="$(RepositoryDirectory)components\**\samples\**\*.md" Exclude="$(RepositoryDirectory)**\**\samples\**\obj\**\*.md;$(RepositoryDirectory)**\**\samples\**\bin\**\*.md"/>
<AdditionalFiles Include="$(RepositoryDirectory)components\**\src\**\*.csproj" />
</ItemGroup>

Expand Down
41 changes: 41 additions & 0 deletions ProjectHeads/Targets/AddMultiTargetCompatibleSampleDocs.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<Project>
<!-- Gather sample markdown for components that MultiTargets this platform. -->
<!-- Each deployable sample app head uses the CommunityToolkit.Toolkit.SampleGen source generator to generate document registries -->
<Import Project="$(ToolingDirectory)\ProjectHeads\Tasks\CheckMultiTarget.props" />

<!-- Simplifies comparison of the current head against component-level <MultiTarget> definition -->
<PropertyGroup>
<MultiTargetIdentifier Condition="'$(IsUwp)' == 'true'">uwp</MultiTargetIdentifier>
<MultiTargetIdentifier Condition="'$(IsWasm)' == 'true'">wasm</MultiTargetIdentifier>
<MultiTargetIdentifier Condition="'$(IsWinAppSdk)' == 'true'">wasdk</MultiTargetIdentifier>
<MultiTargetIdentifier Condition="'$(IsMacOS)' == 'true'">macos</MultiTargetIdentifier>
<MultiTargetIdentifier Condition="'$(IsiOS)' == 'true'">ios</MultiTargetIdentifier>
<MultiTargetIdentifier Condition="'$(IsDroid)' == 'true'">android</MultiTargetIdentifier>
<MultiTargetIdentifier Condition="'$(IsNetstandard)' == 'true'">netstandard</MultiTargetIdentifier>
</PropertyGroup>

<Target Name="AddMultiTargetCompatibleSampleDocs" Condition="'$(IsAllExperimentHead)' == 'true'" BeforeTargets="BeforeBuild">
<Error Condition="'$(MultiTargetIdentifier)' == ''" Text="Missing MultiTargetIdentifier $(MultiTargetIdentifier). Is the `IsSomePlatform` property (`IsUwp`, `IsWasm`, etc) set on the deployable project head?" />

<ItemGroup>
<AllComponentMarkdownFiles Include="$(RepositoryDirectory)components\**\samples\**\*.md" Exclude="$(RepositoryDirectory)**\**\samples\**\obj\**\*.md;$(RepositoryDirectory)**\**\samples\**\bin\**\*.md"/>
</ItemGroup>

<Message Text="Found @(AllComponentMarkdownFiles->Count()) total sample docs" Importance="high" />

<CheckMultiTarget FilePath="%(AllComponentMarkdownFiles.Identity)" MultiTargetIdentifier="$(MultiTargetIdentifier)" RepositoryDirectory="$(RepositoryDirectory)">
<Output TaskParameter="FilePath" ItemName="ProcessedMarkdownFiles" />
</CheckMultiTarget>

<ItemGroup>
<SupportedMarkdownFiles Include="%(ProcessedMarkdownFiles.Identity)" Condition="%(ProcessedMarkdownFiles.IsSupported) == 'true'" />
</ItemGroup>

<Message Text="@(SupportedMarkdownFiles->Count()) sample docs are supported on the current MultiTarget '$(MultiTargetIdentifier)':" Importance="high" />
<Message Text="Included %(SupportedMarkdownFiles.Identity)" Importance="high" />

<ItemGroup>
<AdditionalFiles Include="%(SupportedMarkdownFiles.Identity)" />
</ItemGroup>
</Target>
</Project>
131 changes: 131 additions & 0 deletions ProjectHeads/Tasks/CheckMultiTarget.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<!--
MSBuild Inline C# Task Tips:
1. Avoid defining methods within methods for inline tasks. Use lambda functions instead for any reusable logic.
Example:
Instead of nested methods:
```csharp
void MainLogic() {
Helper();
void Helper() { /* ... */ }
}
```
Use lambda:
```csharp
Func<Type, ReturnType> Helper = (params) => { /* ... */ return result; };
ReturnType result = Helper(params);
```
This ensures compatibility with MSBuild's code generation.
2. When using lambdas, be cautious of variable naming. If a lambda uses a parameter with the same name as an outer scope variable, it can lead to unexpected behavior.
Example:
Avoid:
```csharp
string variable;
Func<string, string> Lambda = (variable) => { /* ... */ };
```
Instead, use different names:
```csharp
string variable;
Func<string, string> Lambda = (param) => { /* ... */ };
```
3. Certain C# features, like string interpolation with the `$""` syntax, may not be supported depending on the MSBuild's CodeTaskFactory and its underlying C# compiler version. Consider using traditional string concatenation or other methods if you encounter issues.
4. Always use fully qualified namespaces in inline tasks to ensure all types are recognized correctly. This can avoid issues where certain types or methods are not found.
-->
<UsingTask TaskName="CheckMultiTarget" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<FilePath ParameterType="Microsoft.Build.Framework.ITaskItem" Required="true" Output="true" />
<MultiTargetIdentifier ParameterType="System.String" Required="true" />
<RepositoryDirectory ParameterType="System.String" Required="true" />
<IsSupported ParameterType="System.Boolean" Output="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<![CDATA[
if (FilePath == null || string.IsNullOrEmpty(FilePath.ItemSpec))
{
Log.LogError("FilePath is null or empty.");
return false; // Exit the task
}
string currentDirectory = System.IO.Path.GetDirectoryName(FilePath.ItemSpec);
if (string.IsNullOrEmpty(currentDirectory))
{
Log.LogError("Failed to get directory name from FilePath.");
return false; // Exit the task
}
bool foundSrc = false;
while (currentDirectory.Contains("components"))
{
if (System.IO.Directory.Exists(System.IO.Path.Combine(currentDirectory, "src")))
{
foundSrc = true;
break;
}
var parentDir = System.IO.Directory.GetParent(currentDirectory);
if (parentDir != null)
{
currentDirectory = parentDir.FullName;
}
else
{
break;
}
}
if (!foundSrc)
{
Log.LogError(string.Format("Failed to find a parent component directory with 'src' for {0}.", FilePath.ItemSpec));
return false; // Exit the task
}
var parentComponent = currentDirectory;
string multiTargetPropsPath;
string multiTargetPropsContent;
string multiTargetIdentifier;
Func<string, string, string> GetMultiTargetPropsPath = (parent, subDir) => System.IO.Path.Combine(parent, subDir, "MultiTarget.props");
Func<string, string> ReadMultiTargetPropsOrDefault = path => System.IO.File.Exists(path) ? System.IO.File.ReadAllText(path) : string.Empty;
Func<string, string> ExtractMultiTargetIdentifier = content => {
System.Text.RegularExpressions.Match match = System.Text.RegularExpressions.Regex.Match(content, @"<MultiTarget>([^<]*)</MultiTarget>", System.Text.RegularExpressions.RegexOptions.Singleline);
return match.Success ? match.Groups[1].Value : string.Empty;
};
multiTargetPropsPath = GetMultiTargetPropsPath(parentComponent, "sample");
multiTargetPropsContent = ReadMultiTargetPropsOrDefault(multiTargetPropsPath);
multiTargetIdentifier = ExtractMultiTargetIdentifier(multiTargetPropsContent);
if (string.IsNullOrEmpty(multiTargetIdentifier))
{
multiTargetPropsPath = GetMultiTargetPropsPath(parentComponent, "src");
multiTargetPropsContent = ReadMultiTargetPropsOrDefault(multiTargetPropsPath);
multiTargetIdentifier = ExtractMultiTargetIdentifier(multiTargetPropsContent);
}
if (string.IsNullOrWhiteSpace(multiTargetIdentifier))
{
multiTargetPropsPath = System.IO.Path.Combine(RepositoryDirectory, "tooling", "MultiTarget", "Defaults.props");
multiTargetPropsContent = ReadMultiTargetPropsOrDefault(multiTargetPropsPath);
multiTargetIdentifier = ExtractMultiTargetIdentifier(multiTargetPropsContent);
}
IsSupported = multiTargetIdentifier.Split(';').Contains(MultiTargetIdentifier);
FilePath.SetMetadata("IsSupported", IsSupported.ToString());
]]>

</Code>
</Task>
</UsingTask>

</Project>

0 comments on commit 1b6a0a6

Please sign in to comment.