Skip to content

Commit 96e97dd

Browse files
committed
Generate mappings used by Xamarin.Android.Build.Tasks.
1 parent bde026f commit 96e97dd

File tree

46 files changed

+1737
-1156
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1737
-1156
lines changed

Documentation/workflow/HowToAddNewApiLevel.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ the more manual parts like enumification that will likely change as the APIs mat
1313
- Add new level to `/build-tools/xaprepare/xaprepare/ConfigAndData/Dependencies/AndroidToolchain.cs`:
1414
- `new AndroidPlatformComponent ("platform-S_r01", apiLevel: "S", pkgRevision: "1"),`
1515

16-
At this point, you can run `Xamarin.Android.sln /t:Prepare` using your usual mechanism, and
16+
At this point, you can run `Xamarin.Android.sln -t:Prepare` using your usual mechanism, and
1717
the new platform will be downloaded to your local Android SDK.
1818

1919
### Generate `params.txt` File
@@ -49,6 +49,11 @@ the new platform will be downloaded to your local Android SDK.
4949
- Add required metadata fixes in `/src/Mono.Android/metadata` until `Mono.Android.csproj` builds
5050
- Check that new package/namespaces are properly cased
5151

52+
### New AndroidManifest.xml Elements
53+
54+
- See `build-tools/manifest-attribute-codegen/README.md` for instructions on surfacing any new
55+
elements or attributes added to `AndroidManifest.xml`.
56+
5257
### ApiCompat
5358

5459
There may be ApiCompat issues that need to be examined. Either fix the assembly with metadata or allow

build-tools/manifest-attribute-codegen/Extensions/StringExtensions.cs

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Diagnostics.CodeAnalysis;
12
using System.Text;
23
using System.Xml.Linq;
34
using Xamarin.SourceWriter;
@@ -41,19 +42,6 @@ public static string ToActualName (this string s)
4142
return ret.Length == 0 ? "manifest" : ret;
4243
}
4344

44-
public static bool? GetAsBoolOrNull (this XElement element, string attribute)
45-
{
46-
var value = element.Attribute (attribute)?.Value;
47-
48-
if (value is null)
49-
return null;
50-
51-
if (bool.TryParse (value, out var ret))
52-
return ret;
53-
54-
return null;
55-
}
56-
5745
public static bool GetAttributeBoolOrDefault (this XElement element, string attribute, bool defaultValue)
5846
{
5947
var value = element.Attribute (attribute)?.Value;
@@ -117,4 +105,38 @@ public static void WriteAutoGeneratedHeader (this CodeWriter sw)
117105
sw.WriteLine ();
118106
sw.WriteLine ("#nullable enable"); // Roslyn turns off NRT for generated files by default, re-enable it
119107
}
108+
109+
/// <summary>
110+
/// Returns the first subset of a delimited string. ("127.0.0.1" -> "127")
111+
/// </summary>
112+
[return: NotNullIfNotNull (nameof (s))]
113+
public static string? FirstSubset (this string? s, char separator)
114+
{
115+
if (!s.HasValue ())
116+
return s;
117+
118+
var index = s.IndexOf (separator);
119+
120+
if (index < 0)
121+
return s;
122+
123+
return s.Substring (0, index);
124+
}
125+
126+
/// <summary>
127+
/// Returns the final subset of a delimited string. ("127.0.0.1" -> "1")
128+
/// </summary>
129+
[return: NotNullIfNotNull (nameof (s))]
130+
public static string? LastSubset (this string? s, char separator)
131+
{
132+
if (!s.HasValue ())
133+
return s;
134+
135+
var index = s.LastIndexOf (separator);
136+
137+
if (index < 0)
138+
return s;
139+
140+
return s.Substring (index + 1);
141+
}
120142
}

build-tools/manifest-attribute-codegen/Models/AttributeDefinition.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ public AttributeDefinition (string apiLevel, string name, string format)
1717
Format = format;
1818
}
1919

20+
public string GetAttributeType ()
21+
{
22+
return Format switch {
23+
"boolean" => "bool",
24+
"integer" => "int",
25+
"string" => "string?",
26+
_ => "string?",
27+
};
28+
}
29+
2030
public static AttributeDefinition FromElement (string api, XElement e)
2131
{
2232
var name = e.GetAttributeStringOrEmpty ("name");

build-tools/manifest-attribute-codegen/Models/MetadataSource.cs

Lines changed: 75 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,16 @@ namespace Xamarin.Android.Tools.ManifestAttributeCodeGenerator;
55

66
class MetadataSource
77
{
8-
public Dictionary<string, MetadataType> Types { get; } = new ();
9-
public Dictionary<string, MetadataElement> Elements { get; } = new ();
10-
11-
static readonly MetadataElement default_element = new MetadataElement ("*");
12-
8+
public Dictionary<string, MetadataType> Types { get; } = [];
9+
public Dictionary<string, MetadataAttribute> Elements { get; } = [];
1310

1411
public MetadataSource (string filename)
1512
{
1613
var xml = XElement.Load (filename);
1714

1815
foreach (var element in xml.Elements ("element")) {
19-
var path = element.Attribute ("path")?.Value ?? throw new InvalidDataException ("Missing 'path' attribute.");
20-
21-
Elements.Add (path, new MetadataElement (path) {
22-
Visible = element.GetAsBoolOrNull ("visible"),
23-
Type = element.Attribute ("type")?.Value,
24-
Name = element.Attribute ("name")?.Value,
25-
Obsolete = element.Attribute ("obsolete")?.Value,
26-
ReadOnly = element.GetAsBoolOrNull ("readonly") ?? false,
27-
});
16+
var me = new MetadataAttribute (element);
17+
Elements.Add (me.Path, me);
2818
}
2919

3020
foreach (var element in xml.Elements ("type")) {
@@ -33,83 +23,115 @@ public MetadataSource (string filename)
3323
}
3424
}
3525

36-
public MetadataElement GetMetadata (string path)
26+
public MetadataAttribute GetMetadata (string path)
3727
{
38-
if (Elements.TryGetValue (path, out var element)) {
39-
element.Consumed = true;
28+
if (Elements.TryGetValue (path, out var element))
4029
return element;
41-
}
4230

43-
return default_element;
31+
throw new InvalidOperationException ($"No MetadataElement found for path '{path}'.");
4432
}
4533

46-
public void EnsureMetadataElementsConsumed ()
34+
public void EnsureAllElementsAccountedFor (List<ElementDefinition> elements)
4735
{
48-
var unconsumed = Elements.Values.Where (e => !e.Consumed).ToList ();
49-
50-
if (unconsumed.Count == 0)
51-
return;
36+
var missing = new List<string> ();
5237

53-
var sb = new StringBuilder ();
54-
sb.AppendLine ("The following metadata elements were not consumed:");
38+
foreach (var e in elements) {
39+
if (!Types.TryGetValue (e.ActualElementName, out var t)) {
40+
missing.Add ($"- Type: <{e.ActualElementName}>");
41+
continue;
42+
}
5543

56-
foreach (var e in unconsumed)
57-
sb.AppendLine ($"- {e.Path}");
44+
if (t.Ignore)
45+
continue;
5846

59-
throw new InvalidOperationException (sb.ToString ());
60-
}
47+
foreach (var a in e.Attributes) {
48+
var name = $"{e.ActualElementName}.{a.Name}";
6149

62-
public void EnsureMetadataTypesConsumed ()
63-
{
64-
var unconsumed = Types.Values.Where (t => !t.Consumed && !t.Ignore).ToList ();
50+
if (!Elements.TryGetValue (name, out _))
51+
missing.Add ($"- Element: {name}");
52+
}
53+
}
6554

66-
if (unconsumed.Count == 0)
55+
if (missing.Count == 0)
6756
return;
6857

6958
var sb = new StringBuilder ();
70-
sb.AppendLine ("The following metadata types were not consumed:");
59+
sb.AppendLine ("The following manifest elements are not specified in the metadata:");
7160

72-
foreach (var t in unconsumed)
73-
sb.AppendLine ($"- {t.Name}");
61+
foreach (var m in missing)
62+
sb.AppendLine (m);
7463

7564
throw new InvalidOperationException (sb.ToString ());
7665
}
7766

78-
public void EnsureAllTypesAccountedFor (IEnumerable<ElementDefinition> elements)
67+
public void EnsureAllMetadataElementsExistInManifest (List<ElementDefinition> elements)
7968
{
8069
var missing = new List<string> ();
8170

82-
foreach (var e in elements) {
83-
if (!Types.ContainsKey (e.ActualElementName))
84-
missing.Add (e.ActualElementName);
71+
foreach (var type in Types) {
72+
var type_def = elements.FirstOrDefault (e => e.ActualElementName == type.Key);
73+
74+
if (type_def is null) {
75+
missing.Add ($"- Type: {type.Key}");
76+
continue;
77+
}
78+
}
79+
80+
foreach (var type in Elements) {
81+
var type_name = type.Key.FirstSubset ('.');
82+
var elem_name = type.Key.LastSubset ('.');
83+
84+
var type_def = elements.FirstOrDefault (e => e.ActualElementName == type_name);
85+
86+
if (type_def is null) {
87+
missing.Add ($"- Element: {type.Key}");
88+
continue;
89+
}
90+
91+
var elem_def = type_def.Attributes.FirstOrDefault (e => e.Name == elem_name);
92+
93+
if (elem_def is null) {
94+
missing.Add ($"- Element: {type.Key}");
95+
continue;
96+
}
8597
}
8698

8799
if (missing.Count == 0)
88100
return;
89101

90102
var sb = new StringBuilder ();
91-
sb.AppendLine ("The following types were not accounted for:");
103+
sb.AppendLine ("The following elements specified in the metadata were not found in the manifest:");
92104

93-
foreach (var m in missing.Order ())
94-
sb.AppendLine ($"- {m}");
105+
foreach (var e in missing)
106+
sb.AppendLine (e);
95107

96108
throw new InvalidOperationException (sb.ToString ());
97109
}
98110
}
99111

100-
class MetadataElement
112+
class MetadataAttribute
101113
{
102114
public string Path { get; set; }
103-
public bool? Visible { get; set; }
115+
public bool Visible { get; set; } = true;
104116
public string? Type { get; set; }
105117
public string? Name { get; set; }
106118
public string? Obsolete { get; set; }
107119
public bool ReadOnly { get; set; }
108-
public bool Consumed { get; set; }
120+
public bool ManualMap { get; set; }
109121

110-
public MetadataElement (string path)
122+
public MetadataAttribute (XElement element)
111123
{
112-
Path = path;
124+
Path = element.Attribute ("path")?.Value ?? throw new InvalidDataException ("Missing 'path' attribute.");
125+
126+
if (!Path.Contains ('.'))
127+
throw new InvalidDataException ($"Invalid 'path' attribute value: {Path}");
128+
129+
Visible = element.GetAttributeBoolOrDefault ("visible", true);
130+
Type = element.Attribute ("type")?.Value;
131+
Name = element.Attribute ("name")?.Value;
132+
Obsolete = element.Attribute ("obsolete")?.Value;
133+
ReadOnly = element.GetAttributeBoolOrDefault ("readonly", false);
134+
ManualMap = element.GetAttributeBoolOrDefault ("manualMap", false);
113135
}
114136
}
115137

@@ -125,8 +147,7 @@ public class MetadataType
125147
public bool IsJniNameProvider { get; set; }
126148
public bool HasDefaultConstructor { get; set; }
127149
public bool IsSealed { get; set; }
128-
public bool Consumed { get; set; }
129-
150+
public bool GenerateMapping { get; set; }
130151

131152
public MetadataType (XElement element)
132153
{
@@ -141,8 +162,9 @@ public MetadataType (XElement element)
141162
Usage = element.GetRequiredAttributeString ("usage");
142163
AllowMultiple = element.GetAttributeBoolOrDefault ("allowMultiple", false);
143164
IsJniNameProvider = element.GetAttributeBoolOrDefault ("jniNameProvider", false);
144-
HasDefaultConstructor = element.GetAttributeBoolOrDefault("defaultConstructor", true);
165+
HasDefaultConstructor = element.GetAttributeBoolOrDefault ("defaultConstructor", true);
145166
IsSealed = element.GetAttributeBoolOrDefault ("sealed", true);
146-
ManagedName = element.Attribute ("managedName")?.Value ?? Name.Unhyphenate ().Capitalize () + "Attribute";
167+
ManagedName = element.Attribute ("managedName")?.Value ?? Name.Unhyphenate ().Capitalize () + "Attribute";
168+
GenerateMapping = element.GetAttributeBoolOrDefault ("generateMapping", true);
147169
}
148170
}

build-tools/manifest-attribute-codegen/Program.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ public static int Main (string [] args)
7474
// Read metadata file
7575
var metadata = new MetadataSource (metadata_file);
7676

77+
// Ensure everything in the Android SDK is accounted for.
78+
// This forces us to handle anything new that's been added to the SDK.
79+
metadata.EnsureAllElementsAccountedFor (merged.Elements);
80+
81+
// Ensure there are no unused elements in the metadata file
82+
metadata.EnsureAllMetadataElementsExistInManifest (merged.Elements);
83+
7784
// Generate manifest attributes C# code
7885
foreach (var type in metadata.Types.Values.Where (t => !t.Ignore)) {
7986
using var w = new StreamWriter (Path.Combine (base_dir, type.OutputFile));
@@ -83,13 +90,6 @@ public static int Main (string [] args)
8390
writer.Write (cw);
8491
}
8592

86-
// Ensure everything we found in the Android SDK is accounted for.
87-
// This forces us to handle anything new that's been added to the SDK.
88-
// metadata.EnsureAllTypesExist (merged.Elements);
89-
metadata.EnsureAllTypesAccountedFor (merged.Elements);
90-
metadata.EnsureMetadataTypesConsumed ();
91-
metadata.EnsureMetadataElementsConsumed ();
92-
9393
return 0;
9494
}
9595
}

0 commit comments

Comments
 (0)