Description
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.