Skip to content

Audit static field usage in Xamarin.Android.Build.Tasks #5996

Open
@jonpryor

Description

@jonpryor

Related: #5964 (comment)

Question: How "safe" is it to have mutable (non-const, non-readonly) static fields in Xamarin.Android.Build.Tasks.dll which are used by MSBuild tasks?

The primary cause of this question is from investigating Issue #5964, in which the (thread-static!) mutable static field NdkUtil.usingClangNDK was inconsistently initialized, resulting in "not entirely obvious" runtime behavior.

…and it occurs to me that, in the context of MSBuild, I'm not sure how safe it is to have mutable static fields in the first place! Continuing with NdkUtil.usingClangNDK, it's set based on the NDK version, as determined by $(AndroidNdkDirectory)/etc. For a given build, this won't change, but within the IDE, it very well could change, e.g. by the customer changing the Android SDK directory.

It "feels" very "dubious" to have potentially changeable information stored in static fields.

The "obvious" replacement? Instance fields in new classes which can be cached in e.g. IBuildEngine4.GetRegisteredTaskObject(), which -- to my mind, anyway -- allows understanding of the relation of the data to the overall build process.

There are (currently) 27 fields that are static mutable fields:

% git grep '\<static\>.*;' src/Xamarin.Android.Build.Tasks | grep -v readonly | grep -v extern | grep -v Tests/
src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/AndroidLinkConfiguration.cs:		static ConditionalWeakTable<LinkContext, AndroidLinkConfiguration> configurations = new ConditionalWeakTable<LinkContext, AndroidLinkConfiguration> ();
src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/ApplyPreserveAttribute.cs:		static List<ICustomAttributeProvider> srs_data_contract = new List<ICustomAttributeProvider> ();
src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/ApplyPreserveAttribute.cs:		static List<ICustomAttributeProvider> xml_serialization = new List<ICustomAttributeProvider> ();
src/Xamarin.Android.Build.Tasks/Tasks/Aapt2Link.cs:		static Regex exraArgSplitRegEx = new Regex (@"[\""].+?[\""]|[\''].+?[\'']|[^ ]+", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline);
src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs:				pkgmgr.WriteLine ("\tpublic static String[] Assemblies = new String[]{");
src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs:				pkgmgr.WriteLine ("\tpublic static String[] Dependencies = new String[]{");
src/Xamarin.Android.Build.Tasks/Tasks/NdkUtils.cs:		static bool usingClangNDK;
src/Xamarin.Android.Build.Tasks/Tasks/NdkUtils.cs:		public static bool UsingClangNDK => usingClangNDK;
src/Xamarin.Android.Build.Tasks/Tasks/RemoveUnknownFiles.cs:		static bool IsWindows = Path.DirectorySeparatorChar == '\\';
src/Xamarin.Android.Build.Tasks/Utilities/Aapt2Daemon.cs:		internal static object RegisterTaskObjectKey => TypeFullName;
src/Xamarin.Android.Build.Tasks/Utilities/JavaResourceParser.cs:		//     public static final int field = 0xZZ;
src/Xamarin.Android.Build.Tasks/Utilities/JavaResourceParser.cs:			Parse (@"^        public static final int ([^ =]+)\s*=\s*([^;]+);$",
src/Xamarin.Android.Build.Tasks/Utilities/ManagedResourceParser.cs:		static CompareTuple compareTuple = new CompareTuple ();
src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs:		public static XNamespace AndroidXmlNamespace = "http://schemas.android.com/apk/res/android";
src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs:		public static XNamespace AndroidXmlToolsNamespace = "http://schemas.android.com/tools";
src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs:		static XNamespace androidNs = AndroidXmlNamespace;
src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs:		static XNamespace androidToolsNs = AndroidXmlToolsNamespace;
src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.Linker.cs:		public static string [] TargetFrameworkDirectories;
src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs:		static Lazy<string> uname = new Lazy<string> (GetOSBinDirName, System.Threading.LazyThreadSafetyMode.PublicationOnly);
src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs:		public static AndroidVersions   SupportedVersions;
src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs:		public static AndroidSdkInfo    AndroidSdk;
src/Xamarin.Android.Build.Tasks/Utilities/ResourceIdentifier.cs:		static Regex validIdentifier = new Regex ($"[^{Identifier}]", RegexOptions.Compiled);
src/Xamarin.Android.Build.Tasks/Utilities/ZipArchiveEx.cs:		public static int ZipFlushSizeLimit = 50 * 1024 * 1024;
src/Xamarin.Android.Build.Tasks/Utilities/ZipArchiveEx.cs:		public static int ZipFlushFilesLimit = 50;

Note that many of these don't need to be mutable! ZipFlushSizeLimit should be const, validIdentifier and AndroidXmlNamespace should be readonly, etc.

MonoAndroidHelper.SupportedVersions and MonoAndroidHelper.AndroidSdk appear to be "obvious" candidates for a more "Task-oriented" IBuildEngine4.GetRegisteredTaskObject() caching strategy, as well as NdkUtil in general.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions