Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented new AddMultiTargetCompatibleSampleDocs target #141

Merged
merged 8 commits into from
Sep 29, 2023
Merged
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
9 changes: 6 additions & 3 deletions ProjectHeads/App.Head.props
Arlodotexe marked this conversation as resolved.
Show resolved Hide resolved
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>
Loading