-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDirectory.Build.targets
More file actions
242 lines (219 loc) · 13.6 KB
/
Copy pathDirectory.Build.targets
File metadata and controls
242 lines (219 loc) · 13.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
<Project>
<Target Name="PublishUnoAssets" AfterTargets="ComputeFilesToPublish" Condition="'$(TargetFramework)' == 'net10.0-desktop'">
<ItemGroup>
<_FluentFont Include="$(TargetDir)Uno.Fonts.Fluent\Fonts\**\*.*" />
<ResolvedFileToPublish Include="@(_FluentFont)">
<RelativePath>Uno.Fonts.Fluent/Fonts/%(RecursiveDir)%(Filename)%(Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
<_OpenSansFont Include="$(TargetDir)Uno.Fonts.OpenSans\Fonts\**\*.*" />
<ResolvedFileToPublish Include="@(_OpenSansFont)">
<RelativePath>Uno.Fonts.OpenSans/Fonts/%(RecursiveDir)%(Filename)%(Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
<_RobotoFont Include="$(TargetDir)Uno.Fonts.Roboto\Fonts\**\*.*" />
<ResolvedFileToPublish Include="@(_RobotoFont)">
<RelativePath>Uno.Fonts.Roboto/Fonts/%(RecursiveDir)%(Filename)%(Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
<_CompiledAsset Include="$(TargetDir)Assets\**\*.*" />
<ResolvedFileToPublish Include="@(_CompiledAsset)">
<RelativePath>Assets/%(RecursiveDir)%(Filename)%(Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
<_ImagesFolder Include="$(TargetDir)images\**\*.*" />
<ResolvedFileToPublish Include="@(_ImagesFolder)">
<RelativePath>images/%(RecursiveDir)%(Filename)%(Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
<_LiveChartsFolder Include="$(TargetDir)LiveChartsCore.SkiaSharpView.Uno.WinUI\**\*.*" />
<ResolvedFileToPublish Include="@(_LiveChartsFolder)">
<RelativePath>LiveChartsCore.SkiaSharpView.Uno.WinUI/%(RecursiveDir)%(Filename)%(Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
<Target Name="RemoveUnwantedWindowsMuiFiles"
AfterTargets="Build;Publish"
Condition="'$(TargetPlatformIdentifier)' == 'Windows' and '$(WindowsPackageType)' == 'None' and '$(WindowsMuiLanguagesToKeep)' != ''">
<ItemGroup>
<_MuiLanguageToKeep Include="$(WindowsMuiLanguagesToKeep)" />
<_MuiDirectoryToKeep Include="@(_MuiLanguageToKeep->'$(TargetDir)%(Identity)')"
Condition="'$(TargetDir)' != '' and Exists('$(TargetDir)')" />
<_MuiDirectoryToKeep Include="@(_MuiLanguageToKeep->'$(PublishDir)%(Identity)')"
Condition="'$(PublishDir)' != '' and Exists('$(PublishDir)')" />
<_MuiFileToKeep Include="@(_MuiLanguageToKeep->'$(TargetDir)%(Identity)\*.mui')"
Condition="'$(TargetDir)' != '' and Exists('$(TargetDir)')" />
<_MuiFileToKeep Include="@(_MuiLanguageToKeep->'$(PublishDir)%(Identity)\*.mui')"
Condition="'$(PublishDir)' != '' and Exists('$(PublishDir)')" />
<_MuiFileToRemove Include="$(TargetDir)**\*.mui"
Exclude="@(_MuiFileToKeep)"
Condition="'$(TargetDir)' != '' and Exists('$(TargetDir)')" />
<_MuiFileToRemove Include="$(PublishDir)**\*.mui"
Exclude="@(_MuiFileToKeep)"
Condition="'$(PublishDir)' != '' and Exists('$(PublishDir)')" />
<_MuiDirectoryToRemove Include="@(_MuiFileToRemove->'%(RootDir)%(Directory)')"
Exclude="@(_MuiDirectoryToKeep)" />
</ItemGroup>
<Delete Files="@(_MuiFileToRemove)" />
<RemoveDir Directories="@(_MuiDirectoryToRemove)" />
</Target>
<!-- ===== IconifyBundle: extract ONLY the icons we use to SVG + register as UnoImage =====
The IconifyBundle.<Pack> packages ship each pack as a single tab-separated `*.icondata` file
(name<TAB>w<TAB>h<TAB>body), not loose SVGs, and Uno.Resizetizer enforces strict asset names.
So we extract only the icons listed in @(IconifyIcon) (items shaped "prefix:name", declared per
project), wrap each in <svg>, and write it under Assets/Iconify/ with a sanitized, pack-prefixed,
globally unique asset name ("simple-icons:amd" -> simple_icons_amd.svg), then register those as UnoImage
so Resizetizer handles them for build AND publish. Incremental: re-extracts when the list, an .icondata,
this file, or the project changes. No-op where @(IconifyIcon) is empty. Needs GeneratePathProperty="true"
on the IconifyBundle.* PackageReferences.
Output lands under the project's Assets/ tree (NOT obj/) because Uno.Resizetizer derives an asset's
logical ms-appx path from its source location: an obj/ source yields a config/TFM-specific path like
ms-appx:///<proj>/obj/Debug/<tfm>/... while Assets/Iconify/foo.svg yields the clean, stable
ms-appx:///Assets/Iconify/foo.png the UI resolver depends on. The folder is git-ignored and fully
regenerated each run. -->
<PropertyGroup>
<!-- Relative dir is used for the UnoImage Include glob: a project-relative include makes Resizetizer
derive the clean logical path Assets/Iconify/foo.png (an absolute include adds a stray project-name
segment). The absolute dir is used by the extraction task so its File I/O is CWD-independent. -->
<_IconifyRelDir>Assets\Iconify\</_IconifyRelDir>
<_IconifyOutDir>$(MSBuildProjectDirectory)\$(_IconifyRelDir)</_IconifyOutDir>
<_IconifyStamp>$(IntermediateOutputPath)IconifyIcons.stamp</_IconifyStamp>
</PropertyGroup>
<ItemGroup>
<_IconifyIcondata Include="$(PkgIconifyBundle_SimpleIcons)\build\*.icondata" Condition="'$(PkgIconifyBundle_SimpleIcons)' != ''" />
<_IconifyIcondata Include="$(PkgIconifyBundle_Logos)\build\*.icondata" Condition="'$(PkgIconifyBundle_Logos)' != ''" />
</ItemGroup>
<UsingTask TaskName="ExtractIcondataIcons" TaskFactory="RoslynCodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<WantedIcons ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<IcondataFiles ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<OutputDir ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Class" Language="cs"><![CDATA[
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
public class ExtractIcondataIcons : Task
{
public ITaskItem[] WantedIcons { get; set; }
public ITaskItem[] IcondataFiles { get; set; }
public string OutputDir { get; set; }
public override bool Execute()
{
if (Directory.Exists(OutputDir)) Directory.Delete(OutputDir, true);
Directory.CreateDirectory(OutputDir);
// prefix -> (icon name -> "w\th\tbody")
var packs = new Dictionary<string, Dictionary<string, string>>(StringComparer.Ordinal);
foreach (var item in IcondataFiles)
{
var path = item.ItemSpec;
if (!File.Exists(path)) continue;
string prefix = null;
Dictionary<string, string> map = null;
foreach (var line in File.ReadLines(path))
{
if (line.Length == 0) continue;
if (prefix == null && line.StartsWith("prefix="))
{
prefix = line.Substring(7).Trim();
map = new Dictionary<string, string>(StringComparer.Ordinal);
packs[prefix] = map;
continue;
}
if (line.StartsWith("class=")) continue;
if (map == null) continue;
var t = line.IndexOf('\t');
if (t > 0) map[line.Substring(0, t)] = line.Substring(t + 1);
}
}
int written = 0;
foreach (var wanted in WantedIcons)
{
var spec = wanted.ItemSpec.Trim();
var colon = spec.IndexOf(':');
if (colon <= 0) { Log.LogWarning("IconifyBundle: '" + spec + "' is not in 'prefix:name' form."); continue; }
var prefix = spec.Substring(0, colon);
var name = spec.Substring(colon + 1);
Dictionary<string, string> map;
string data;
if (!packs.TryGetValue(prefix, out map) || !map.TryGetValue(name, out data))
{
Log.LogWarning("IconifyBundle: icon '" + spec + "' not found (is the matching IconifyBundle.<Pack> referenced?).");
continue;
}
var parts = data.Split(new[] { '\t' }, 3); // width, height, body
if (parts.Length < 3) continue;
var asset = Sanitize(prefix + "_" + name);
// Monochrome packs (e.g. simple-icons) paint with fill="currentColor", which Resizetizer
// rasterizes to black — invisible on the dark theme. Optional Color metadata on the
// IconifyIcon item bakes the brand color into the asset (multicolor packs omit it).
var body = parts[2];
var color = wanted.GetMetadata("Color");
if (!string.IsNullOrEmpty(color)) body = body.Replace("currentColor", color);
var svg = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 "
+ parts[0] + " " + parts[1] + "\">" + body + "</svg>";
File.WriteAllText(Path.Combine(OutputDir, asset + ".svg"), svg);
written++;
}
Log.LogMessage(MessageImportance.High, "IconifyBundle: extracted " + written + " of " + WantedIcons.Length + " requested icon(s) -> " + OutputDir);
return true;
}
// Uno.Resizetizer asset names: lowercase, alnum/underscore only, must start AND end with a letter.
// hyphens -> '_'; a non-letter first/last char is padded with 'i'. The pack prefix guarantees a
// letter start and >= 2 chars.
static string Sanitize(string name)
{
var chars = name.ToLowerInvariant().ToCharArray();
for (int i = 0; i < chars.Length; i++)
{
var c = chars[i];
if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))) chars[i] = '_';
}
var s = new string(chars);
if (s.Length == 0) return "icon";
if (!(s[0] >= 'a' && s[0] <= 'z')) s = "i" + s;
if (!(s[s.Length - 1] >= 'a' && s[s.Length - 1] <= 'z')) s = s + "i";
return s;
}
}
]]></Code>
</Task>
</UsingTask>
<!-- Writes the requested icon SVGs (incremental). -->
<Target Name="_WriteIconifyImages"
Condition="'@(IconifyIcon)' != '' and '@(_IconifyIcondata)' != ''"
Inputs="@(_IconifyIcondata);$(MSBuildThisFileFullPath);$(MSBuildProjectFullPath)"
Outputs="$(_IconifyStamp)">
<ExtractIcondataIcons WantedIcons="@(IconifyIcon)" IcondataFiles="@(_IconifyIcondata)" OutputDir="$(_IconifyOutDir)" />
<Touch Files="$(_IconifyStamp)" AlwaysCreate="true" />
</Target>
<!-- Registers the extracted SVGs as UnoImage before Resizetizer collects (build + publish). -->
<Target Name="_AddIconifyUnoImages"
Condition="'@(IconifyIcon)' != '' and '@(_IconifyIcondata)' != ''"
BeforeTargets="UnoResizetizeCollectItems"
DependsOnTargets="_WriteIconifyImages">
<!-- Collect into a private item FIRST, then project into UnoImage with QUALIFIED metadata.
Writing the glob and `<Link>Assets\Iconify\%(RecursiveDir)%(Filename)%(Extension)</Link>` together
looks equivalent but is not: unqualified %() inside a target batches the ItemGroup over every
EXISTING @(UnoImage), so the whole Iconify set was re-added once per pre-existing image and stamped
with THAT image's path as its Link. The result was a cartesian blow-up in which several different
SVGs all claimed Assets/Iconify/main_logo.png, and Resizetizer's parallel resize then had two threads
creating the same file — an IOException that surfaced only when the timing lined up (it broke the
linux-arm64 job while linux-x64 passed). Qualifying the metadata pins the batch to this item. -->
<ItemGroup>
<_IconifySvgFile Include="$(_IconifyRelDir)**\*.svg" />
</ItemGroup>
<ItemGroup>
<!-- Link pins the logical ms-appx path deterministically (Resizetizer captures it into its manifest):
ms-appx:///Assets/Iconify/<asset>.png. Without it, a glob added inside a target infers its path from
the build's startup dir and picks up a stray project-name segment. -->
<UnoImage Include="@(_IconifySvgFile)"
Link="Assets\Iconify\%(_IconifySvgFile.RecursiveDir)%(_IconifySvgFile.Filename)%(_IconifySvgFile.Extension)" />
</ItemGroup>
</Target>
</Project>