From 34b938be5c847554086688f97d9d4137675e804c Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Thu, 6 Apr 2023 19:36:05 +0200 Subject: [PATCH 1/3] Fix integer to string conversion when generating code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/xamarin/xamarin-android/issues/7939 Context: https://github.com/dotnet/runtime/issues/13363 Context: https://www.unicode.org/charts/PDF/U2200.pdf (page 2) LLVM IR generator outputs a number of integer values, relying on the standard `ToString()` method to convert the value to string. However, since NET6, this conversion is culture-sensitive and in certain cultures (list below) it will output the minus sign not as the standard ASCII 0x2D character but rather as the Unicode 0x2212 (mathematical operator "minus") character. This breaks LLVM LLC: llc: environment.arm64-v8a.ll:554:7: error: expected value token stderr | i32 −1, ; apk_fd Fix the problem by using invariant culture when converting **all** integers and unknown objects to strings. The reason all of them are converted in this way is to avoid future changes to ICU and/or .NET to-string conversion that might affect the resulting integer format. Locales/cultures affected by this issue are as follows: * et * et-EE * eu * eu-ES * fi * fi-FI * fo * fo-DK * fo-FO * gsw * gsw-CH * gsw-FR * gsw-LI * hr * hr-BA * hr-HR * ksh * ksh-DE * lt * lt-LT * nb * nb-NO * nb-SJ * nn * nn-NO * no * rm * rm-CH * se * se-FI * se-NO * se-SE * sl * sl-SI * sv * sv-AX * sv-FI * sv-SE --- ...pplicationConfigNativeAssemblyGenerator.cs | 3 +- .../LlvmIrGenerator/FunctionAttributes.cs | 15 +++-- .../LlvmIrGenerator/LlvmIrFunction.cs | 1 - .../LlvmIrGenerator/LlvmIrGenerator.Code.cs | 14 ++-- .../LlvmIrGenerator/LlvmIrGenerator.cs | 67 +++++++++++-------- .../LlvmIrGenerator/LlvmIrMetadataManager.cs | 5 +- .../Utilities/MonoAndroidHelper.cs | 10 +++ 7 files changed, 70 insertions(+), 45 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs index 6802af86229..c240e5aefa2 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using Java.Interop.Tools.TypeNameMappings; @@ -253,7 +254,7 @@ List> InitDSOCache () continue; } - dsos.Add ((name, $"dsoName{dsos.Count}", ELFHelper.IsEmptyAOTLibrary (log, item.ItemSpec))); + dsos.Add ((name, $"dsoName{dsos.Count.ToString (CultureInfo.InvariantCulture)}", ELFHelper.IsEmptyAOTLibrary (log, item.ItemSpec))); } var dsoCache = new List> (); diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/FunctionAttributes.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/FunctionAttributes.cs index 84167d9084d..d0bbc9cc7cc 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/FunctionAttributes.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/FunctionAttributes.cs @@ -1,5 +1,6 @@ using System; using System.Text; +using System.Globalization; namespace Xamarin.Android.Tasks.LLVMIR { @@ -113,7 +114,7 @@ public AlignstackFunctionAttribute (uint powerOfTwoAlignment) protected override void RenderParams (StringBuilder sb) { - sb.Append (alignment); + sb.Append (alignment.ToString (CultureInfo.InvariantCulture)); } } @@ -165,13 +166,13 @@ public AllocsizeFunctionAttribute (uint elementSize, uint? numberOfElements = nu protected override void RenderParams (StringBuilder sb) { - sb.Append (elementSize); + sb.Append (elementSize.ToString (CultureInfo.InvariantCulture)); if (!numberOfElements.HasValue) { return; } sb.Append (", "); - sb.Append (numberOfElements.Value); + sb.Append (numberOfElements.Value.ToString (CultureInfo.InvariantCulture)); } } @@ -701,13 +702,13 @@ public VscaleRangeFunctionAttribute (uint min, uint? max = null) protected override void RenderParams (StringBuilder sb) { - sb.Append (min); + sb.Append (min.ToString (CultureInfo.InvariantCulture)); if (!max.HasValue) { return; } sb.Append (", "); - sb.Append (max.Value); + sb.Append (max.Value.ToString (CultureInfo.InvariantCulture)); } } @@ -721,7 +722,7 @@ public MinLegalVectorWidthFunctionAttribute (uint size) this.size = size; } - protected override void RenderAssignedValue (StringBuilder sb) => sb.Append (size); + protected override void RenderAssignedValue (StringBuilder sb) => sb.Append (size.ToString (CultureInfo.InvariantCulture)); } class StackProtectorBufferSizeFunctionAttribute : LLVMFunctionAttribute @@ -734,7 +735,7 @@ public StackProtectorBufferSizeFunctionAttribute (uint size) this.size = size; } - protected override void RenderAssignedValue (StringBuilder sb) => sb.Append (size); + protected override void RenderAssignedValue (StringBuilder sb) => sb.Append (size.ToString (CultureInfo.InvariantCulture)); } class TargetCpuFunctionAttribute : LLVMFunctionAttribute diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunction.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunction.cs index 219491437d5..f2f9ba989ec 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunction.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunction.cs @@ -3,7 +3,6 @@ using System.Globalization; using System.Linq; using System.Text; -using System.Reflection; namespace Xamarin.Android.Tasks.LLVMIR { diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.Code.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.Code.cs index 85b9662c524..94f90dae375 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.Code.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.Code.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Text; @@ -52,7 +53,7 @@ public void WriteFunctionStart (LlvmIrFunction function, string? comment = null) WriteFunctionParameters (function.Parameters, writeNames: true); Output.Write(") local_unnamed_addr "); if (attributes != null) { - Output.Write ($"#{function.AttributeSetID}"); + Output.Write ($"#{function.AttributeSetID.ToString (CultureInfo.InvariantCulture)}"); } Output.WriteLine (); Output.WriteLine ("{"); @@ -184,7 +185,7 @@ public void EmitStoreInstruction (LlvmIrFunction function, LlvmIrFunctionLocalVa CodeRenderType (source); Output.Write ($" %{source.Name}, "); CodeRenderType (destination); - Output.WriteLine ($"* {destination.Reference}, align {GetTypeSize (destination.Type)}"); + Output.WriteLine ($"* {destination.Reference}, align {GetTypeSize (destination.Type).ToString (CultureInfo.InvariantCulture)}"); } /// @@ -201,7 +202,7 @@ public LlvmIrFunctionLocalVariable EmitLoadInstruction (LlvmIrFunction function, string variableType = sb.ToString (); LlvmIrFunctionLocalVariable result = function.MakeLocalVariable (source, resultVariableName); - Output.WriteLine ($"{function.Indent}%{result.Name} = load {variableType}, {variableType}* @{source.Name}, align {PointerSize}"); + Output.WriteLine ($"{function.Indent}%{result.Name} = load {variableType}, {variableType}* @{source.Name}, align {PointerSize.ToString (CultureInfo.InvariantCulture)}"); return result; } @@ -378,7 +379,8 @@ public void EmitLabel (LlvmIrFunction function, string labelName) Output.Write ("nonnull "); } - Output.Write ($"align {PointerSize} dereferenceable({PointerSize}) "); + string ptrSize = PointerSize.ToString (CultureInfo.InvariantCulture); + Output.Write ($"align {ptrSize} dereferenceable({ptrSize}) "); if (argument.Value is LlvmIrVariableReference variableRef) { bool needBitcast = parameter.Type != argument.Type; @@ -409,7 +411,7 @@ public void EmitLabel (LlvmIrFunction function, string labelName) if (!FunctionAttributes.ContainsKey (AttributeSetID)) { throw new InvalidOperationException ($"Unknown attribute set ID {AttributeSetID}"); } - Output.Write ($" #{AttributeSetID}"); + Output.Write ($" #{AttributeSetID.ToString (CultureInfo.InvariantCulture)}"); } Output.WriteLine (); @@ -538,7 +540,7 @@ void WriteAttributeSets () void WriteSet (int id, TextWriter output) { - output.Write ($"attributes #{id} = {{ "); + output.Write ($"attributes #{id.ToString (CultureInfo.InvariantCulture)} = {{ "); foreach (LLVMFunctionAttribute attr in FunctionAttributes[id]) { output.Write (attr.Render ()); output.Write (' '); diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs index 2a35b407b67..469133189d6 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs @@ -1,6 +1,7 @@ using System; using System.Buffers; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Text; @@ -325,10 +326,14 @@ public string GetValue (T value) } var v = (LlvmNativeFunctionSignature)(object)value; - return v.FieldValue?.ToString () ?? v.ToString (); + if (v.FieldValue != null) { + return MonoAndroidHelper.CultureInvariantToString (v.FieldValue); + } + + return MonoAndroidHelper.CultureInvariantToString (v); } - return value?.ToString () ?? String.Empty; + return MonoAndroidHelper.CultureInvariantToString (value) ?? String.Empty; } /// @@ -410,7 +415,7 @@ void WriteGlobalSymbolStart (string symbolName, LlvmIrVariableOptions options, T output.Write (symbolName); output.Write (" = "); - var linkage = llvmLinkage [options.Linkage]; + string linkage = llvmLinkage [options.Linkage]; if (!string.IsNullOrEmpty (linkage)) { output.Write (linkage); output.Write (' '); @@ -473,18 +478,19 @@ bool MaybeWritePreAllocatedBuffer (StructureInfo info, StructureMemberInfo output = EnsureOutput (output); string irType = MapManagedTypeToIR (smi.MemberType); - string variableName = $"__{info.Name}_{smi.Info.Name}_{structBufferCounter++}"; + string variableName = $"__{info.Name}_{smi.Info.Name}_{structBufferCounter.ToString (CultureInfo.InvariantCulture)}"; + structBufferCounter++; WriteGlobalSymbolStart (variableName, preAllocatedBufferVariableOptions, output); ulong size = bufferSize * smi.BaseTypeSize; // WriteLine $"[{bufferSize} x {irType}] zeroinitializer, align {GetAggregateAlignment ((int)smi.BaseTypeSize, size)}" output.Write ('['); - output.Write (bufferSize); + output.Write (bufferSize.ToString (CultureInfo.InvariantCulture)); output.Write (" x "); output.Write (irType); output.Write ("] zeroinitializer, align "); - output.WriteLine (GetAggregateAlignment ((int) smi.BaseTypeSize, size)); + output.WriteLine (GetAggregateAlignment ((int) smi.BaseTypeSize, size).ToString (CultureInfo.InvariantCulture)); instance.AddPointerData (smi, variableName, size); return true; @@ -515,7 +521,7 @@ void WriteStructureArrayEnd (StructureInfo info, string? symbolName, ulong int alignment = isArrayOfPointers ? PointerSize : GetAggregateAlignment (info.MaxFieldAlignment, info.Size * count); output.Write (", align "); - output.Write (alignment); + output.Write (alignment.ToString (CultureInfo.InvariantCulture)); if (named && !skipFinalComment) { WriteEOL ($"end of '{symbolName!}' array", output); } else { @@ -532,7 +538,7 @@ public void WriteStructureArray (StructureInfo info, ulong count, LlvmIrVa // $"[{count} x %{info.NativeTypeDesignator}.{info.Name}{pointerAsterisk}] zeroinitializer" Output.Write ('['); - Output.Write (count); + Output.Write (count.ToString (CultureInfo.InvariantCulture)); Output.Write (" x %"); Output.Write (info.NativeTypeDesignator); Output.Write ('.'); @@ -566,7 +572,7 @@ public void WriteStructureArray (StructureInfo info, IList (StructureInfo info, IList (IList values, LlvmIrVariableOptions options, strin // WriteLine $"[{values.Count} x {elementType}] [" Output.Write ('['); - Output.Write (values.Count); + Output.Write (values.Count.ToString (CultureInfo.InvariantCulture)); Output.Write (" x "); Output.Write (elementType); Output.WriteLine ("] ["); @@ -673,7 +679,7 @@ public void WriteArray (IList values, LlvmIrVariableOptions options, strin Output.Write (elementType); Output.Write (' '); - Output.Write (values [i]); + Output.Write (MonoAndroidHelper.CultureInvariantToString (values [i])); if (!optimizeOutput) { bool last = i == values.Count - 1; @@ -702,7 +708,7 @@ public void WriteArray (IList values, LlvmIrVariableOptions options, strin Output.WriteLine (); Output.Write ("], align "); - Output.WriteLine (GetAggregateAlignment ((int) size, size * (ulong) values.Count)); + Output.WriteLine (GetAggregateAlignment ((int) size, size * (ulong) values.Count).ToString (CultureInfo.InvariantCulture)); } void AssertArraySize (StructureInfo info, StructureMemberInfo smi, ulong length, ulong expectedLength) @@ -832,7 +838,7 @@ void WritePrimitiveField (StructureInfo info, StructureMemberInfo smi, object? value = overrideValue ?? GetTypedMemberValue (info, smi, instance, smi.MemberType); output.Write (smi.IRType); output.Write (' '); - output.Write (value); + output.Write (MonoAndroidHelper.CultureInvariantToString (value)); } void WritePointer (StructureInfo info, StructureMemberInfo smi, StructureInstance instance, TextWriter output, object? overrideValue = null) @@ -1079,7 +1085,7 @@ StructureBodyWriterOptions InitStructureWrite (StructureInfo info, LlvmIrV void FinishStructureWrite (StructureInfo info, StructureBodyWriterOptions bodyWriterOptions) { bodyWriterOptions.StructureOutput.Write (", align "); - bodyWriterOptions.StructureOutput.WriteLine (info.MaxFieldAlignment); + bodyWriterOptions.StructureOutput.WriteLine (info.MaxFieldAlignment.ToString (CultureInfo.InvariantCulture)); WriteBufferToOutput (bodyWriterOptions.StringsOutput); WriteBufferToOutput (bodyWriterOptions.BuffersOutput); @@ -1161,13 +1167,14 @@ void WriteGetBufferPointer (string? variableName, string irType, ulong size, boo } // $"{irType} getelementptr inbounds ([{size} x {irBaseType}], [{size} x {irBaseType}]* @{variableName}, i32 0, i32 0)" + string sizeStr = size.ToString (CultureInfo.InvariantCulture); output.Write (irType); output.Write (" getelementptr inbounds (["); - output.Write (size); + output.Write (sizeStr); output.Write (" x "); output.Write (irBaseType); output.Write ("], ["); - output.Write (size); + output.Write (sizeStr); output.Write (" x "); output.Write (irBaseType); output.Write ("]* @"); @@ -1191,9 +1198,10 @@ public void WriteNameValueArray (string symbolName, IDictionary foreach (var kvp in arrayContents) { string name = kvp.Key; string value = kvp.Value; + string iStr = i.ToString (CultureInfo.InvariantCulture); - WriteArrayString (name, $"n_{i}"); - WriteArrayString (value, $"v_{i}"); + WriteArrayString (name, $"n_{iStr}"); + WriteArrayString (value, $"v_{iStr}"); i++; } @@ -1216,7 +1224,7 @@ void WriteStringArray (string symbolName, LlvmIrVariableOptions options, List 0) { @@ -1250,7 +1258,7 @@ void WriteStringArray (string symbolName, LlvmIrVariableOptions options, List @@ -1295,7 +1303,7 @@ public string WriteString (string value, LlvmIrVariableOptions options) { string name = $"@.str"; if (stringCounter > 0) { - name += $".{stringCounter}"; + name += $".{stringCounter.ToString (CultureInfo.InvariantCulture)}"; } stringCounter++; return WriteString (name, value, options); @@ -1345,13 +1353,14 @@ public string WriteString (string symbolName, string value, LlvmIrVariableOption // but global strings are actually pointers to local storage. WriteGlobalSymbolStart (strSymbolName, global ? LlvmIrVariableOptions.LocalConstexprString : options); + string stringSizeStr = stringSize.ToString (CultureInfo.InvariantCulture); // WriteLine $"[{stringSize} x i8] c{quotedString}, align {GetAggregateAlignment (1, stringSize)}" Output.Write ('['); - Output.Write (stringSize); + Output.Write (stringSizeStr); Output.Write (" x i8] c"); Output.Write (quotedString); Output.Write (", align "); - Output.WriteLine (GetAggregateAlignment (1, stringSize)); + Output.WriteLine (GetAggregateAlignment (1, stringSize).ToString (CultureInfo.InvariantCulture)); if (!global) { return symbolName; @@ -1362,9 +1371,9 @@ public string WriteString (string symbolName, string value, LlvmIrVariableOption // WriteLine $"i8* getelementptr inbounds ([{stringSize} x i8], [{stringSize} x i8]* @{strSymbolName}, {indexType} 0, {indexType} 0), align {GetAggregateAlignment (PointerSize, stringSize)}" Output.Write ("i8* getelementptr inbounds (["); - Output.Write (stringSize); + Output.Write (stringSizeStr); Output.Write (" x i8], ["); - Output.Write (stringSize); + Output.Write (stringSizeStr); Output.Write (" x i8]* @"); Output.Write (strSymbolName); Output.Write (", "); @@ -1372,7 +1381,7 @@ public string WriteString (string symbolName, string value, LlvmIrVariableOption Output.Write (" 0, "); Output.Write (indexType); Output.Write (" 0), align "); - Output.WriteLine (GetAggregateAlignment (PointerSize, stringSize)); + Output.WriteLine (GetAggregateAlignment (PointerSize, stringSize).ToString (CultureInfo.InvariantCulture)); return symbolName; } @@ -1404,7 +1413,9 @@ public StringSymbolInfo WriteUniqueString (string potentialSymbolNamePrefix, str return info; } - string newSymbolName = $"{potentialSymbolNamePrefix}.{counter++}"; + string newSymbolName = $"{potentialSymbolNamePrefix}.{counter.ToString (CultureInfo.InvariantCulture)}"; + counter++; + WriteString (newSymbolName, value, options, out ulong stringSize); info = new StringSymbolInfo (newSymbolName, stringSize); stringSymbolCache.Add (value, info); diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrMetadataManager.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrMetadataManager.cs index 543f5d5365a..b2d8bf7379a 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrMetadataManager.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrMetadataManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Text; namespace Xamarin.Android.Tasks.LLVMIR @@ -35,7 +36,7 @@ string FormatValue (object value) } string irType = LlvmIrGenerator.MapManagedTypeToIR (vt); - return $"{irType} {value}"; + return $"{irType} {MonoAndroidHelper.CultureInvariantToString (value)}"; } string QuoteString (string value) @@ -114,7 +115,7 @@ public LlvmIrMetadataItem Add (string name, params object[]? values) public LlvmIrMetadataItem AddNumbered (params object[]? values) { - string name = counter.ToString (); + string name = counter.ToString (CultureInfo.InvariantCulture); counter++; return Add (name, values); } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs index 179c78cb6b4..bbea9a58667 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Linq; using System.IO; using System.Reflection.Metadata; @@ -534,5 +535,14 @@ public static string GetRelativePathForAndroidAsset (string assetsDirectory, ITa path = head.Length == path.Length ? path : path.Substring ((head.Length == 0 ? 0 : head.Length + 1) + assetsDirectory.Length).TrimStart (DirectorySeparators); return path; } + + public static string? CultureInvariantToString (object? obj) + { + if (obj == null) { + return null; + } + + return Convert.ToString (obj, CultureInfo.InvariantCulture); + } } } From f7dde205e50f1be753a1f0d01c7931fc38fc1240 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 14 Apr 2023 13:00:07 +0200 Subject: [PATCH 2/3] Unit test --- .../Xamarin.Android.Build.Tests/BuildTest2.cs | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs index 754feee9dd9..1d0bd6eb803 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs @@ -105,14 +105,30 @@ public static string [] SupportedTargetFrameworks () } [Test] - public void BuildBasicApplication ([ValueSource (nameof (SupportedTargetFrameworks))] string tfv, [Values (true, false)] bool isRelease) + public void BuildBasicApplication ([ValueSource (nameof (SupportedTargetFrameworks))] string tfv, [Values (true, false)] bool isRelease, [Values ("", "en_US.UTF-8", "sv_SE.UTF-8")] string langEnvironmentVariable) { - var proj = new XamarinAndroidApplicationProject { - IsRelease = isRelease, - TargetFrameworkVersion = tfv, - }; - using (var b = CreateApkBuilder ()) { - Assert.IsTrue (b.Build (proj), "Build should have succeeded."); + string? oldLangVar = null; + + if (!String.IsNullOrEmpty (langEnvironmentVariable)) { + // See: https://github.com/xamarin/xamarin-android/pull/7941 + oldLangVar = Environment.GetEnvironmentVariable ("LANG"); + + // This will **not** call `setenv(3)`, but it should be fine since we're after the managed code here, not native + Environment.SetEnvironmentVariable ("LANG", langEnvironmentVariable); + } + + try { + var proj = new XamarinAndroidApplicationProject { + IsRelease = isRelease, + TargetFrameworkVersion = tfv, + }; + using (var b = CreateApkBuilder ()) { + Assert.IsTrue (b.Build (proj), "Build should have succeeded."); + } + } finally { + if (oldLangVar != null) { + Environment.SetEnvironmentVariable ("LANG", oldLangVar); + } } } From 20847f3b12edbd613ca94fc300a1e85144d5c79f Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 14 Apr 2023 21:08:35 +0200 Subject: [PATCH 3/3] Update the unit test --- .../Xamarin.Android.Build.Tests/BuildTest2.cs | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs index 1d0bd6eb803..ea864a93365 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs @@ -107,28 +107,20 @@ public static string [] SupportedTargetFrameworks () [Test] public void BuildBasicApplication ([ValueSource (nameof (SupportedTargetFrameworks))] string tfv, [Values (true, false)] bool isRelease, [Values ("", "en_US.UTF-8", "sv_SE.UTF-8")] string langEnvironmentVariable) { - string? oldLangVar = null; + var proj = new XamarinAndroidApplicationProject { + IsRelease = isRelease, + TargetFrameworkVersion = tfv, + }; + Dictionary envvar = null; if (!String.IsNullOrEmpty (langEnvironmentVariable)) { - // See: https://github.com/xamarin/xamarin-android/pull/7941 - oldLangVar = Environment.GetEnvironmentVariable ("LANG"); - - // This will **not** call `setenv(3)`, but it should be fine since we're after the managed code here, not native - Environment.SetEnvironmentVariable ("LANG", langEnvironmentVariable); + envvar = new Dictionary (StringComparer.OrdinalIgnoreCase) { + {"LANG", langEnvironmentVariable}, + }; } - try { - var proj = new XamarinAndroidApplicationProject { - IsRelease = isRelease, - TargetFrameworkVersion = tfv, - }; - using (var b = CreateApkBuilder ()) { - Assert.IsTrue (b.Build (proj), "Build should have succeeded."); - } - } finally { - if (oldLangVar != null) { - Environment.SetEnvironmentVariable ("LANG", oldLangVar); - } + using (var b = CreateApkBuilder ()) { + Assert.IsTrue (b.Build (proj, environmentVariables: envvar), "Build should have succeeded."); } }