Skip to content

Commit

Permalink
Generate mappings used by Xamarin.Android.Build.Tasks.
Browse files Browse the repository at this point in the history
  • Loading branch information
jpobst committed Mar 7, 2024
1 parent bde026f commit e8f8cf7
Show file tree
Hide file tree
Showing 43 changed files with 1,421 additions and 1,089 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,6 @@ public static string ToActualName (this string s)
return ret.Length == 0 ? "manifest" : ret;
}

public static bool? GetAsBoolOrNull (this XElement element, string attribute)
{
var value = element.Attribute (attribute)?.Value;

if (value is null)
return null;

if (bool.TryParse (value, out var ret))
return ret;

return null;
}

public static bool GetAttributeBoolOrDefault (this XElement element, string attribute, bool defaultValue)
{
var value = element.Attribute (attribute)?.Value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ public AttributeDefinition (string apiLevel, string name, string format)
Format = format;
}

public string GetAttributeType ()
{
return Format switch {
"boolean" => "bool",
"integer" => "int",
"string" => "string?",
_ => "string?",
};
}

public static AttributeDefinition FromElement (string api, XElement e)
{
var name = e.GetAttributeStringOrEmpty ("name");
Expand Down
13 changes: 8 additions & 5 deletions build-tools/manifest-attribute-codegen/Models/MetadataSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ public MetadataSource (string filename)
var path = element.Attribute ("path")?.Value ?? throw new InvalidDataException ("Missing 'path' attribute.");

Elements.Add (path, new MetadataElement (path) {
Visible = element.GetAsBoolOrNull ("visible"),
Visible = element.GetAttributeBoolOrDefault ("visible", true),
Type = element.Attribute ("type")?.Value,
Name = element.Attribute ("name")?.Value,
Obsolete = element.Attribute ("obsolete")?.Value,
ReadOnly = element.GetAsBoolOrNull ("readonly") ?? false,
ReadOnly = element.GetAttributeBoolOrDefault("readonly", false),
ManualMap = element.GetAttributeBoolOrDefault ("manualMap", false),
});
}

Expand Down Expand Up @@ -100,11 +101,12 @@ public void EnsureAllTypesAccountedFor (IEnumerable<ElementDefinition> elements)
class MetadataElement
{
public string Path { get; set; }
public bool? Visible { get; set; }
public bool Visible { get; set; } = true;
public string? Type { get; set; }
public string? Name { get; set; }
public string? Obsolete { get; set; }
public bool ReadOnly { get; set; }
public bool ManualMap { get; set; }
public bool Consumed { get; set; }

public MetadataElement (string path)
Expand All @@ -126,7 +128,7 @@ public class MetadataType
public bool HasDefaultConstructor { get; set; }
public bool IsSealed { get; set; }
public bool Consumed { get; set; }

public bool GenerateMapping { get; set; }

public MetadataType (XElement element)
{
Expand All @@ -143,6 +145,7 @@ public MetadataType (XElement element)
IsJniNameProvider = element.GetAttributeBoolOrDefault ("jniNameProvider", false);
HasDefaultConstructor = element.GetAttributeBoolOrDefault("defaultConstructor", true);
IsSealed = element.GetAttributeBoolOrDefault ("sealed", true);
ManagedName = element.Attribute ("managedName")?.Value ?? Name.Unhyphenate ().Capitalize () + "Attribute";
ManagedName = element.Attribute ("managedName")?.Value ?? Name.Unhyphenate ().Capitalize () + "Attribute";
GenerateMapping = element.GetAttributeBoolOrDefault ("generateMapping", true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ namespace Xamarin.Android.Tools.ManifestAttributeCodeGenerator;
// This is the common data class used by both Mono.Android and Xamarin.Android.Build.Tasks.
class AttributeDataClass : ClassWriter
{
AttributeMappingField? mapping_field;
AttributeMappingStaticConstructor? static_constructor;

static AttributeMappingManualInitializer manual_mapping_partial = AttributeMappingManualInitializer.Create ();

public string Namespace { get; set; }

public AttributeDataClass (string ns)
Expand Down Expand Up @@ -41,12 +46,12 @@ public static AttributeDataClass Create (ElementDefinition attr, MetadataSource
foreach (var a in attr.Attributes.OrderBy (a => a.Name)) {
var attr_metadata = metadata.GetMetadata ($"{attr.ActualElementName}.{a.Name}");

if (attr_metadata.Visible == false)
if (!attr_metadata.Visible)
continue;

var p = new PropertyWriter {
Name = (attr_metadata.Name ?? a.Name).Capitalize (),
PropertyType = new TypeReferenceWriter (attr_metadata.Type ?? GetAttributeType (a)),
PropertyType = new TypeReferenceWriter (attr_metadata.Type ?? a.GetAttributeType ()),
IsPublic = true,
HasGet = true,
HasSet = true,
Expand All @@ -61,17 +66,13 @@ public static AttributeDataClass Create (ElementDefinition attr, MetadataSource
c.Properties.Add (p);
}

return c;
}
// Create mapping field used by Xamarin.Android.Build.Tasks
if (type.GenerateMapping) {
c.mapping_field = AttributeMappingField.Create (type);
c.static_constructor = AttributeMappingStaticConstructor.Create (attr, metadata, type);
}

static string GetAttributeType (AttributeDefinition attr)
{
return attr.Format switch {
"boolean" => "bool",
"integer" => "int",
"string" => "string?",
_ => "string?",
};
return c;
}

public override void Write (CodeWriter writer)
Expand All @@ -86,4 +87,19 @@ public override void Write (CodeWriter writer)

base.Write (writer);
}

public override void WriteMembers (CodeWriter writer)
{
base.WriteMembers (writer);

if (mapping_field is not null) {
writer.WriteLineNoIndent ("#if XABT_MANIFEST_EXTENSIONS");
mapping_field?.Write (writer);
writer.WriteLine ();
static_constructor?.Write (writer);
writer.WriteLine ();
manual_mapping_partial?.Write (writer);
writer.WriteLineNoIndent ("#endif");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Xamarin.SourceWriter;

namespace Xamarin.Android.Tools.ManifestAttributeCodeGenerator;

class AttributeMappingField : FieldWriter
{
public static AttributeMappingField Create (MetadataType type)
{
var field = new AttributeMappingField {
Name = "mapping",
IsStatic = true,
Type = new TypeReferenceWriter ($"Xamarin.Android.Manifest.ManifestDocumentElement<{type.ManagedName}>"),
Value = $"new (\"{type.Name}\")",
};

return field;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Xamarin.SourceWriter;

namespace Xamarin.Android.Tools.ManifestAttributeCodeGenerator;

class AttributeMappingManualInitializer : MethodWriter
{
public static AttributeMappingManualInitializer Create ()
{
var method = new AttributeMappingManualInitializer {
Name = "AddManualMapping",
IsStatic = true,
IsPartial = true,
IsDeclaration = true,
ReturnType = TypeReferenceWriter.Void,
};

return method;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Xamarin.SourceWriter;

namespace Xamarin.Android.Tools.ManifestAttributeCodeGenerator;

class AttributeMappingStaticConstructor : ConstructorWriter
{
public static AttributeMappingStaticConstructor Create (ElementDefinition attr, MetadataSource metadata, MetadataType type)
{
var ctor = new AttributeMappingStaticConstructor {
Name = type.ManagedName,
IsStatic = true,
};

// mapping.Add (
// member: "Name",
// attributeName: "name",
// getter: self => self.Name,
// setter: (self, value) => self.Name = (string?) value
// );
foreach (var a in attr.Attributes.OrderBy (a => a.Name)) {
var attr_metadata = metadata.GetMetadata ($"{attr.ActualElementName}.{a.Name}");

if (!attr_metadata.Visible || attr_metadata.ManualMap)
continue;

var name = (attr_metadata.Name ?? a.Name).Capitalize ();
var setter = $"(self, value) => self.{name} = ({attr_metadata.Type ?? a.GetAttributeType ()}) value";

if (attr_metadata.ReadOnly)
setter = "null";

ctor.Body.Add ($"mapping.Add (");
ctor.Body.Add ($" member: \"{name}\",");
ctor.Body.Add ($" attributeName: \"{a.Name}\",");
ctor.Body.Add ($" getter: self => self.{name},");
ctor.Body.Add ($" setter: {setter}");
ctor.Body.Add ($");");
}

ctor.Body.Add (string.Empty);
ctor.Body.Add ($"AddManualMapping ();");

return ctor;
}
}
28 changes: 17 additions & 11 deletions build-tools/manifest-attribute-codegen/metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,20 @@
<type name="application" namespace="Android.App" outputFile="src\Xamarin.Android.NamingCustomAttributes\Android.App\ApplicationAttribute.cs" usage="AttributeTargets.Assembly | AttributeTargets.Class" jniNameProvider="true" />
<type name="instrumentation" namespace="Android.App" outputFile="src\Xamarin.Android.NamingCustomAttributes\Android.App\InstrumentationAttribute.cs" usage="AttributeTargets.Assembly | AttributeTargets.Class" allowMultiple="true" jniNameProvider="true" />
<type name="layout" namespace="Android.App" outputFile="src\Xamarin.Android.NamingCustomAttributes\Android.App\LayoutAttribute.cs" usage="AttributeTargets.Class" />
<type name="service" namespace="Android.App" outputFile="src\Xamarin.Android.NamingCustomAttributes\Android.App\ServiceAttribute.cs" usage="AttributeTargets.Class" jniNameProvider="true" />
<type name="receiver" managedName="BroadcastReceiverAttribute" namespace="Android.Content" outputFile="src\Xamarin.Android.NamingCustomAttributes\Android.Content\BroadcastReceiverAttribute.cs" usage="AttributeTargets.Class" jniNameProvider="true" sealed="false" />
<type name="provider" managedName="ContentProviderAttribute" namespace="Android.Content" outputFile="src\Xamarin.Android.NamingCustomAttributes\Android.Content\ContentProviderAttribute.cs" usage="AttributeTargets.Class" jniNameProvider="true" defaultConstructor="false" sealed="false" />
<type name="receiver" managedName="BroadcastReceiverAttribute" namespace="Android.Content" outputFile="src\Xamarin.Android.NamingCustomAttributes\Android.Content\BroadcastReceiverAttribute.cs" usage="AttributeTargets.Class" jniNameProvider="true" sealed="false" />
<type name="service" namespace="Android.App" outputFile="src\Xamarin.Android.NamingCustomAttributes\Android.App\ServiceAttribute.cs" usage="AttributeTargets.Class" jniNameProvider="true" />

<type name="intent-filter" namespace="Android.App" outputFile="src\Mono.Android\Android.App\IntentFilterAttribute.cs" usage="AttributeTargets.Class" allowMultiple="true" defaultConstructor="false" />
<type name="meta-data" namespace="Android.App" outputFile="src\Mono.Android\Android.App\MetaDataAttribute.cs" usage="AttributeTargets.Assembly | AttributeTargets.Class" allowMultiple="true" defaultConstructor="false" />
<type name="grant-uri-permission" namespace="Android.Content" outputFile="src\Mono.Android\Android.Content\GrantUriPermissionAttribute.cs" usage="AttributeTargets.Class" allowMultiple="true" sealed="false" />
<type name="uses-library" namespace="Android.App" outputFile="src\Mono.Android\Android.App\UsesLibraryAttribute.cs" usage="AttributeTargets.Assembly | AttributeTargets.Class" allowMultiple="true" />
<type name="intent-filter" namespace="Android.App" outputFile="src\Mono.Android\Android.App\IntentFilterAttribute.cs" usage="AttributeTargets.Class" allowMultiple="true" defaultConstructor="false" generateMapping="false" />
<type name="meta-data" namespace="Android.App" outputFile="src\Mono.Android\Android.App\MetaDataAttribute.cs" usage="AttributeTargets.Assembly | AttributeTargets.Class" allowMultiple="true" defaultConstructor="false" />
<type name="permission" namespace="Android.App" outputFile="src\Mono.Android\Android.App\PermissionAttribute.cs" usage="AttributeTargets.Assembly" allowMultiple="true" />
<type name="permission-group" namespace="Android.App" outputFile="src\Mono.Android\Android.App\PermissionGroupAttribute.cs" usage="AttributeTargets.Assembly" allowMultiple="true" />
<type name="permission-tree" namespace="Android.App" outputFile="src\Mono.Android\Android.App\PermissionTreeAttribute.cs" usage="AttributeTargets.Assembly" />
<type name="uses-configuration" namespace="Android.App" outputFile="src\Mono.Android\Android.App\UsesConfigurationAttribute.cs" usage="AttributeTargets.Assembly" allowMultiple="true" />
<type name="uses-feature" namespace="Android.App" outputFile="src\Mono.Android\Android.App\UsesFeatureAttribute.cs" usage="AttributeTargets.Assembly" allowMultiple="true" />
<type name="uses-library" namespace="Android.App" outputFile="src\Mono.Android\Android.App\UsesLibraryAttribute.cs" usage="AttributeTargets.Assembly | AttributeTargets.Class" allowMultiple="true" />
<type name="uses-permission" namespace="Android.App" outputFile="src\Mono.Android\Android.App\UsesPermissionAttribute.cs" usage="AttributeTargets.Assembly" allowMultiple="true" />

<!-- Manifest elements we are ignoring -->
<type name="action" ignore="true" />
Expand Down Expand Up @@ -66,7 +67,6 @@
<type name="upgrade-key-set" ignore="true" />
<type name="uses-native-library" ignore="true" />
<type name="uses-package" ignore="true" />
<type name="uses-permission" ignore="true" />
<type name="uses-sdk" ignore="true" />
<type name="uses-sdk-library" ignore="true" />
<type name="uses-split" ignore="true" />
Expand Down Expand Up @@ -102,7 +102,7 @@
<element path="activity.minAspectRatio" visible="false" />
<element path="activity.multiprocess" name="MultiProcess" type="bool" />
<element path="activity.noHistory" type="bool" />
<element path="activity.parentActivityName" name="ParentActivity" type="Type?" />
<element path="activity.parentActivityName" name="ParentActivity" type="Type?" manualMap="true" />
<element path="activity.persistableMode" type="Android.Content.PM.ActivityPersistableMode" />
<element path="activity.playHomeTransitionSound" visible="false" />
<element path="activity.preferMinimalPostProcessing" visible="false" />
Expand Down Expand Up @@ -140,7 +140,7 @@
<element path="application.attributionsAreUserVisible" visible="false" />
<element path="application.autoRevokePermissions" visible="false" />
<element path="application.cantSaveState" visible="false" />
<element path="application.backupAgent" type="Type?" />
<element path="application.backupAgent" type="Type?" manualMap="true" />
<element path="application.backupInForeground" type="bool" />
<element path="application.classLoader" visible="false" />
<element path="application.crossProfile" visible="false" />
Expand All @@ -161,11 +161,12 @@
<element path="application.killAfterRestore" type="bool" />
<element path="application.knownActivityEmbeddingCerts" visible="false" />
<element path="application.localeConfig" visible="false" />
<element path="application.manageSpaceActivity" type="Type?" />
<element path="application.manageSpaceActivity" type="Type?" manualMap="true" />
<element path="application.maxAspectRatio" visible="false" />
<element path="application.memtagMode" visible="false" />
<element path="application.minAspectRatio" visible="false" />
<element path="application.multiArch" visible="false" />
<element path="application.name" manualMap="true" />
<element path="application.nativeHeapZeroInitialized" visible="false" />
<element path="application.neverEncrypt" visible="false" />
<element path="application.persistent" type="bool" />
Expand Down Expand Up @@ -239,7 +240,7 @@

<!-- <provider> -->
<element path="provider.attributionTags" visible="false" />
<element path="provider.authorities" type="string[]" readonly="true" />
<element path="provider.authorities" type="string[]" readonly="true" manualMap="true" />
<element path="provider.banner" visible="false" />
<element path="provider.description" visible="false" />
<element path="provider.directBootAware" type="bool" />
Expand Down Expand Up @@ -286,11 +287,16 @@
<element path="uses-configuration.reqHardKeyboard" type="bool" />

<!-- <uses-feature> -->
<element path="uses-feature.glEsVersion" name="GLESVersion" />
<element path="uses-feature.glEsVersion" name="GLESVersion" manualMap="true" />
<element path="uses-feature.name" readonly="true" />
<element path="uses-feature.version" readonly="true" />

<!-- <uses-library> -->
<element path="uses-library.required" type="bool" />

<!-- <uses-permission> -->
<element path="uses-permission.minSdkVersion" visible="false" />
<element path="uses-permission.requiredFeature" visible="false" />
<element path="uses-permission.requiredNotFeature" visible="false" />
<element path="uses-permission.usesPermissionFlags" visible="false" />
</metadata>
29 changes: 29 additions & 0 deletions src/Mono.Android/Android.App/MetaDataAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,33 @@ public sealed partial class MetaDataAttribute : Attribute {

public string? Value { get; set; }

#if XABT_MANIFEST_EXTENSIONS
static Xamarin.Android.Manifest.ManifestDocumentElement<MetaDataAttribute> mapping = new ("meta-data");

static MetaDataAttribute ()
{
mapping.Add (
member: "Name",
attributeName: "name",
getter: self => self.Name,
setter: null
);
mapping.Add (
member: "Resource",
attributeName: "resource",
getter: self => self.Resource,
setter: (self, value) => self.Resource = (string?) value
);
mapping.Add (
member: "Value",
attributeName: "value",
getter: self => self.Value,
setter: (self, value) => self.Value = (string?) value
);

AddManualMapping ();
}

static partial void AddManualMapping ();
#endif
}
Loading

0 comments on commit e8f8cf7

Please sign in to comment.