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..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 @@ -105,14 +105,22 @@ 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, }; + + Dictionary envvar = null; + if (!String.IsNullOrEmpty (langEnvironmentVariable)) { + envvar = new Dictionary (StringComparer.OrdinalIgnoreCase) { + {"LANG", langEnvironmentVariable}, + }; + } + using (var b = CreateApkBuilder ()) { - Assert.IsTrue (b.Build (proj), "Build should have succeeded."); + Assert.IsTrue (b.Build (proj, environmentVariables: envvar), "Build should have succeeded."); } } 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); + } } }