diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7317893 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,199 @@ +############################### +# Core EditorConfig Options # +############################### + +root = true + +# All files +[*] +charset = utf-8 +trim_trailing_whitespace = true + +# MSBuild +[*.{csproj,proj,projitems,shproj,fsproj,targets,props}] +indent_style = space +indent_size = 2 + +# XML config files +[*.{xml,axml,xaml,config,nuspec,resx}] +indent_style = space +indent_size = 2 + +# JSON files +[*.json] +indent_style = space +indent_size = 2 + +# F# files +[*.{fs, fsx, fsi}] +indent_style = space +indent_size = 4 + +# Code files +[*.{cs,csx,vb,vbx}] +insert_final_newline = true +indent_style = tab +tab_width = 8 +indent_size = 8 +max_line_length = 180 + +############################### +# .NET Coding Conventions # +############################### + +[*.{cs,vb}] +# Organize usings +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false + +# Avoid "this." and "Me." if not necessary +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +# Use language keywords instead of framework type names for type references +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Suggest more modern language features when available +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent + +# Avoid redundant accessibility modifiers when they're default +dotnet_style_require_accessibility_modifiers = omit_if_default:suggestion +dotnet_style_readonly_field = true:suggestion + +# Expression-level preferences +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent + +############################### +# Naming Conventions # +############################### + +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +dotnet_naming_style.underline_separator.word_separator = _ +dotnet_naming_style.underline_separator.capitalization = all_lower + +# Symbol Definitions +dotnet_naming_symbols.parameters.applicable_kinds = parameter +dotnet_naming_symbols.parameters.applicable_accessibilities = * + +dotnet_naming_symbols.fields.applicable_kinds = field + +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const + +# Use CamelCase for parameters +dotnet_naming_rule.method_parameters_should_be_camel_case.severity = suggestion +dotnet_naming_rule.method_parameters_should_be_camel_case.symbols = parameters +dotnet_naming_rule.method_parameters_should_be_camel_case.style = camel_case + +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style + +# Use underline separator for instance fields +dotnet_naming_rule.fields_should_be_underline_separator.severity = suggestion +dotnet_naming_rule.fields_should_be_underline_separator.symbols = fields +dotnet_naming_rule.fields_should_be_underline_separator.style = underline_separator + + +############################### +# C# Code Style Rules # +############################### + +[*.cs] +# var preferences +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent + +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent + +# Pattern-matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion + +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion + +# Expression-level preferences +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion + +############################### +# C# Formatting Rules # +############################### + +# Newline settings +csharp_new_line_before_open_brace = methods,types +csharp_new_line_before_else = false +csharp_new_line_before_catch = false +csharp_new_line_before_finally = false +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true + +# Indentation preferences +csharp_indent_switch_labels = false +csharp_indent_case_contents = true +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = true +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = true +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = true +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_before_open_square_brackets = true + +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true + +################################## +# Visual Basic Code Style Rules # +################################## + +[*.vb] +# Modifier preferences +visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..0377b39 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "ms-vscode.csharp", + "ms-vscode.mono-debug", + "wghats.vscode-nxunit-test-adapter", + "visualstudioexptteam.vscodeintellicode", + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..a0ddfb7 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,22 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch", + "type": "mono", + "request": "launch", + "program": "${workspaceRoot}/packages/NUnit.ConsoleRunner.3.9.0/tools/nunit3-console.exe ${workspaceRoot}/bin/TestDebug/generator-Tests.dll", + "cwd": "${workspaceRoot}bin/TestDebug/" + }, + { + "name": "Attach", + "type": "mono", + "request": "attach", + "address": "localhost", + "port": 55555 + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..bff3f6a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "nxunitExplorer.nunit": "packages/NUnit.ConsoleRunner.3.9.0/tools/nunit3-console.exe", + "nxunitExplorer.modules": [ + "bin/TestDebug/Xamarin.Android.Tools.AndroidSdk-Tests.dll" + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..87828d6 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,43 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Build Xamarin.Android.Tools", + "type": "shell", + "command": "msbuild Xamarin.Android.Tools.sln /restore", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$msCompile" + ] + }, + { + "label": "Clean Xamarin.Android.Tools", + "type": "shell", + "command": "msbuild Xamarin.Android.Tools.sln /restore /t:Clean", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$msCompile" + ] + }, + { + "label": "Run Unit Tests", + "type": "shell", + "command": "make run-all-tests", + "group": { + "kind": "test", + "isDefault": true + }, + "problemMatcher": [ + "$msCompile" + ] + } + ] +} diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..74fe6cf --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,25 @@ + + + + + Debug + + + obj\ + + + True + + + $(BaseIntermediateOutputPath)\$(Configuration)-$(TargetFramework.ToLowerInvariant()) + $(MSBuildThisFileDirectory)bin\Build$(Configuration)\$(TargetFramework.ToLowerInvariant())\ + $(MSBuildThisFileDirectory)bin\$(Configuration)\$(TargetFramework.ToLowerInvariant())\ + $(MSBuildThisFileDirectory)bin\Test$(Configuration)-$(TargetFramework.ToLowerInvariant())\ + + + $(BaseIntermediateOutputPath)\$(Configuration) + $(MSBuildThisFileDirectory)bin\Build$(Configuration)\ + $(MSBuildThisFileDirectory)bin\$(Configuration)\ + $(MSBuildThisFileDirectory)bin\Test$(Configuration)\ + + diff --git a/Makefile b/Makefile index 6cf6e73..1c686c4 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ V ?= 0 include build-tools/scripts/msbuild.mk all: - $(MSBUILD) $(MSBUILD_FLAGS) Xamarin.Android.Tools.sln + $(MSBUILD) /restore $(MSBUILD_FLAGS) Xamarin.Android.Tools.sln clean: -$(MSBUILD) $(MSBUILD_FLAGS) /t:Clean Xamarin.Android.Tools.sln diff --git a/NuGet.Config b/NuGet.Config new file mode 100644 index 0000000..9eee256 --- /dev/null +++ b/NuGet.Config @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/NuGet.config b/NuGet.config index a877b8c..713d9fc 100644 --- a/NuGet.config +++ b/NuGet.config @@ -1,4 +1,8 @@ + diff --git a/Xamarin.Android.Tools.code-workspace b/Xamarin.Android.Tools.code-workspace new file mode 100644 index 0000000..be71cd0 --- /dev/null +++ b/Xamarin.Android.Tools.code-workspace @@ -0,0 +1,9 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": { + } +} diff --git a/azure-pipelines.yaml b/azure-pipelines.yaml index 1db8f6c..59a3e7b 100644 --- a/azure-pipelines.yaml +++ b/azure-pipelines.yaml @@ -1,14 +1,32 @@ name: Xamarin.Android.Tools $(Rev:r) +trigger: + - master + - d16-* + +pr: + - master + - d16-* + +# Global variables +variables: + DotNetCoreVersion: 3.1.100 + HostedMac: Hosted Mac Internal + HostedWinVS2019: Hosted Windows 2019 with VS2019 + jobs: - job: windows displayName: windows - pool: Hosted VS2017 + pool: $(HostedWinVS2019) steps: - - task: NuGetToolInstaller@1 + - task: UseDotNet@2 + displayName: Use .NET Core $(DotNetCoreVersion) + inputs: + version: $(DotNetCoreVersion) + - task: NuGetToolInstaller@0 displayName: 'Install NuGet' inputs: - versionSpec: '5.4.x' + versionSpec: 5.x - task: NuGetCommand@2 displayName: 'NuGet Restore' inputs: @@ -38,25 +56,27 @@ jobs: pathtoPublish: $(Build.ArtifactStagingDirectory) - job: mac displayName: mac - pool: Hosted macOS + pool: $(HostedMac) steps: - - task: NuGetToolInstaller@1 - displayName: 'Install NuGet' - inputs: - versionSpec: '5.4.x' - - task: NuGetCommand@2 - displayName: 'NuGet Restore' + - task: UseDotNet@2 + displayName: Use .NET Core $(DotNetCoreVersion) inputs: - restoreSolution: Xamarin.Android.Tools.sln - restoreDirectory: 'packages' - - task: MSBuild@1 - displayName: 'Build solution Xamarin.Android.Tools.sln' - inputs: - solution: Xamarin.Android.Tools.sln - - script: mono packages/nunit.consolerunner/3.9.0/tools/nunit3-console.exe bin/TestDebug/Xamarin.Android.Tools.AndroidSdk-Tests.dll - displayName: Run Tests + version: $(DotNetCoreVersion) + - script: | + dotnet tool install --global boots + boots https://download.mono-project.com/archive/6.4.0/macos-10-universal/MonoFramework-MDK-6.4.0.198.macos10.xamarin.universal.pkg + displayName: Install Mono 6.4 + - script: make prepare CONFIGURATION=$(Build.Configuration) + displayName: make prepare + + - script: make all CONFIGURATION=$(Build.Configuration) + displayName: make all + + - script: make run-all-tests CONFIGURATION=$(Build.Configuration) + displayName: make run-all-tests + - task: PublishTestResults@2 condition: always() inputs: testResultsFormat: NUnit - testResultsFiles: TestResult.xml + testResultsFiles: TestResult*.xml diff --git a/src/Xamarin.Android.Tools.AndroidSdk/AndroidAppManifest.cs b/src/Xamarin.Android.Tools.AndroidSdk/AndroidAppManifest.cs index 2db516c..5a9e5a7 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/AndroidAppManifest.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/AndroidAppManifest.cs @@ -130,42 +130,42 @@ public void WriteToFile (string fileName) } } - static string NullIfEmpty (string value) + static string? NullIfEmpty (string? value) { return string.IsNullOrEmpty (value) ? null : value; } - public string PackageName { + public string? PackageName { get { return (string) manifest.Attribute ("package"); } set { manifest.SetAttributeValue ("package", NullIfEmpty (value)); } } - public string ApplicationLabel { + public string? ApplicationLabel { get { return (string) application.Attribute (aNS + "label"); } set { application.SetAttributeValue (aNS + "label", NullIfEmpty (value)); } } - public string ApplicationIcon { + public string? ApplicationIcon { get { return (string) application.Attribute (aNS + "icon"); } set { application.SetAttributeValue (aNS + "icon", NullIfEmpty (value)); } } - public string ApplicationTheme { + public string? ApplicationTheme { get { return (string) application.Attribute (aNS + "theme"); } set { application.SetAttributeValue (aNS + "theme", NullIfEmpty (value)); } } - public string VersionName { + public string? VersionName { get { return (string) manifest.Attribute (aNS + "versionName"); } set { manifest.SetAttributeValue (aNS + "versionName", NullIfEmpty (value)); } } - public string VersionCode { + public string? VersionCode { get { return (string) manifest.Attribute (aNS + "versionCode"); } set { manifest.SetAttributeValue (aNS + "versionCode", NullIfEmpty (value)); } } - public string InstallLocation { + public string? InstallLocation { get { return (string) manifest.Attribute (aNS + "installLocation"); } set { manifest.SetAttributeValue (aNS + "installLocation", NullIfEmpty (value)); } } @@ -190,7 +190,7 @@ public int? TargetSdkVersion { int? apiLevel = versions.GetApiLevelFromId (version); if (apiLevel.HasValue) return apiLevel.Value; - return versions.MaxStableVersion.ApiLevel; + return versions.MaxStableVersion?.ApiLevel; } return vn; } @@ -273,16 +273,16 @@ void RemoveAndroidPermissions (IEnumerable permissions) } [Obsolete ("Use GetLaunchableFastdevActivityName or GetLaunchableUserActivityName")] - public string GetLaunchableActivityName () + public string? GetLaunchableActivityName () { return GetLaunchableFastDevActivityName (); } /// Gets an activity that can be used to initialize the override directory for fastdev. [Obsolete ("This should not be needed anymore; Activity execution is not part of installation.")] - public string GetLaunchableFastDevActivityName () + public string? GetLaunchableFastDevActivityName () { - string first = null; + string? first = null; foreach (var a in GetLaunchableActivities ()) { var name = (string) a.Attribute (aName); //prefer the fastdev launcher, it's quicker @@ -300,7 +300,7 @@ public string GetLaunchableFastDevActivityName () // We add a fake launchable activity for FastDev, but we don't want // to launch that one when the user does Run or Debug - public string GetLaunchableUserActivityName () + public string? GetLaunchableUserActivityName () { return GetLaunchableActivities () .Select (a => (string) a.Attribute (aName)) diff --git a/src/Xamarin.Android.Tools.AndroidSdk/AndroidSdkInfo.cs b/src/Xamarin.Android.Tools.AndroidSdk/AndroidSdkInfo.cs index c12d7a4..1297e51 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/AndroidSdkInfo.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/AndroidSdkInfo.cs @@ -10,7 +10,7 @@ public class AndroidSdkInfo { AndroidSdkBase sdk; - public AndroidSdkInfo (Action logger = null, string androidSdkPath = null, string androidNdkPath = null, string javaSdkPath = null) + public AndroidSdkInfo (Action? logger = null, string? androidSdkPath = null, string? androidNdkPath = null, string? javaSdkPath = null) { logger = logger ?? DefaultConsoleLogger; @@ -71,10 +71,9 @@ orderby version descending select p; } - static Version TryParseVersion (string v) + static Version? TryParseVersion (string v) { - Version version; - if (Version.TryParse (v, out version)) + if (Version.TryParse (v, out var version)) return version; return null; } @@ -97,10 +96,13 @@ public string GetPlatformDirectoryFromId (string id) return Path.Combine (AndroidSdkPath, "platforms", "android-" + id); } - public string TryGetPlatformDirectoryFromApiLevel (string idOrApiLevel, AndroidVersions versions) + public string? TryGetPlatformDirectoryFromApiLevel (string idOrApiLevel, AndroidVersions versions) { var id = versions.GetIdFromApiLevel (idOrApiLevel); - var dir = GetPlatformDirectoryFromId (id); + if (id == null) + return null; + + string? dir = GetPlatformDirectoryFromId (id); if (Directory.Exists (dir)) return dir; @@ -118,12 +120,12 @@ public bool IsPlatformInstalled (int apiLevel) return apiLevel != 0 && Directory.Exists (GetPlatformDirectory (apiLevel)); } - public string AndroidNdkPath { + public string? AndroidNdkPath { get { return sdk.AndroidNdkPath; } } public string AndroidSdkPath { - get { return sdk.AndroidSdkPath; } + get { return sdk.AndroidSdkPath!; } } public string [] AllAndroidSdkPaths { @@ -133,14 +135,14 @@ public string [] AllAndroidSdkPaths { } public string JavaSdkPath { - get { return sdk.JavaSdkPath; } + get { return sdk.JavaSdkPath!; } } public string AndroidNdkHostPlatform { get { return sdk.NdkHostPlatform; } } - public static void SetPreferredAndroidNdkPath (string path, Action logger = null) + public static void SetPreferredAndroidNdkPath (string path, Action? logger = null) { logger = logger ?? DefaultConsoleLogger; @@ -160,7 +162,7 @@ internal static void DefaultConsoleLogger (TraceLevel level, string message) } } - public static void SetPreferredAndroidSdkPath (string path, Action logger = null) + public static void SetPreferredAndroidSdkPath (string path, Action? logger = null) { logger = logger ?? DefaultConsoleLogger; @@ -168,7 +170,7 @@ public static void SetPreferredAndroidSdkPath (string path, Action logger = null) + public static void SetPreferredJavaSdkPath (string path, Action? logger = null) { logger = logger ?? DefaultConsoleLogger; @@ -176,7 +178,7 @@ public static void SetPreferredJavaSdkPath (string path, Action logger = null) + public static void DetectAndSetPreferredJavaSdkPathToLatest (Action? logger = null) { if (OS.IsWindows) throw new NotImplementedException ("Windows is not supported at this time."); diff --git a/src/Xamarin.Android.Tools.AndroidSdk/AndroidVersion.cs b/src/Xamarin.Android.Tools.AndroidSdk/AndroidVersion.cs index 9805fc6..5cb3a3b 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/AndroidVersion.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/AndroidVersion.cs @@ -13,7 +13,7 @@ public class AndroidVersion public string Id { get; private set; } // Name of an Android release, e.g. "Oreo" - public string CodeName { get; private set; } + public string? CodeName { get; private set; } // Android version number, e.g. 8.0 public string OSVersion { get; private set; } @@ -28,9 +28,9 @@ public class AndroidVersion public bool Stable { get; private set; } // Alternate Ids for a given API level. Allows for historical mapping, e.g. API-11 has alternate ID 'H'. - internal string[] AlternateIds { get; set; } + internal string[]? AlternateIds { get; set; } - public AndroidVersion (int apiLevel, string osVersion, string codeName = null, string id = null, bool stable = true) + public AndroidVersion (int apiLevel, string osVersion, string? codeName = null, string? id = null, bool stable = true) { if (osVersion == null) throw new ArgumentNullException (nameof (osVersion)); diff --git a/src/Xamarin.Android.Tools.AndroidSdk/AndroidVersions.cs b/src/Xamarin.Android.Tools.AndroidSdk/AndroidVersions.cs index a56dcf3..9896288 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/AndroidVersions.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/AndroidVersions.cs @@ -13,8 +13,8 @@ public class AndroidVersions List installedVersions = new List (); public IReadOnlyList FrameworkDirectories { get; } - public AndroidVersion MaxStableVersion { get; private set; } - public AndroidVersion MinStableVersion { get; private set; } + public AndroidVersion? MaxStableVersion { get; private set; } + public AndroidVersion? MinStableVersion { get; private set; } public IReadOnlyList InstalledBindingVersions { get; private set; } @@ -33,7 +33,7 @@ public AndroidVersions (IEnumerable frameworkDirectories) var dn = Path.GetFileName (dp); // In "normal" use, `dp` will contain e.g. `...\MonoAndroid\v1.0`. // We want the `MonoAndroid` dir, not the versioned dir. - var p = dn.StartsWith ("v", StringComparison.Ordinal) ? Path.GetDirectoryName (dp) : dp; + var p = dn.StartsWith ("v", StringComparison.Ordinal) ? (Path.GetDirectoryName (dp) ?? "") : dp; dirs.Add (Path.GetFullPath (p)); } @@ -46,6 +46,8 @@ public AndroidVersions (IEnumerable frameworkDirectories) .Select (file => AndroidVersion.Load (file)); LoadVersions (versions); + + InstalledBindingVersions = new ReadOnlyCollection (installedVersions); } public AndroidVersions (IEnumerable versions) @@ -56,6 +58,8 @@ public AndroidVersions (IEnumerable versions) FrameworkDirectories = new ReadOnlyCollection (new string [0]); LoadVersions (versions); + + InstalledBindingVersions = new ReadOnlyCollection (installedVersions); } void LoadVersions (IEnumerable versions) @@ -71,8 +75,6 @@ void LoadVersions (IEnumerable versions) MinStableVersion = version; } } - - InstalledBindingVersions = new ReadOnlyCollection(installedVersions); } public int? GetApiLevelFromFrameworkVersion (string frameworkVersion) @@ -100,7 +102,7 @@ static bool MatchesId (AndroidVersion version, string id) (version.ApiLevel.ToString () == id); } - public string GetIdFromApiLevel (int apiLevel) + public string? GetIdFromApiLevel (int apiLevel) { return installedVersions.FirstOrDefault (v => v.ApiLevel == apiLevel)?.Id ?? KnownVersions.FirstOrDefault (v => v.ApiLevel == apiLevel)?.Id; @@ -108,7 +110,7 @@ public string GetIdFromApiLevel (int apiLevel) // Sometimes, e.g. when new API levels are introduced, the "API level" is a letter, not a number, // e.g. 'API-H' for API-11, 'API-O' for API-26, etc. - public string GetIdFromApiLevel (string apiLevel) + public string? GetIdFromApiLevel (string apiLevel) { if (int.TryParse (apiLevel, out var platform)) return GetIdFromApiLevel (platform); @@ -116,19 +118,19 @@ public string GetIdFromApiLevel (string apiLevel) KnownVersions.FirstOrDefault (v => MatchesId (v, apiLevel))?.Id; } - public string GetIdFromFrameworkVersion (string frameworkVersion) + public string? GetIdFromFrameworkVersion (string frameworkVersion) { return installedVersions.FirstOrDefault (v => MatchesFrameworkVersion (v, frameworkVersion))?.Id ?? KnownVersions.FirstOrDefault (v => MatchesFrameworkVersion (v, frameworkVersion))?.Id; } - public string GetFrameworkVersionFromApiLevel (int apiLevel) + public string? GetFrameworkVersionFromApiLevel (int apiLevel) { return installedVersions.FirstOrDefault (v => v.ApiLevel == apiLevel)?.FrameworkVersion ?? KnownVersions.FirstOrDefault (v => v.ApiLevel == apiLevel)?.FrameworkVersion; } - public string GetFrameworkVersionFromId (string id) + public string? GetFrameworkVersionFromId (string id) { return installedVersions.FirstOrDefault (v => MatchesId (v, id))?.FrameworkVersion ?? KnownVersions.FirstOrDefault (v => MatchesId (v, id))?.FrameworkVersion; @@ -179,10 +181,10 @@ class EqualityComparer : IEqualityComparer Func equals; Func getHashCode; - public EqualityComparer (Func equals, Func getHashCode = null) + public EqualityComparer (Func equals, Func? getHashCode = null) { this.equals = equals; - this.getHashCode = getHashCode ?? (v => v.GetHashCode ()); + this.getHashCode = getHashCode ?? (v => v?.GetHashCode () ?? 0); } public bool Equals (T x, T y) diff --git a/src/Xamarin.Android.Tools.AndroidSdk/FileUtil.cs b/src/Xamarin.Android.Tools.AndroidSdk/FileUtil.cs index 6fe8487..1b84575 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/FileUtil.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/FileUtil.cs @@ -16,7 +16,7 @@ public static void SystemRename (string sourceFile, string destFile) { //FIXME: use the atomic System.IO.File.Replace on NTFS if (OS.IsWindows) { - string wtmp = null; + string? wtmp = null; if (File.Exists (destFile)) { do { wtmp = Path.Combine (Path.GetTempPath (), Guid.NewGuid ().ToString ()); diff --git a/src/Xamarin.Android.Tools.AndroidSdk/JdkInfo.cs b/src/Xamarin.Android.Tools.AndroidSdk/JdkInfo.cs index 4d7f453..e82886d 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/JdkInfo.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/JdkInfo.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Text; @@ -20,7 +21,7 @@ public class JdkInfo { public string HomePath {get;} - public string Locator {get;} + public string? Locator {get;} public string JarPath {get;} public string JavaPath {get;} @@ -28,10 +29,10 @@ public class JdkInfo { public string JdkJvmPath {get;} public ReadOnlyCollection IncludePath {get;} - public Version Version => javaVersion.Value; - public string Vendor { + public Version? Version => javaVersion.Value; + public string? Vendor { get { - if (GetJavaSettingsPropertyValue ("java.vendor", out string vendor)) + if (GetJavaSettingsPropertyValue ("java.vendor", out string? vendor)) return vendor; return null; } @@ -41,7 +42,7 @@ public string Vendor { public IEnumerable JavaSettingsPropertyKeys => javaProperties.Value.Keys; Lazy>> javaProperties; - Lazy javaVersion; + Lazy javaVersion; public JdkInfo (string homePath) { @@ -57,7 +58,7 @@ public JdkInfo (string homePath) JavaPath = ProcessUtils.FindExecutablesInDirectory (binPath, "java").FirstOrDefault (); JavacPath = ProcessUtils.FindExecutablesInDirectory (binPath, "javac").FirstOrDefault (); - string topDir = null; + string? topDir = null; foreach (string dir in JdkLibraryTopDirs) { topDir = Path.Combine (HomePath, dir); if (!Directory.Exists (topDir)) { @@ -71,8 +72,8 @@ public JdkInfo (string homePath) topDir = Path.Combine (HomePath, JdkLibraryTopDirs [0]); JdkJvmPath = OS.IsMac - ? FindLibrariesInDirectory (topDir, "jli").FirstOrDefault () - : FindLibrariesInDirectory (topDir, "jvm").FirstOrDefault (); + ? FindLibrariesInDirectory (topDir!, "jli").FirstOrDefault () + : FindLibrariesInDirectory (topDir!, "jvm").FirstOrDefault (); ValidateFile ("jar", JarPath); ValidateFile ("java", JavaPath); @@ -93,7 +94,7 @@ public JdkInfo (string homePath) IncludePath = new ReadOnlyCollection (includes); javaProperties = new Lazy>> (GetJavaProperties, LazyThreadSafetyMode.ExecutionAndPublication); - javaVersion = new Lazy (GetJavaVersion, LazyThreadSafetyMode.ExecutionAndPublication); + javaVersion = new Lazy (GetJavaVersion, LazyThreadSafetyMode.ExecutionAndPublication); } public JdkInfo (string homePath, string locator) @@ -107,7 +108,7 @@ public override string ToString() return $"JdkInfo(Version={Version}, Vendor=\"{Vendor}\", HomePath=\"{HomePath}\", Locator=\"{Locator}\")"; } - public bool GetJavaSettingsPropertyValues (string key, out IEnumerable value) + public bool GetJavaSettingsPropertyValues (string key, [NotNullWhen (true)] out IEnumerable? value) { value = null; var props = javaProperties.Value; @@ -118,7 +119,7 @@ public bool GetJavaSettingsPropertyValues (string key, out IEnumerable v return false; } - public bool GetJavaSettingsPropertyValue (string key, out string value) + public bool GetJavaSettingsPropertyValue (string key, [NotNullWhen (true)] out string? value) { value = null; var props = javaProperties.Value; @@ -147,9 +148,9 @@ void ValidateFile (string name, string path) static Regex NonDigitMatcher = new Regex (@"[^\d]", RegexOptions.Compiled | RegexOptions.CultureInvariant); - Version GetJavaVersion () + Version? GetJavaVersion () { - string version = null; + string? version = null; if (ReleaseProperties.TryGetValue ("JAVA_VERSION", out version) && !string.IsNullOrEmpty (version)) { version = GetParsableVersion (version); if (ReleaseProperties.TryGetValue ("BUILD_NUMBER", out var build) && !string.IsNullOrEmpty (build)) @@ -193,7 +194,7 @@ ReadOnlyDictionary GetReleaseProperties () return new ReadOnlyDictionary(props); using (var release = File.OpenText (releasePath)) { - string line; + string? line; while ((line = release.ReadLine ()) != null) { line = line.Trim (); const string PropertyDelim = "="; @@ -243,7 +244,7 @@ static Dictionary> GetJavaProperties (string java) }; var props = new Dictionary> (); - string curKey = null; + string? curKey = null; if (!AnySystemJavasInstalled () && (java == "/usr/bin/java" || java == "java")) return props; @@ -266,8 +267,8 @@ static Dictionary> GetJavaProperties (string java) return; curKey = e.Data.Substring (NewValuePrefix.Length, delim - NewValuePrefix.Length); var value = e.Data.Substring (delim + NameValueDelim.Length); - List values; - if (!props.TryGetValue (curKey, out values)) + List? values; + if (!props.TryGetValue (curKey!, out values)) props.Add (curKey, values = new List ()); values.Add (value); } @@ -276,7 +277,7 @@ static Dictionary> GetJavaProperties (string java) return props; } - public static IEnumerable GetKnownSystemJdkInfos (Action logger = null) + public static IEnumerable GetKnownSystemJdkInfos (Action? logger = null) { logger = logger ?? AndroidSdkInfo.DefaultConsoleLogger; @@ -295,6 +296,7 @@ static IEnumerable GetConfiguredJdks (Action logger return GetConfiguredJdkPaths (logger) .Select (p => TryGetJdkInfo (p, logger, "monodroid-config.xml")) .Where (jdk => jdk != null) + .Select (jdk => jdk!) .OrderByDescending (jdk => jdk, JdkInfoVersionComparer.Default); } @@ -312,6 +314,7 @@ internal static IEnumerable GetMacOSMicrosoftJdks (Action TryGetJdkInfo (p, logger, "$HOME/Library/Developer/Xamarin/jdk")) .Where (jdk => jdk != null) + .Select (jdk => jdk!) .OrderByDescending (jdk => jdk, JdkInfoVersionComparer.Default); } @@ -329,9 +332,9 @@ static IEnumerable GetMacOSMicrosoftJdkPaths () return Directory.EnumerateDirectories (jdks); } - static JdkInfo TryGetJdkInfo (string path, Action logger, string locator) + static JdkInfo? TryGetJdkInfo (string path, Action logger, string locator) { - JdkInfo jdk = null; + JdkInfo? jdk = null; try { jdk = new JdkInfo (path, locator); } @@ -366,6 +369,7 @@ static IEnumerable GetLibexecJdks (Action logger) .Distinct () .Select (p => TryGetJdkInfo (p, logger, "`/usr/libexec/java_home -X`")) .Where (jdk => jdk != null) + .Select (jdk => jdk!) .OrderByDescending (jdk => jdk, JdkInfoVersionComparer.Default); } @@ -404,7 +408,8 @@ static IEnumerable GetJavaAlternativesJdks (Action return GetJavaAlternativesJdkPaths () .Distinct () .Select (p => TryGetJdkInfo (p, logger, "`/usr/sbin/update-java-alternatives -l`")) - .Where (jdk => jdk != null); + .Where (jdk => jdk != null) + .Select (jdk => jdk!); } static IEnumerable GetJavaAlternativesJdkPaths () @@ -438,6 +443,7 @@ static IEnumerable GetLibJvmJdks (Action logger) .Distinct () .Select (p => TryGetJdkInfo (p, logger, "`ls /usr/lib/jvm/*`")) .Where (jdk => jdk != null) + .Select (jdk => jdk!) .OrderByDescending (jdk => jdk, JdkInfoVersionComparer.Default); } @@ -459,7 +465,8 @@ static IEnumerable GetPathEnvironmentJdks (Action l { return GetPathEnvironmentJdkPaths () .Select (p => TryGetJdkInfo (p, logger, "$PATH")) - .Where (jdk => jdk != null); + .Where (jdk => jdk != null) + .Select (jdk => jdk!); } static IEnumerable GetPathEnvironmentJdkPaths () @@ -471,7 +478,7 @@ static IEnumerable GetPathEnvironmentJdkPaths () // `java -XshowSettings:properties -version 2>&1 | grep java.home` ends with `/jre` on macOS. // We need the parent dir so we can properly lookup the `include` directories if (java_home.EndsWith ("jre", StringComparison.OrdinalIgnoreCase)) { - java_home = Path.GetDirectoryName (java_home); + java_home = Path.GetDirectoryName (java_home) ?? ""; } yield return java_home; } diff --git a/src/Xamarin.Android.Tools.AndroidSdk/NullableAttributes.cs b/src/Xamarin.Android.Tools.AndroidSdk/NullableAttributes.cs new file mode 100644 index 0000000..ba4e92f --- /dev/null +++ b/src/Xamarin.Android.Tools.AndroidSdk/NullableAttributes.cs @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Diagnostics.CodeAnalysis +{ + /// Specifies that null is allowed as an input even if the corresponding type disallows it. + [AttributeUsage (AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class AllowNullAttribute : Attribute + { } + + /// Specifies that null is disallowed as an input even if the corresponding type allows it. + [AttributeUsage (AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class DisallowNullAttribute : Attribute + { } + + /// Specifies that an output may be null even if the corresponding type disallows it. + [AttributeUsage (AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class MaybeNullAttribute : Attribute + { } + + /// Specifies that an output will not be null even if the corresponding type allows it. + [AttributeUsage (AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class NotNullAttribute : Attribute + { } + + /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. + [AttributeUsage (AttributeTargets.Parameter, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class MaybeNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter may be null. + /// + public MaybeNullWhenAttribute (bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. + [AttributeUsage (AttributeTargets.Parameter, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class NotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute (bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that the output will be non-null if the named parameter is non-null. + [AttributeUsage (AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class NotNullIfNotNullAttribute : Attribute + { + /// Initializes the attribute with the associated parameter name. + /// + /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. + /// + public NotNullIfNotNullAttribute (string parameterName) => ParameterName = parameterName; + + /// Gets the associated parameter name. + public string ParameterName { get; } + } + + /// Applied to a method that will never return under any circumstance. + [AttributeUsage (AttributeTargets.Method, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class DoesNotReturnAttribute : Attribute + { } + + /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. + [AttributeUsage (AttributeTargets.Parameter, Inherited = false)] +#if INTERNAL_NULLABLE_ATTRIBUTES + internal +#else + public +#endif + sealed class DoesNotReturnIfAttribute : Attribute + { + /// Initializes the attribute with the specified parameter value. + /// + /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + /// the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute (bool parameterValue) => ParameterValue = parameterValue; + + /// Gets the condition parameter value. + public bool ParameterValue { get; } + } +} diff --git a/src/Xamarin.Android.Tools.AndroidSdk/OS.cs b/src/Xamarin.Android.Tools.AndroidSdk/OS.cs index 81e7766..ffb395f 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/OS.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/OS.cs @@ -10,9 +10,9 @@ public class OS public readonly static bool IsWindows; public readonly static bool IsMac; - internal readonly static string ProgramFilesX86; + internal readonly static string? ProgramFilesX86; - internal readonly static string NativeLibraryFormat; + internal readonly static string NativeLibraryFormat = "{0}"; static OS () { @@ -39,7 +39,7 @@ static bool IsRunningOnMac () buf = Marshal.AllocHGlobal (8192); // This is a hacktastic way of getting sysname from uname () if (uname (buf) == 0) { - string os = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (buf); + string? os = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (buf); if (os == "Darwin") return true; } @@ -136,13 +136,13 @@ static extern int RegSetValueExW (UIntPtr hKey, string lpValueName, int lpReserv uint dwType, IntPtr data, uint cbData); [DllImport (ADVAPI, CharSet = CharSet.Unicode, SetLastError = true)] - static extern int RegCreateKeyEx (UIntPtr hKey, string subKey, uint reserved, string @class, uint options, + static extern int RegCreateKeyEx (UIntPtr hKey, string subKey, uint reserved, string? @class, uint options, uint samDesired, IntPtr lpSecurityAttributes, out UIntPtr phkResult, out Disposition lpdwDisposition); [DllImport ("advapi32.dll", SetLastError = true)] static extern int RegCloseKey (UIntPtr hKey); - public static string GetValueString (UIntPtr key, string subkey, string valueName, Wow64 wow64) + public static string? GetValueString (UIntPtr key, string subkey, string valueName, Wow64 wow64) { UIntPtr regKeyHandle; uint sam = (uint)Rights.QueryValue + (uint)wow64; @@ -168,8 +168,8 @@ public static void SetValueString (UIntPtr key, string subkey, string valueName, uint sam = (uint)(Rights.CreateSubKey | Rights.SetValue) + (uint)wow64; uint options = (uint) Options.NonVolatile; Disposition disposition; - if (RegCreateKeyEx (key, subkey, 0, null, options, sam, IntPtr.Zero, out regKeyHandle, out disposition) != 0) { - throw new Exception ("Could not open or craete key"); + if (RegCreateKeyEx (key, subkey, 0, "", options, sam, IntPtr.Zero, out regKeyHandle, out disposition) != 0) { + throw new Exception ("Could not open or create key"); } try { diff --git a/src/Xamarin.Android.Tools.AndroidSdk/ProcessUtils.cs b/src/Xamarin.Android.Tools.AndroidSdk/ProcessUtils.cs index 352eb77..19965e2 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/ProcessUtils.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/ProcessUtils.cs @@ -19,7 +19,7 @@ static ProcessUtils () ExecutableFileExtensions = pathExts; } - public static async Task StartProcess (ProcessStartInfo psi, TextWriter stdout, TextWriter stderr, CancellationToken cancellationToken, Action onStarted = null) + public static async Task StartProcess (ProcessStartInfo psi, TextWriter? stdout, TextWriter? stderr, CancellationToken cancellationToken, Action? onStarted = null) { cancellationToken.ThrowIfCancellationRequested (); psi.UseShellExecute = false; @@ -47,10 +47,10 @@ public static async Task StartProcess (ProcessStartInfo psi, TextWriter std // end up writing to the same buffer, or they are the same object. using (cancellationToken.Register (() => KillProcess (process))) { if (psi.RedirectStandardOutput) - output = ReadStreamAsync (process.StandardOutput, TextWriter.Synchronized (stdout)); + output = ReadStreamAsync (process.StandardOutput, TextWriter.Synchronized (stdout!)); if (psi.RedirectStandardError) - error = ReadStreamAsync (process.StandardError, TextWriter.Synchronized (stderr)); + error = ReadStreamAsync (process.StandardError, TextWriter.Synchronized (stderr!)); await Task.WhenAll (new [] { output, error, exit }).ConfigureAwait (false); } @@ -89,7 +89,7 @@ static async Task ReadStreamAsync (StreamReader stream, TextWriter destination) /// /// Executes an Android Sdk tool and returns a result. The result is based on a function of the command output. /// - public static Task ExecuteToolAsync (string exe, Func result, CancellationToken token, Action onStarted = null) + public static Task ExecuteToolAsync (string exe, Func result, CancellationToken token, Action? onStarted = null) { var tcs = new TaskCompletionSource (); @@ -115,12 +115,12 @@ public static Task ExecuteToolAsync (string exe, Func FindExecutablesInPath (string executable) { - var path = Environment.GetEnvironmentVariable ("PATH"); + var path = Environment.GetEnvironmentVariable ("PATH") ?? ""; var pathDirs = path.Split (new char[] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries); foreach (var dir in pathDirs) { diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkBase.cs b/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkBase.cs index b401fb6..1b10df1 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkBase.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkBase.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.IO; using System.Collections.Generic; @@ -8,8 +9,8 @@ namespace Xamarin.Android.Tools { abstract class AndroidSdkBase { - string[] allAndroidSdks = null; - string[] allAndroidNdks = null; + string[]? allAndroidSdks; + string[]? allAndroidNdks; public string[] AllAndroidSdks { get { @@ -33,12 +34,12 @@ public AndroidSdkBase (Action logger) Logger = logger; } - public string AndroidSdkPath { get; private set; } - public string AndroidNdkPath { get; private set; } - public string JavaSdkPath { get; private set; } - public string JavaBinPath { get; private set; } - public string AndroidPlatformToolsPath { get; private set; } - public string AndroidPlatformToolsPathShort { get; private set; } + public string? AndroidSdkPath { get; private set; } + public string? AndroidNdkPath { get; private set; } + public string? JavaSdkPath { get; private set; } + public string? JavaBinPath { get; private set; } + public string? AndroidPlatformToolsPath { get; private set; } + public string? AndroidPlatformToolsPathShort { get; private set; } public virtual string Adb { get; protected set; } = "adb"; public virtual string ZipAlign { get; protected set; } = "zipalign"; @@ -50,11 +51,11 @@ public AndroidSdkBase (Action logger) public abstract string NdkHostPlatform64Bit { get; } public virtual string Javac { get; protected set; } = "javac"; - public abstract string PreferedAndroidSdkPath { get; } - public abstract string PreferedAndroidNdkPath { get; } - public abstract string PreferedJavaSdkPath { get; } + public abstract string? PreferedAndroidSdkPath { get; } + public abstract string? PreferedAndroidNdkPath { get; } + public abstract string? PreferedJavaSdkPath { get; } - public virtual void Initialize (string androidSdkPath = null, string androidNdkPath = null, string javaSdkPath = null) + public virtual void Initialize (string? androidSdkPath = null, string? androidNdkPath = null, string? javaSdkPath = null) { androidSdkPath = androidSdkPath ?? PreferedAndroidSdkPath; androidNdkPath = androidNdkPath ?? PreferedAndroidNdkPath; @@ -94,12 +95,12 @@ public virtual void Initialize (string androidSdkPath = null, string androidNdkP protected abstract IEnumerable GetAllAvailableAndroidSdks (); protected abstract IEnumerable GetAllAvailableAndroidNdks (); - protected abstract string GetJavaSdkPath (); + protected abstract string? GetJavaSdkPath (); protected abstract string GetShortFormPath (string path); - public abstract void SetPreferredAndroidSdkPath (string path); - public abstract void SetPreferredJavaSdkPath (string path); - public abstract void SetPreferredAndroidNdkPath (string path); + public abstract void SetPreferredAndroidSdkPath (string? path); + public abstract void SetPreferredJavaSdkPath (string? path); + public abstract void SetPreferredAndroidNdkPath (string? path); public bool IsNdk64Bit { get; private set; } @@ -110,7 +111,7 @@ public string NdkHostPlatform { /// /// Checks that a value is the location of an Android SDK. /// - public bool ValidateAndroidSdkLocation (string loc) + public bool ValidateAndroidSdkLocation ([NotNullWhen (true)] string? loc) { bool result = !string.IsNullOrEmpty (loc) && ProcessUtils.FindExecutablesInDirectory (Path.Combine (loc, "platform-tools"), Adb).Any (); Logger (TraceLevel.Verbose, $"{nameof (ValidateAndroidSdkLocation)}: `{loc}`, result={result}"); @@ -120,7 +121,7 @@ public bool ValidateAndroidSdkLocation (string loc) /// /// Checks that a value is the location of a Java SDK. /// - public virtual bool ValidateJavaSdkLocation (string loc) + public virtual bool ValidateJavaSdkLocation ([NotNullWhen (true)] string? loc) { bool result = !string.IsNullOrEmpty (loc) && ProcessUtils.FindExecutablesInDirectory (Path.Combine (loc, "bin"), JarSigner).Any (); Logger (TraceLevel.Verbose, $"{nameof (ValidateJavaSdkLocation)}: `{loc}`, result={result}"); @@ -130,14 +131,15 @@ public virtual bool ValidateJavaSdkLocation (string loc) /// /// Checks that a value is the location of an Android SDK. /// - public bool ValidateAndroidNdkLocation (string loc) + public bool ValidateAndroidNdkLocation ([NotNullWhen (true)] string? loc) { - bool result = !string.IsNullOrEmpty (loc) && ProcessUtils.FindExecutablesInDirectory (loc, NdkStack).Any (); + bool result = !string.IsNullOrEmpty (loc) && + ProcessUtils.FindExecutablesInDirectory (loc!, NdkStack).Any (); Logger (TraceLevel.Verbose, $"{nameof (ValidateAndroidNdkLocation)}: `{loc}`, result={result}"); return result; } - protected static string NullIfEmpty (string s) + protected static string? NullIfEmpty (string? s) { if (s == null || s.Length != 0) return s; @@ -145,7 +147,7 @@ protected static string NullIfEmpty (string s) return null; } - static string GetExecutablePath (string dir, string exe) + static string GetExecutablePath (string? dir, string exe) { if (string.IsNullOrEmpty (dir)) return exe; diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkUnix.cs b/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkUnix.cs index 0d18f7b..12bd536 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkUnix.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkUnix.cs @@ -13,8 +13,8 @@ namespace Xamarin.Android.Tools class AndroidSdkUnix : AndroidSdkBase { // See comments above UnixConfigPath for explanation on why these are needed - static readonly string sudo_user; - static readonly string user; + static readonly string? sudo_user; + static readonly string? user; static readonly bool need_chown; static AndroidSdkUnix () @@ -41,7 +41,7 @@ public override string NdkHostPlatform64Bit { get { return OS.IsMac ? "darwin-x86_64" : "linux-x86_64"; } } - public override string PreferedAndroidSdkPath { + public override string? PreferedAndroidSdkPath { get { var config_file = GetUnixConfigFile (Logger); var androidEl = config_file.Root.Element ("android-sdk"); @@ -56,7 +56,7 @@ public override string PreferedAndroidSdkPath { } } - public override string PreferedAndroidNdkPath { + public override string? PreferedAndroidNdkPath { get { var config_file = GetUnixConfigFile (Logger); var androidEl = config_file.Root.Element ("android-ndk"); @@ -71,7 +71,7 @@ public override string PreferedAndroidNdkPath { } } - public override string PreferedJavaSdkPath { + public override string? PreferedJavaSdkPath { get { var config_file = GetUnixConfigFile (Logger); var javaEl = config_file.Root.Element ("java-sdk"); @@ -90,7 +90,7 @@ protected override IEnumerable GetAllAvailableAndroidSdks () { var preferedSdkPath = PreferedAndroidSdkPath; if (!string.IsNullOrEmpty (preferedSdkPath)) - yield return preferedSdkPath; + yield return preferedSdkPath!; // Look in PATH foreach (var adb in ProcessUtils.FindExecutablesInPath (Adb)) { @@ -108,7 +108,7 @@ protected override IEnumerable GetAllAvailableAndroidSdks () yield return macSdkPath; } - protected override string GetJavaSdkPath () + protected override string? GetJavaSdkPath () { return JdkInfo.GetKnownSystemJdkInfos (Logger).FirstOrDefault ()?.HomePath; } @@ -117,7 +117,7 @@ protected override IEnumerable GetAllAvailableAndroidNdks () { var preferedNdkPath = PreferedAndroidNdkPath; if (!string.IsNullOrEmpty (preferedNdkPath)) - yield return preferedNdkPath; + yield return preferedNdkPath!; // Look in PATH foreach (var ndkStack in ProcessUtils.FindExecutablesInPath (NdkStack)) { @@ -133,7 +133,7 @@ protected override string GetShortFormPath (string path) return path; } - public override void SetPreferredAndroidSdkPath (string path) + public override void SetPreferredAndroidSdkPath (string? path) { path = NullIfEmpty (path); @@ -149,7 +149,7 @@ public override void SetPreferredAndroidSdkPath (string path) SaveConfig (doc); } - public override void SetPreferredJavaSdkPath (string path) + public override void SetPreferredJavaSdkPath (string? path) { path = NullIfEmpty (path); @@ -165,7 +165,7 @@ public override void SetPreferredJavaSdkPath (string path) SaveConfig (doc); } - public override void SetPreferredAndroidNdkPath (string path) + public override void SetPreferredAndroidNdkPath (string? path) { path = NullIfEmpty (path); @@ -184,11 +184,11 @@ public override void SetPreferredAndroidNdkPath (string path) void SaveConfig (XDocument doc) { string cfg = UnixConfigPath; - List created = null; + List ? created = null; if (!File.Exists (cfg)) { - string dir = Path.GetDirectoryName (cfg); - if (!Directory.Exists (dir)) { + string? dir = Path.GetDirectoryName (cfg); + if (dir != null && !Directory.Exists (dir)) { Directory.CreateDirectory (dir); AddToList (dir); } @@ -245,7 +245,7 @@ private static string UnixConfigPath { internal static XDocument GetUnixConfigFile (Action logger) { var file = UnixConfigPath; - XDocument doc = null; + XDocument? doc = null; if (File.Exists (file)) { try { doc = XDocument.Load (file); @@ -270,7 +270,7 @@ internal static XDocument GetUnixConfigFile (Action logger) return doc; } - void FixOwnership (List paths) + void FixOwnership (List? paths) { if (!need_chown || paths == null || paths.Count == 0) return; @@ -278,7 +278,7 @@ void FixOwnership (List paths) var stdout = new StringWriter (); var stderr = new StringWriter (); var args = new List { - QuoteString (sudo_user) + QuoteString (sudo_user!) }; foreach (string p in paths) diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkWindows.cs b/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkWindows.cs index 792c658..c363703 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkWindows.cs +++ b/src/Xamarin.Android.Tools.AndroidSdk/Sdks/AndroidSdkWindows.cs @@ -32,7 +32,7 @@ public AndroidSdkWindows (Action logger) public override string NdkHostPlatform64Bit { get { return "windows-x86_64"; } } public override string Javac { get; protected set; } = "javac.exe"; - public override string PreferedAndroidSdkPath { + public override string? PreferedAndroidSdkPath { get { var wow = RegistryEx.Wow64.Key32; var regKey = GetMDRegistryKey (); @@ -41,7 +41,7 @@ public override string PreferedAndroidSdkPath { return null; } } - public override string PreferedAndroidNdkPath { + public override string? PreferedAndroidNdkPath { get { var wow = RegistryEx.Wow64.Key32; var regKey = GetMDRegistryKey (); @@ -50,7 +50,7 @@ public override string PreferedAndroidNdkPath { return null; } } - public override string PreferedJavaSdkPath { + public override string? PreferedJavaSdkPath { get { var wow = RegistryEx.Wow64.Key32; var regKey = GetMDRegistryKey (); @@ -77,16 +77,16 @@ protected override IEnumerable GetAllAvailableAndroidSdks () // Check for the key the user gave us in the VS/addin options foreach (var root in roots) if (CheckRegistryKeyForExecutable (root, regKey, MDREG_ANDROID_SDK, wow, "platform-tools", Adb)) - yield return RegistryEx.GetValueString (root, regKey, MDREG_ANDROID_SDK, wow); + yield return RegistryEx.GetValueString (root, regKey, MDREG_ANDROID_SDK, wow) ?? ""; // Check for the key written by the Xamarin installer if (CheckRegistryKeyForExecutable (RegistryEx.CurrentUser, XAMARIN_ANDROID_INSTALLER_PATH, XAMARIN_ANDROID_INSTALLER_KEY, wow, "platform-tools", Adb)) - yield return RegistryEx.GetValueString (RegistryEx.CurrentUser, XAMARIN_ANDROID_INSTALLER_PATH, XAMARIN_ANDROID_INSTALLER_KEY, wow); + yield return RegistryEx.GetValueString (RegistryEx.CurrentUser, XAMARIN_ANDROID_INSTALLER_PATH, XAMARIN_ANDROID_INSTALLER_KEY, wow) ?? ""; // Check for the key written by the Android SDK installer foreach (var root in roots) if (CheckRegistryKeyForExecutable (root, ANDROID_INSTALLER_PATH, ANDROID_INSTALLER_KEY, wow, "platform-tools", Adb)) - yield return RegistryEx.GetValueString (root, ANDROID_INSTALLER_PATH, ANDROID_INSTALLER_KEY, wow); + yield return RegistryEx.GetValueString (root, ANDROID_INSTALLER_PATH, ANDROID_INSTALLER_KEY, wow) ?? ""; // Check some hardcoded paths for good measure var paths = new string [] { @@ -94,7 +94,7 @@ protected override IEnumerable GetAllAvailableAndroidSdks () Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.ProgramFilesX86), "Android", "android-sdk"), Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.ProgramFilesX86), "Android", "android-sdk-windows"), !string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("ProgramW6432")) - ? Path.Combine (Environment.GetEnvironmentVariable ("ProgramW6432"), "Android", "android-sdk") + ? Path.Combine (Environment.GetEnvironmentVariable ("ProgramW6432") ?? "", "Android", "android-sdk") : Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.ProgramFiles), "Android", "android-sdk"), Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData), "Android", "android-sdk"), Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData), "Android", "android-sdk"), @@ -106,7 +106,7 @@ protected override IEnumerable GetAllAvailableAndroidSdks () yield return basePath; } - protected override string GetJavaSdkPath () + protected override string? GetJavaSdkPath () { var jdk = GetJdkInfos (Logger).FirstOrDefault (); return jdk?.HomePath; @@ -114,9 +114,9 @@ protected override string GetJavaSdkPath () internal static IEnumerable GetJdkInfos (Action logger) { - JdkInfo TryGetJdkInfo (string path, string locator) + JdkInfo? TryGetJdkInfo (string path, string locator) { - JdkInfo jdk = null; + JdkInfo? jdk = null; try { jdk = new JdkInfo (path, locator); } @@ -131,6 +131,7 @@ IEnumerable ToJdkInfos (IEnumerable paths, string locator) { return paths.Select (p => TryGetJdkInfo (p, locator)) .Where (jdk => jdk != null) + .Select(jdk => jdk!) .OrderByDescending (jdk => jdk, JdkInfoVersionComparer.Default); } @@ -161,7 +162,7 @@ private static IEnumerable GetPreferredJdkPaths () foreach (var root in roots) { if (CheckRegistryKeyForExecutable (root, regKey, MDREG_JAVA_SDK, wow, "bin", _JarSigner)) - yield return RegistryEx.GetValueString (root, regKey, MDREG_JAVA_SDK, wow); + yield return RegistryEx.GetValueString (root, regKey, MDREG_JAVA_SDK, wow) ?? ""; } } @@ -174,7 +175,7 @@ private static IEnumerable GetOpenJdkPaths () foreach (var wow in wows) { if (CheckRegistryKeyForExecutable (root, subKey, valueName, wow, "bin", _JarSigner)) - yield return RegistryEx.GetValueString (root, subKey, valueName, wow); + yield return RegistryEx.GetValueString (root, subKey, valueName, wow) ?? ""; } } @@ -196,7 +197,7 @@ private static IEnumerable GetKnownOpenJdkPaths () if (Directory.Exists (rootPath)) { foreach (var directoryName in Directory.EnumerateDirectories (rootPath, $"{JdkFolderNamePattern}*").ToList ()) { var versionString = directoryName.Replace ($"{rootPath}\\{JdkFolderNamePattern}", string.Empty); - if (Version.TryParse (versionString, out Version ver)) { + if (Version.TryParse (versionString, out Version? ver)) { paths.Add (new Tuple(directoryName, ver)); } } @@ -220,13 +221,13 @@ private static IEnumerable GetOracleJdkPaths () // No matter what the CurrentVersion is, look for 1.6 or 1.7 or 1.8 if (CheckRegistryKeyForExecutable (RegistryEx.LocalMachine, subkey + "\\" + "1.8", "JavaHome", wow64, "bin", _JarSigner)) - yield return RegistryEx.GetValueString (RegistryEx.LocalMachine, subkey + "\\" + "1.8", "JavaHome", wow64); + yield return RegistryEx.GetValueString (RegistryEx.LocalMachine, subkey + "\\" + "1.8", "JavaHome", wow64) ?? ""; if (CheckRegistryKeyForExecutable (RegistryEx.LocalMachine, subkey + "\\" + "1.7", "JavaHome", wow64, "bin", _JarSigner)) - yield return RegistryEx.GetValueString (RegistryEx.LocalMachine, subkey + "\\" + "1.7", "JavaHome", wow64); + yield return RegistryEx.GetValueString (RegistryEx.LocalMachine, subkey + "\\" + "1.7", "JavaHome", wow64) ?? ""; if (CheckRegistryKeyForExecutable (RegistryEx.LocalMachine, subkey + "\\" + "1.6", "JavaHome", wow64, "bin", _JarSigner)) - yield return RegistryEx.GetValueString (RegistryEx.LocalMachine, subkey + "\\" + "1.6", "JavaHome", wow64); + yield return RegistryEx.GetValueString (RegistryEx.LocalMachine, subkey + "\\" + "1.6", "JavaHome", wow64) ?? ""; } } } @@ -244,7 +245,7 @@ protected override IEnumerable GetAllAvailableAndroidNdks () var sdks = GetAllAvailableAndroidSdks().ToList(); if (!string.IsNullOrEmpty(AndroidSdkPath)) - sdks.Add(AndroidSdkPath); + sdks.Add (AndroidSdkPath!); foreach(var sdk in sdks.Distinct()) if (Directory.Exists(ndk = Path.Combine(sdk, "ndk-bundle"))) @@ -254,7 +255,7 @@ protected override IEnumerable GetAllAvailableAndroidNdks () // Check for the key the user gave us in the VS/addin options foreach (var root in roots) if (CheckRegistryKeyForExecutable (root, regKey, MDREG_ANDROID_NDK, wow, ".", NdkStack)) - yield return RegistryEx.GetValueString (root, regKey, MDREG_ANDROID_NDK, wow); + yield return RegistryEx.GetValueString (root, regKey, MDREG_ANDROID_NDK, wow) ?? ""; /* // Check for the key written by the Xamarin installer @@ -267,7 +268,7 @@ protected override IEnumerable GetAllAvailableAndroidNdks () var vs_default = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData), "Microsoft", "AndroidNDK"); var vs_default32bit = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData), "Microsoft", "AndroidNDK32"); var vs_2017_default = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData), "Microsoft", "AndroidNDK64"); - var android_default = Path.Combine (OS.ProgramFilesX86, "Android"); + var android_default = Path.Combine (OS.ProgramFilesX86 ?? "", "Android"); var cdrive_default = @"C:\"; foreach (var basePath in new string [] {xamarin_private, android_default, vs_default, vs_default32bit, vs_2017_default, cdrive_default}) @@ -282,19 +283,19 @@ protected override string GetShortFormPath (string path) return KernelEx.GetShortPathName (path); } - public override void SetPreferredAndroidSdkPath (string path) + public override void SetPreferredAndroidSdkPath (string? path) { var regKey = GetMDRegistryKey (); RegistryEx.SetValueString (RegistryEx.CurrentUser, regKey, MDREG_ANDROID_SDK, path ?? "", RegistryEx.Wow64.Key32); } - public override void SetPreferredJavaSdkPath (string path) + public override void SetPreferredJavaSdkPath (string? path) { var regKey = GetMDRegistryKey (); RegistryEx.SetValueString (RegistryEx.CurrentUser, regKey, MDREG_JAVA_SDK, path ?? "", RegistryEx.Wow64.Key32); } - public override void SetPreferredAndroidNdkPath (string path) + public override void SetPreferredAndroidNdkPath (string? path) { var regKey = GetMDRegistryKey (); RegistryEx.SetValueString (RegistryEx.CurrentUser, regKey, MDREG_ANDROID_NDK, path ?? "", RegistryEx.Wow64.Key32); @@ -323,7 +324,7 @@ private static bool CheckRegistryKeyForExecutable (UIntPtr key, string subkey, s } #endregion - public override void Initialize (string androidSdkPath = null, string androidNdkPath = null, string javaSdkPath = null) + public override void Initialize (string? androidSdkPath = null, string? androidNdkPath = null, string? javaSdkPath = null) { base.Initialize (androidSdkPath, androidNdkPath, javaSdkPath); @@ -335,7 +336,7 @@ public override void Initialize (string androidSdkPath = null, string androidNdk var javaBinPath = this.JavaBinPath; if (!string.IsNullOrEmpty (javaBinPath)) { - var environmentPath = Environment.GetEnvironmentVariable ("PATH"); + var environmentPath = Environment.GetEnvironmentVariable ("PATH") ?? ""; if (!environmentPath.Contains (javaBinPath)) { var processPath = string.Concat (javaBinPath, Path.PathSeparator, environmentPath); Environment.SetEnvironmentVariable ("PATH", processPath); diff --git a/src/Xamarin.Android.Tools.AndroidSdk/Xamarin.Android.Tools.AndroidSdk.csproj b/src/Xamarin.Android.Tools.AndroidSdk/Xamarin.Android.Tools.AndroidSdk.csproj index 02aec41..81a7a74 100644 --- a/src/Xamarin.Android.Tools.AndroidSdk/Xamarin.Android.Tools.AndroidSdk.csproj +++ b/src/Xamarin.Android.Tools.AndroidSdk/Xamarin.Android.Tools.AndroidSdk.csproj @@ -1,7 +1,10 @@ - netstandard2.0 + netstandard2.0;netcoreapp3.1 + 8.0 + enable + INTERNAL_NULLABLE_ATTRIBUTES true ..\..\product.snk Xamarin.Android.Tools @@ -13,12 +16,12 @@ Xamarin;Xamarin.Android - - ..\..\bin\Debug - + + + - - ..\..\bin\Release + + $(ToolOutputFullPath) diff --git a/tests/Xamarin.Android.Tools.AndroidSdk-Tests/Xamarin.Android.Tools.AndroidSdk-Tests.csproj b/tests/Xamarin.Android.Tools.AndroidSdk-Tests/Xamarin.Android.Tools.AndroidSdk-Tests.csproj index b2de219..2de72a8 100644 --- a/tests/Xamarin.Android.Tools.AndroidSdk-Tests/Xamarin.Android.Tools.AndroidSdk-Tests.csproj +++ b/tests/Xamarin.Android.Tools.AndroidSdk-Tests/Xamarin.Android.Tools.AndroidSdk-Tests.csproj @@ -1,17 +1,13 @@ - net461 + net461;netcoreapp3.1 false false - - ..\..\bin\TestDebug - - - - ..\..\bin\TestRelease + + $(TestOutputFullPath)