Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
jonpryor opened this issue Jun 4, 2021 · 0 comments
Open

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

jonpryor opened this issue Jun 4, 2021 · 0 comments
Assignees

Comments

@jonpryor
Copy link
Member

jonpryor commented Jun 4, 2021

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.

@jonpryor jonpryor added the needs-triage Issues that need to be assigned. label Jun 4, 2021
grendello added a commit to grendello/xamarin-android that referenced this issue Jul 14, 2021
Context: dotnet#5996
Context: dotnet#5964
Context: dotnet#5964 (comment)

The `NdkUtils` class used by Xamarin.Andrid.Build.Tasks to find tooling
shipped with the Android NDK, has grown increasingly complicated over
the years due to a number of incompatibilities between various versions
of the NDK. The code became hard to follow and untidy. This commit
attempts to address the issue by replacing the single static `NdkUtils`
class with a hierarchy of dynamically instantiated classes rooted in a
new base class, `NdkTools`.

`NdkUtils` had to be initialized for each thread that needed to access
its methods, which led to various issues with concurrency and lack of
proper initialization since the initialization had to be done wherever
`NdkUtils` was first accessed, meaning that any task using it had to do
it.

`NdkTools` doesn't require such initialization, instead it provides a
factory method called `Create` which takes path to the NDK as its
parameter and returns an instance of `NdkTools` child class (or `null`
if an error occurs) which the can be safely used by the caller.  Callers
need not concern themselves with what is the actual type of the returned
instance, they access only methods and properties defined in the
`NdkTools` base abstract class.

The hierarchy of `NdkTools` derivatives is structured and named after
the breaking changes in the NDK.  For instance, NDK versions before 16
used the GNU compilers, while release 16 and above use the clang
compilers - this is reflected in existence of two classes derived from
`NdkTools`, `NoClang` for NDKs older than r16 and `WithClang` for the
newer ones. The other breaking changes are the addition of unified
headers in r19, removal of the `platforms` directory in r22 and removal
of GNU Binutils in r23.

NDK r23 is recognized in this commit but it is NOT supported. Support
for r23 is being worked on in PR dotnet#6073 which will be merged once r23 is
out of beta.
grendello added a commit to grendello/xamarin-android that referenced this issue Jul 14, 2021
Context: dotnet#5996
Context: dotnet#5964
Context: dotnet#5964 (comment)

The `NdkUtils` class used by Xamarin.Andrid.Build.Tasks to find tooling
shipped with the Android NDK, has grown increasingly complicated over
the years due to a number of incompatibilities between various versions
of the NDK. The code became hard to follow and untidy. This commit
attempts to address the issue by replacing the single static `NdkUtils`
class with a hierarchy of dynamically instantiated classes rooted in a
new base class, `NdkTools`.

`NdkUtils` had to be initialized for each thread that needed to access
its methods, which led to various issues with concurrency and lack of
proper initialization since the initialization had to be done wherever
`NdkUtils` was first accessed, meaning that any task using it had to do
it.

`NdkTools` doesn't require such initialization, instead it provides a
factory method called `Create` which takes path to the NDK as its
parameter and returns an instance of `NdkTools` child class (or `null`
if an error occurs) which the can be safely used by the caller.  Callers
need not concern themselves with what is the actual type of the returned
instance, they access only methods and properties defined in the
`NdkTools` base abstract class.

The hierarchy of `NdkTools` derivatives is structured and named after
the breaking changes in the NDK.  For instance, NDK versions before 16
used the GNU compilers, while release 16 and above use the clang
compilers - this is reflected in existence of two classes derived from
`NdkTools`, `NoClang` for NDKs older than r16 and `WithClang` for the
newer ones. The other breaking changes are the addition of unified
headers in r19, removal of the `platforms` directory in r22 and removal
of GNU Binutils in r23.

NDK r23 is recognized in this commit but it is NOT supported. Support
for r23 is being worked on in PR dotnet#6073 which will be merged once r23 is
out of beta.
grendello added a commit to grendello/xamarin-android that referenced this issue Jul 14, 2021
Context: dotnet#5996
Context: dotnet#5964
Context: dotnet#5964 (comment)

The `NdkUtils` class used by Xamarin.Andrid.Build.Tasks to find tooling
shipped with the Android NDK, has grown increasingly complicated over
the years due to a number of incompatibilities between various versions
of the NDK. The code became hard to follow and untidy. This commit
attempts to address the issue by replacing the single static `NdkUtils`
class with a hierarchy of dynamically instantiated classes rooted in a
new base class, `NdkTools`.

`NdkUtils` had to be initialized for each thread that needed to access
its methods, which led to various issues with concurrency and lack of
proper initialization since the initialization had to be done wherever
`NdkUtils` was first accessed, meaning that any task using it had to do
it.

`NdkTools` doesn't require such initialization, instead it provides a
factory method called `Create` which takes path to the NDK as its
parameter and returns an instance of `NdkTools` child class (or `null`
if an error occurs) which the can be safely used by the caller.  Callers
need not concern themselves with what is the actual type of the returned
instance, they access only methods and properties defined in the
`NdkTools` base abstract class.

The hierarchy of `NdkTools` derivatives is structured and named after
the breaking changes in the NDK.  For instance, NDK versions before 16
used the GNU compilers, while release 16 and above use the clang
compilers - this is reflected in existence of two classes derived from
`NdkTools`, `NoClang` for NDKs older than r16 and `WithClang` for the
newer ones. The other breaking changes are the addition of unified
headers in r19, removal of the `platforms` directory in r22 and removal
of GNU Binutils in r23.

NDK r23 is recognized in this commit but it is NOT supported. Support
for r23 is being worked on in PR dotnet#6073 which will be merged once r23 is
out of beta.
grendello added a commit to grendello/xamarin-android that referenced this issue Jul 14, 2021
Context: dotnet#5996
Context: dotnet#5964
Context: dotnet#5964 (comment)

The `NdkUtils` class used by Xamarin.Andrid.Build.Tasks to find tooling
shipped with the Android NDK, has grown increasingly complicated over
the years due to a number of incompatibilities between various versions
of the NDK. The code became hard to follow and untidy. This commit
attempts to address the issue by replacing the single static `NdkUtils`
class with a hierarchy of dynamically instantiated classes rooted in a
new base class, `NdkTools`.

`NdkUtils` had to be initialized for each thread that needed to access
its methods, which led to various issues with concurrency and lack of
proper initialization since the initialization had to be done wherever
`NdkUtils` was first accessed, meaning that any task using it had to do
it.

`NdkTools` doesn't require such initialization, instead it provides a
factory method called `Create` which takes path to the NDK as its
parameter and returns an instance of `NdkTools` child class (or `null`
if an error occurs) which the can be safely used by the caller.  Callers
need not concern themselves with what is the actual type of the returned
instance, they access only methods and properties defined in the
`NdkTools` base abstract class.

The hierarchy of `NdkTools` derivatives is structured and named after
the breaking changes in the NDK.  For instance, NDK versions before 16
used the GNU compilers, while release 16 and above use the clang
compilers - this is reflected in existence of two classes derived from
`NdkTools`, `NoClang` for NDKs older than r16 and `WithClang` for the
newer ones. The other breaking changes are the addition of unified
headers in r19, removal of the `platforms` directory in r22 and removal
of GNU Binutils in r23.

NDK r23 is recognized in this commit but it is NOT supported. Support
for r23 is being worked on in PR dotnet#6073 which will be merged once r23 is
out of beta.
jonpryor pushed a commit that referenced this issue Jul 15, 2021
Context: #5964
Context: #5964 (comment)
Context: #5996
Context: #6073

The `NdkUtils` class is used by Xamarin.Andrid.Build.Tasks to find
tooling shipped with the Android NDK, and has grown increasingly
complicated over the years due to a number of incompatibilities
between various NDK versions. The code became hard to follow and
untidy.

Address this issue by replacing the single static `NdkUtils` class
with a hierarchy of dynamically instantiated classes rooted in a new
base class, `NdkTools`.

`NdkUtils` had to be initialized for each thread that needed to access
its methods, which led to various issues with concurrency and lack of
proper initialization since the initialization had to be done wherever
`NdkUtils` was first accessed, meaning that every task using it had to
do it; see accc846 & [this comment][0].

`NdkTools` doesn't require such initialization, instead it provides an
`NdkTools.Create()` factory method takes the path to the NDK and
returns an instance of an `NdkTools` subclass (or `null` if an error
occurs), which the can be safely used by the caller.  Callers need not
concern themselves with what is the actual type of the returned
instance, they access only methods and properties defined in the
`NdkTools` base abstract class.

The hierarchy of `NdkTools` derivatives is structured and named after
the breaking changes in the NDK.  For instance, NDK versions before 16
used the GNU compilers, while release 16 and above use the clang
compilers - this is reflected in existence of two classes derived from
`NdkTools`, `NoClang` for NDKs older than r16 and `WithClang` for the
newer ones.  The other breaking changes are the addition of unified
headers in r19, removal of the `platforms` directory in r22 and removal
of GNU Binutils in r23.

NDK r23 is recognized in this commit but it is NOT supported.
Support for r23 is being worked on in PR #6073, which will be merged
once r23 is out of beta.

[0]: #5964 (comment)
@jpobst jpobst removed the needs-triage Issues that need to be assigned. label Aug 5, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants