void UpdateWhenChanged(string path, string type, MemoryStream ms, Action <Stream> generator) { if (InstantRunEnabled) { ms.SetLength(0); generator(ms); MonoAndroidHelper.CopyIfStreamChanged(ms, path); } string dataFilePath = $"{path}.inc"; using (var stream = new NativeAssemblyDataStream()) { generator(stream); stream.EndOfFile(); MonoAndroidHelper.CopyIfStreamChanged(stream, dataFilePath); var generatedFiles = new List <ITaskItem> (); string mappingFieldName = $"{type}_typemap"; string dataFileName = Path.GetFileName(dataFilePath); NativeAssemblerTargetProvider asmTargetProvider; var utf8Encoding = new UTF8Encoding(false); foreach (string abi in SupportedAbis) { ms.SetLength(0); switch (abi.Trim()) { case "armeabi-v7a": asmTargetProvider = new ARMNativeAssemblerTargetProvider(is64Bit: false); break; case "arm64-v8a": asmTargetProvider = new ARMNativeAssemblerTargetProvider(is64Bit: true); break; case "x86": asmTargetProvider = new X86NativeAssemblerTargetProvider(is64Bit: false); break; case "x86_64": asmTargetProvider = new X86NativeAssemblerTargetProvider(is64Bit: true); break; default: throw new InvalidOperationException($"Unknown ABI {abi}"); } var asmgen = new TypeMappingNativeAssemblyGenerator(asmTargetProvider, stream, dataFileName, stream.MapByteCount, mappingFieldName); string asmFileName = $"{path}.{abi.Trim ()}.s"; using (var sw = new StreamWriter(ms, utf8Encoding, bufferSize: 8192, leaveOpen: true)) { asmgen.Write(sw, dataFileName); MonoAndroidHelper.CopyIfStreamChanged(ms, asmFileName); } } } }
void GenerateNativeAssembly(Func <NativeAssemblerTargetProvider, bool, bool, NativeAssemblyGenerator> getGenerator) { NativeAssemblerTargetProvider asmTargetProvider; bool sharedBitsWritten = false; bool sharedIncludeUsesAbiPrefix; foreach (string abi in supportedAbis) { sharedIncludeUsesAbiPrefix = false; switch (abi.Trim()) { case "armeabi-v7a": asmTargetProvider = new ARMNativeAssemblerTargetProvider(is64Bit: false); sharedIncludeUsesAbiPrefix = true; // ARMv7a is "special", it uses different directive prefix // than the others and the "shared" code won't build for it break; case "arm64-v8a": asmTargetProvider = new ARMNativeAssemblerTargetProvider(is64Bit: true); break; case "x86": asmTargetProvider = new X86NativeAssemblerTargetProvider(is64Bit: false); break; case "x86_64": asmTargetProvider = new X86NativeAssemblerTargetProvider(is64Bit: true); break; default: throw new InvalidOperationException($"Unknown ABI {abi}"); } NativeAssemblyGenerator generator = getGenerator(asmTargetProvider, sharedBitsWritten, sharedIncludeUsesAbiPrefix); using (var sw = MemoryStreamPool.Shared.CreateStreamWriter(outputEncoding)) { generator.Write(sw); sw.Flush(); Files.CopyIfStreamChanged(sw.BaseStream, generator.MainSourceFile); if (!sharedIncludeUsesAbiPrefix) { sharedBitsWritten = true; } } } }
public bool Generate(bool skipJniAddNativeMethodRegistrationAttributeScan, List <TypeDefinition> javaTypes, string outputDirectory, bool generateNativeAssembly, out ApplicationConfigTaskState appConfState) { if (String.IsNullOrEmpty(outputDirectory)) { throw new ArgumentException("must not be null or empty", nameof(outputDirectory)); } if (!Directory.Exists(outputDirectory)) { Directory.CreateDirectory(outputDirectory); } int assemblyId = 0; int maxJavaNameLength = 0; int maxModuleFileNameLength = 0; var knownAssemblies = new Dictionary <string, int> (StringComparer.Ordinal); var tempModules = new Dictionary <byte[], ModuleData> (); Dictionary <AssemblyDefinition, int> moduleCounter = null; var mvidCache = new Dictionary <Guid, byte[]> (); appConfState = new ApplicationConfigTaskState { JniAddNativeMethodRegistrationAttributePresent = skipJniAddNativeMethodRegistrationAttributeScan }; foreach (TypeDefinition td in javaTypes) { UpdateApplicationConfig(td, appConfState); string assemblyName = td.Module.Assembly.FullName; if (!knownAssemblies.ContainsKey(assemblyName)) { assemblyId++; knownAssemblies.Add(assemblyName, assemblyId); } // We must NOT use Guid here! The reason is that Guid sort order is different than its corresponding // byte array representation and on the runtime we need the latter in order to be able to binary search // through the module array. byte[] moduleUUID; if (!mvidCache.TryGetValue(td.Module.Mvid, out moduleUUID)) { moduleUUID = td.Module.Mvid.ToByteArray(); mvidCache.Add(td.Module.Mvid, moduleUUID); } ModuleData moduleData; if (!tempModules.TryGetValue(moduleUUID, out moduleData)) { if (moduleCounter == null) { moduleCounter = new Dictionary <AssemblyDefinition, int> (); } moduleData = new ModuleData { Mvid = td.Module.Mvid, MvidBytes = moduleUUID, Assembly = td.Module.Assembly, AssemblyName = td.Module.Assembly.Name.Name, TypesScratch = new Dictionary <string, TypeMapEntry> (StringComparer.Ordinal), DuplicateTypes = new Dictionary <uint, TypeMapEntry> (), }; tempModules.Add(moduleUUID, moduleData); if (!generateNativeAssembly) { int moduleNum; if (!moduleCounter.TryGetValue(moduleData.Assembly, out moduleNum)) { moduleNum = 0; moduleCounter [moduleData.Assembly] = 0; } else { moduleNum++; moduleCounter [moduleData.Assembly] = moduleNum; } string fileName = $"{moduleData.Assembly.Name.Name}.{moduleNum}.typemap"; moduleData.OutputFilePath = Path.Combine(outputDirectory, fileName); if (maxModuleFileNameLength < fileName.Length) { maxModuleFileNameLength = fileName.Length; } } } string javaName = Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager.ToJniName(td); var entry = new TypeMapEntry { JavaName = javaName, JavaNameLength = outputEncoding.GetByteCount(javaName), ManagedTypeName = td.FullName, Token = td.MetadataToken.ToUInt32(), AssemblyNameIndex = knownAssemblies [assemblyName] }; if (generateNativeAssembly) { if (entry.JavaNameLength > maxJavaNameLength) { maxJavaNameLength = entry.JavaNameLength; } } if (moduleData.TypesScratch.ContainsKey(entry.JavaName)) { // This is disabled because it costs a lot of time (around 150ms per standard XF Integration app // build) and has no value for the end user. The message is left here because it may be useful to us // in our devloop at some point. //logger ($"Warning: duplicate Java type name '{entry.JavaName}' in assembly '{moduleData.AssemblyName}' (new token: {entry.Token})."); moduleData.DuplicateTypes.Add(entry.Token, entry); } else { moduleData.TypesScratch.Add(entry.JavaName, entry); } } var modules = tempModules.Values.ToArray(); Array.Sort(modules, new ModuleUUIDArrayComparer()); var typeMapEntryComparer = new TypeMapEntryArrayComparer(); foreach (ModuleData module in modules) { if (module.TypesScratch.Count == 0) { module.Types = new TypeMapEntry[0]; continue; } module.Types = module.TypesScratch.Values.ToArray(); Array.Sort(module.Types, typeMapEntryComparer); } NativeTypeMappingData data; if (!generateNativeAssembly) { string typeMapIndexPath = Path.Combine(outputDirectory, "typemap.index"); using (var indexWriter = MemoryStreamPool.Shared.CreateBinaryWriter()) { OutputModules(modules, indexWriter, maxModuleFileNameLength + 1); indexWriter.Flush(); MonoAndroidHelper.CopyIfStreamChanged(indexWriter.BaseStream, typeMapIndexPath); } GeneratedBinaryTypeMaps.Add(typeMapIndexPath); data = new NativeTypeMappingData(logger, new ModuleData[0], 0); } else { data = new NativeTypeMappingData(logger, modules, maxJavaNameLength + 1); } NativeAssemblerTargetProvider asmTargetProvider; bool sharedBitsWritten = false; bool sharedIncludeUsesAbiPrefix; foreach (string abi in supportedAbis) { sharedIncludeUsesAbiPrefix = false; switch (abi.Trim()) { case "armeabi-v7a": asmTargetProvider = new ARMNativeAssemblerTargetProvider(is64Bit: false); sharedIncludeUsesAbiPrefix = true; // ARMv7a is "special", it uses different directive prefix // than the others and the "shared" code won't build for it break; case "arm64-v8a": asmTargetProvider = new ARMNativeAssemblerTargetProvider(is64Bit: true); break; case "x86": asmTargetProvider = new X86NativeAssemblerTargetProvider(is64Bit: false); break; case "x86_64": asmTargetProvider = new X86NativeAssemblerTargetProvider(is64Bit: true); break; default: throw new InvalidOperationException($"Unknown ABI {abi}"); } var generator = new TypeMappingNativeAssemblyGenerator(asmTargetProvider, data, Path.Combine(outputDirectory, "typemaps"), sharedBitsWritten, sharedIncludeUsesAbiPrefix); using (var sw = MemoryStreamPool.Shared.CreateStreamWriter(outputEncoding)) { generator.Write(sw); sw.Flush(); MonoAndroidHelper.CopyIfStreamChanged(sw.BaseStream, generator.MainSourceFile); if (!sharedIncludeUsesAbiPrefix) { sharedBitsWritten = true; } } } return(true); }
void AddEnvironment() { bool usesMonoAOT = false; bool usesAssemblyPreload = EnablePreloadAssembliesDefault; uint monoAOTMode = 0; string androidPackageName = null; var environmentVariables = new Dictionary <string, string> (StringComparer.Ordinal); var systemProperties = new Dictionary <string, string> (StringComparer.Ordinal); AotMode aotMode; if (AndroidAotMode != null && Aot.GetAndroidAotMode(AndroidAotMode, out aotMode)) { usesMonoAOT = true; monoAOTMode = (uint)aotMode; } bool haveLogLevel = false; bool haveMonoDebug = false; bool havebuildId = false; bool haveHttpMessageHandler = false; bool haveTlsProvider = false; bool haveMonoGCParams = false; SequencePointsMode sequencePointsMode; if (!Aot.TryGetSequencePointsMode(AndroidSequencePointsMode, out sequencePointsMode)) { sequencePointsMode = SequencePointsMode.None; } foreach (ITaskItem env in Environments ?? new TaskItem[0]) { foreach (string line in File.ReadLines(env.ItemSpec)) { var lineToWrite = line; if (lineToWrite.StartsWith("MONO_LOG_LEVEL=", StringComparison.Ordinal)) { haveLogLevel = true; } if (lineToWrite.StartsWith("MONO_GC_PARAMS=", StringComparison.Ordinal)) { haveMonoGCParams = true; } if (lineToWrite.StartsWith("XAMARIN_BUILD_ID=", StringComparison.Ordinal)) { havebuildId = true; } if (lineToWrite.StartsWith("MONO_DEBUG=", StringComparison.Ordinal)) { haveMonoDebug = true; if (sequencePointsMode != SequencePointsMode.None && !lineToWrite.Contains("gen-compact-seq-points")) { lineToWrite = line + ",gen-compact-seq-points"; } } if (lineToWrite.StartsWith("XA_HTTP_CLIENT_HANDLER_TYPE=", StringComparison.Ordinal)) { haveHttpMessageHandler = true; } if (lineToWrite.StartsWith("XA_TLS_PROVIDER=", StringComparison.Ordinal)) { haveTlsProvider = true; } if (lineToWrite.StartsWith("mono.enable_assembly_preload=", StringComparison.Ordinal)) { int idx = lineToWrite.IndexOf('='); uint val; if (idx < lineToWrite.Length - 1 && UInt32.TryParse(lineToWrite.Substring(idx + 1), out val)) { usesAssemblyPreload = idx == 1; } continue; } AddEnvironmentVariableLine(lineToWrite); } } if (_Debug && !haveLogLevel) { AddEnvironmentVariable(defaultLogLevel[0], defaultLogLevel[1]); } if (sequencePointsMode != SequencePointsMode.None && !haveMonoDebug) { AddEnvironmentVariable(defaultMonoDebug[0], defaultMonoDebug[1]); } if (!havebuildId) { AddEnvironmentVariable("XAMARIN_BUILD_ID", BuildId); } if (!haveHttpMessageHandler) { if (HttpClientHandlerType == null) { AddEnvironmentVariable(defaultHttpMessageHandler[0], defaultHttpMessageHandler[1]); } else { AddEnvironmentVariable("XA_HTTP_CLIENT_HANDLER_TYPE", HttpClientHandlerType.Trim()); } } if (!haveTlsProvider) { if (TlsProvider == null) { AddEnvironmentVariable(defaultTlsProvider[0], defaultTlsProvider[1]); } else { AddEnvironmentVariable("XA_TLS_PROVIDER", TlsProvider.Trim()); } } if (!haveMonoGCParams) { if (EnableSGenConcurrent) { AddEnvironmentVariable("MONO_GC_PARAMS", "major=marksweep-conc"); } else { AddEnvironmentVariable("MONO_GC_PARAMS", "major=marksweep"); } } using (var ms = new MemoryStream()) { var utf8Encoding = new UTF8Encoding(false); foreach (string abi in SupportedAbis) { ms.SetLength(0); NativeAssemblerTargetProvider asmTargetProvider; string asmFileName = Path.Combine(EnvironmentOutputDirectory, $"environment.{abi.ToLowerInvariant ()}.s"); switch (abi.Trim()) { case "armeabi-v7a": asmTargetProvider = new ARMNativeAssemblerTargetProvider(false); break; case "arm64-v8a": asmTargetProvider = new ARMNativeAssemblerTargetProvider(true); break; case "x86": asmTargetProvider = new X86NativeAssemblerTargetProvider(false); break; case "x86_64": asmTargetProvider = new X86NativeAssemblerTargetProvider(true); break; default: throw new InvalidOperationException($"Unknown ABI {abi}"); } var asmgen = new ApplicationConfigNativeAssemblyGenerator(asmTargetProvider, environmentVariables, systemProperties) { IsBundledApp = IsBundledApplication, UsesMonoAOT = usesMonoAOT, UsesMonoLLVM = EnableLLVM, UsesAssemblyPreload = usesAssemblyPreload, MonoAOTMode = monoAOTMode.ToString().ToLowerInvariant(), AndroidPackageName = AndroidPackageName, }; using (var sw = new StreamWriter(ms, utf8Encoding, bufferSize: 8192, leaveOpen: true)) { asmgen.Write(sw, asmFileName); MonoAndroidHelper.CopyIfStreamChanged(ms, asmFileName); } } } void AddEnvironmentVariable(string name, string value) { if (Char.IsUpper(name [0]) || !Char.IsLetter(name [0])) { environmentVariables [ValidAssemblerString(name)] = ValidAssemblerString(value); } else { systemProperties [ValidAssemblerString(name)] = ValidAssemblerString(value); } } void AddEnvironmentVariableLine(string l) { string line = l?.Trim(); if (String.IsNullOrEmpty(line) || line [0] == '#') { return; } string[] nv = line.Split(new char[] { '=' }, 2); AddEnvironmentVariable(nv[0].Trim(), nv.Length < 2 ? String.Empty : nv[1].Trim()); } string ValidAssemblerString(string s) { return(s.Replace("\"", "\\\"")); } }
void AddEnvironment() { bool usesMonoAOT = false; bool usesAssemblyPreload = EnablePreloadAssembliesDefault; bool brokenExceptionTransitions = false; var environmentVariables = new Dictionary <string, string> (StringComparer.Ordinal); var systemProperties = new Dictionary <string, string> (StringComparer.Ordinal); if (!Enum.TryParse(PackageNamingPolicy, out PackageNamingPolicy pnp)) { pnp = PackageNamingPolicyEnum.LowercaseCrc64; } AotMode aotMode = AotMode.None; if (!string.IsNullOrEmpty(AndroidAotMode) && Aot.GetAndroidAotMode(AndroidAotMode, out aotMode) && aotMode != AotMode.None) { usesMonoAOT = true; } bool haveLogLevel = false; bool haveMonoDebug = false; bool havebuildId = false; bool haveHttpMessageHandler = false; bool haveTlsProvider = false; bool haveMonoGCParams = false; SequencePointsMode sequencePointsMode; if (!Aot.TryGetSequencePointsMode(AndroidSequencePointsMode, out sequencePointsMode)) { sequencePointsMode = SequencePointsMode.None; } foreach (ITaskItem env in Environments ?? new TaskItem[0]) { foreach (string line in File.ReadLines(env.ItemSpec)) { var lineToWrite = line; if (lineToWrite.StartsWith("MONO_LOG_LEVEL=", StringComparison.Ordinal)) { haveLogLevel = true; } if (lineToWrite.StartsWith("MONO_GC_PARAMS=", StringComparison.Ordinal)) { haveMonoGCParams = true; } if (lineToWrite.StartsWith("XAMARIN_BUILD_ID=", StringComparison.Ordinal)) { havebuildId = true; } if (lineToWrite.StartsWith("MONO_DEBUG=", StringComparison.Ordinal)) { haveMonoDebug = true; if (sequencePointsMode != SequencePointsMode.None && !lineToWrite.Contains("gen-compact-seq-points")) { lineToWrite = line + ",gen-compact-seq-points"; } } if (lineToWrite.StartsWith("XA_HTTP_CLIENT_HANDLER_TYPE=", StringComparison.Ordinal)) { haveHttpMessageHandler = true; } if (lineToWrite.StartsWith("XA_TLS_PROVIDER=", StringComparison.Ordinal)) { haveTlsProvider = true; } if (lineToWrite.StartsWith("mono.enable_assembly_preload=", StringComparison.Ordinal)) { int idx = lineToWrite.IndexOf('='); uint val; if (idx < lineToWrite.Length - 1 && UInt32.TryParse(lineToWrite.Substring(idx + 1), out val)) { usesAssemblyPreload = idx == 1; } continue; } if (lineToWrite.StartsWith("XA_BROKEN_EXCEPTION_TRANSITIONS=")) { brokenExceptionTransitions = true; continue; } AddEnvironmentVariableLine(lineToWrite); } } if (_Debug && !haveLogLevel) { AddEnvironmentVariable(defaultLogLevel[0], defaultLogLevel[1]); } if (sequencePointsMode != SequencePointsMode.None && !haveMonoDebug) { AddEnvironmentVariable(defaultMonoDebug[0], defaultMonoDebug[1]); } if (!havebuildId) { AddEnvironmentVariable("XAMARIN_BUILD_ID", BuildId); } if (!haveHttpMessageHandler) { if (HttpClientHandlerType == null) { AddEnvironmentVariable(defaultHttpMessageHandler[0], defaultHttpMessageHandler[1]); } else { AddEnvironmentVariable("XA_HTTP_CLIENT_HANDLER_TYPE", HttpClientHandlerType.Trim()); } } if (!haveTlsProvider) { if (TlsProvider == null) { AddEnvironmentVariable(defaultTlsProvider[0], defaultTlsProvider[1]); } else { AddEnvironmentVariable("XA_TLS_PROVIDER", TlsProvider.Trim()); } } if (!haveMonoGCParams) { if (EnableSGenConcurrent) { AddEnvironmentVariable("MONO_GC_PARAMS", "major=marksweep-conc"); } else { AddEnvironmentVariable("MONO_GC_PARAMS", "major=marksweep"); } } global::Android.Runtime.BoundExceptionType boundExceptionType; if (String.IsNullOrEmpty(BoundExceptionType) || String.Compare(BoundExceptionType, "System", StringComparison.OrdinalIgnoreCase) == 0) { boundExceptionType = global::Android.Runtime.BoundExceptionType.System; } else if (String.Compare(BoundExceptionType, "Java", StringComparison.OrdinalIgnoreCase) == 0) { boundExceptionType = global::Android.Runtime.BoundExceptionType.Java; } else { throw new InvalidOperationException($"Unsupported BoundExceptionType value '{BoundExceptionType}'"); } foreach (string abi in SupportedAbis) { NativeAssemblerTargetProvider asmTargetProvider; string baseAsmFilePath = Path.Combine(EnvironmentOutputDirectory, $"environment.{abi.ToLowerInvariant ()}"); string asmFilePath = $"{baseAsmFilePath}.s"; switch (abi.Trim()) { case "armeabi-v7a": asmTargetProvider = new ARMNativeAssemblerTargetProvider(false); break; case "arm64-v8a": asmTargetProvider = new ARMNativeAssemblerTargetProvider(true); break; case "x86": asmTargetProvider = new X86NativeAssemblerTargetProvider(false); break; case "x86_64": asmTargetProvider = new X86NativeAssemblerTargetProvider(true); break; default: throw new InvalidOperationException($"Unknown ABI {abi}"); } var asmgen = new ApplicationConfigNativeAssemblyGenerator(asmTargetProvider, baseAsmFilePath, environmentVariables, systemProperties) { IsBundledApp = IsBundledApplication, UsesMonoAOT = usesMonoAOT, UsesMonoLLVM = EnableLLVM, UsesAssemblyPreload = usesAssemblyPreload, MonoAOTMode = aotMode.ToString().ToLowerInvariant(), AndroidPackageName = AndroidPackageName, BrokenExceptionTransitions = brokenExceptionTransitions, PackageNamingPolicy = pnp, BoundExceptionType = boundExceptionType, InstantRunEnabled = InstantRunEnabled, }; using (var sw = MemoryStreamPool.Shared.CreateStreamWriter()) { asmgen.Write(sw); sw.Flush(); MonoAndroidHelper.CopyIfStreamChanged(sw.BaseStream, asmFilePath); } } void AddEnvironmentVariable(string name, string value) { if (Char.IsUpper(name [0]) || !Char.IsLetter(name [0])) { environmentVariables [ValidAssemblerString(name)] = ValidAssemblerString(value); } else { systemProperties [ValidAssemblerString(name)] = ValidAssemblerString(value); } } void AddEnvironmentVariableLine(string l) { string line = l?.Trim(); if (String.IsNullOrEmpty(line) || line [0] == '#') { return; } string[] nv = line.Split(new char[] { '=' }, 2); AddEnvironmentVariable(nv[0].Trim(), nv.Length < 2 ? String.Empty : nv[1].Trim()); } string ValidAssemblerString(string s) { return(s.Replace("\"", "\\\"")); } }