void Run(DirectoryAssemblyResolver res) { PackageNamingPolicy pnp; JavaNativeTypeManager.PackageNamingPolicy = Enum.TryParse(PackageNamingPolicy, out pnp) ? pnp : PackageNamingPolicyEnum.LowercaseHash; var temp = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Directory.CreateDirectory(temp); foreach (var dir in FrameworkDirectories) { if (Directory.Exists(dir.ItemSpec)) { res.SearchDirectories.Add(dir.ItemSpec); } } var selectedWhitelistAssemblies = new List <string> (); // Put every assembly we'll need in the resolver foreach (var assembly in ResolvedAssemblies) { res.Load(Path.GetFullPath(assembly.ItemSpec)); if (MonoAndroidHelper.FrameworkAttributeLookupTargets.Any(a => Path.GetFileName(assembly.ItemSpec) == a)) { selectedWhitelistAssemblies.Add(Path.GetFullPath(assembly.ItemSpec)); } } // However we only want to look for JLO types in user code var assemblies = ResolvedUserAssemblies.Select(p => p.ItemSpec).ToList(); var fxAdditions = MonoAndroidHelper.GetFrameworkAssembliesToTreatAsUserAssemblies(ResolvedAssemblies) .Where(a => assemblies.All(x => Path.GetFileName(x) != Path.GetFileName(a))); assemblies = assemblies.Concat(fxAdditions).ToList(); // Step 1 - Find all the JLO types var scanner = new JavaTypeScanner(this.CreateTaskLogger()) { ErrorOnCustomJavaObject = ErrorOnCustomJavaObject, }; var all_java_types = scanner.GetJavaTypes(assemblies, res); WriteTypeMappings(all_java_types); var java_types = all_java_types.Where(t => !JavaTypeScanner.ShouldSkipJavaCallableWrapperGeneration(t)); // Step 2 - Generate Java stub code var keep_going = Generator.CreateJavaSources( Log, java_types, temp, ApplicationJavaClass, UseSharedRuntime, int.Parse(AndroidSdkPlatform) <= 10, ResolvedAssemblies.Any(assembly => Path.GetFileName(assembly.ItemSpec) == "Mono.Android.Export.dll")); var temp_map_file = Path.Combine(temp, "acw-map.temp"); // We need to save a map of .NET type -> ACW type for resource file fixups var managed = new Dictionary <string, TypeDefinition> (); var java = new Dictionary <string, TypeDefinition> (); var acw_map = new StreamWriter(temp_map_file); foreach (var type in java_types) { string managedKey = type.FullName.Replace('/', '.'); string javaKey = JavaNativeTypeManager.ToJniName(type).Replace('/', '.'); acw_map.WriteLine("{0};{1}", type.GetPartialAssemblyQualifiedName(), javaKey); TypeDefinition conflict; if (managed.TryGetValue(managedKey, out conflict)) { Log.LogWarning( "Duplicate managed type found! Mappings between managed types and Java types must be unique. " + "First Type: '{0}'; Second Type: '{1}'.", conflict.GetAssemblyQualifiedName(), type.GetAssemblyQualifiedName()); Log.LogWarning( "References to the type '{0}' will refer to '{1}'.", managedKey, conflict.GetAssemblyQualifiedName()); continue; } if (java.TryGetValue(javaKey, out conflict)) { Log.LogError( "Duplicate Java type found! Mappings between managed types and Java types must be unique. " + "First Type: '{0}'; Second Type: '{1}'", conflict.GetAssemblyQualifiedName(), type.GetAssemblyQualifiedName()); keep_going = false; continue; } managed.Add(managedKey, type); java.Add(javaKey, type); acw_map.WriteLine("{0};{1}", managedKey, javaKey); acw_map.WriteLine("{0};{1}", JavaNativeTypeManager.ToCompatJniName(type).Replace('/', '.'), javaKey); } acw_map.Close(); //The previous steps found an error, so we must abort and not generate any further output //We must do so subsequent unchanged builds fail too. if (!keep_going) { File.Delete(temp_map_file); return; } MonoAndroidHelper.CopyIfChanged(temp_map_file, AcwMapFile); try { File.Delete(temp_map_file); } catch (Exception) { } // Only overwrite files if the contents actually changed foreach (var file in Directory.GetFiles(temp, "*", SearchOption.AllDirectories)) { var dest = Path.GetFullPath(Path.Combine(OutputDirectory, "src", file.Substring(temp.Length + 1))); MonoAndroidHelper.CopyIfChanged(file, dest); } // Step 3 - Merge [Activity] and friends into AndroidManifest.xml var manifest = new ManifestDocument(ManifestTemplate, this.Log); manifest.PackageName = PackageName; manifest.ApplicationName = ApplicationName ?? PackageName; manifest.Placeholders = ManifestPlaceholders; manifest.Assemblies.AddRange(assemblies); manifest.Resolver = res; manifest.SdkDir = AndroidSdkDir; manifest.SdkVersion = AndroidSdkPlatform; manifest.Debug = Debug; manifest.NeedsInternet = NeedsInternet; var additionalProviders = manifest.Merge(all_java_types, selectedWhitelistAssemblies, ApplicationJavaClass, EmbedAssemblies, BundledWearApplicationName, MergedManifestDocuments); var temp_manifest = Path.Combine(temp, "AndroidManifest.xml"); var real_manifest = Path.GetFullPath(MergedAndroidManifestOutput); manifest.Save(temp_manifest); // Only write the new manifest if it actually changed MonoAndroidHelper.CopyIfChanged(temp_manifest, real_manifest); // Create additional runtime provider java sources. string providerTemplateFile = UseSharedRuntime ? "MonoRuntimeProvider.Shared.java" : "MonoRuntimeProvider.Bundled.java"; string providerTemplate = new StreamReader(typeof(JavaCallableWrapperGenerator).Assembly.GetManifestResourceStream(providerTemplateFile)).ReadToEnd(); foreach (var provider in additionalProviders) { var temp_provider = Path.Combine(temp, provider + ".java"); File.WriteAllText(temp_provider, providerTemplate.Replace("MonoRuntimeProvider", provider)); var real_provider_dir = Path.GetFullPath(Path.Combine(OutputDirectory, "src", "mono")); Directory.CreateDirectory(real_provider_dir); var real_provider = Path.Combine(real_provider_dir, provider + ".java"); MonoAndroidHelper.CopyIfChanged(temp_provider, real_provider); } // Create additional application java sources. Action <string, string, string, Func <string, string> > save = (resource, filename, destDir, applyTemplate) => { string temp_file = Path.Combine(temp, filename); string template = applyTemplate(new StreamReader(typeof(GenerateJavaStubs).Assembly.GetManifestResourceStream(resource)).ReadToEnd()); File.WriteAllText(temp_file, template); Directory.CreateDirectory(destDir); var real_file = Path.Combine(destDir, filename); MonoAndroidHelper.CopyIfChanged(temp_file, real_file); }; StringWriter regCallsWriter = new StringWriter(); regCallsWriter.WriteLine("\t\t// Application and Instrumentation ACWs must be registered first."); foreach (var type in java_types) { if (JavaNativeTypeManager.IsApplication(type) || JavaNativeTypeManager.IsInstrumentation(type)) { string javaKey = JavaNativeTypeManager.ToJniName(type).Replace('/', '.'); regCallsWriter.WriteLine("\t\tmono.android.Runtime.register (\"{0}\", {1}.class, {1}.__md_methods);", type.GetAssemblyQualifiedName(), javaKey); } } regCallsWriter.Close(); var real_app_dir = Path.GetFullPath(Path.Combine(OutputDirectory, "src", "mono", "android", "app")); string applicationTemplateFile = "ApplicationRegistration.java"; save(applicationTemplateFile, applicationTemplateFile, real_app_dir, template => template.Replace("// REGISTER_APPLICATION_AND_INSTRUMENTATION_CLASSES_HERE", regCallsWriter.ToString())); // Create NotifyTimeZoneChanges java sources. string notifyTimeZoneChangesFile = "NotifyTimeZoneChanges.java"; save(notifyTimeZoneChangesFile, notifyTimeZoneChangesFile, real_app_dir, template => template); // Delete our temp directory try { Directory.Delete(temp, true); } catch (Exception) { } }
public override bool Execute() { // if there are no assemblies, then we are done if (ResolvedAssemblies == null || ResolvedAssemblies.Length == 0) { Log.LogMessage($"There were no assemblies to check."); return(true); } var hasMissing = false; var assemblyNames = ResolvedAssemblies.Select(a => Path.GetFileNameWithoutExtension(a.ItemSpec)); var mapping = new AndroidXAssembliesCsvMapping(); var assemblyPairs = new Dictionary <string, string>(); var androidxAssemblies = new Dictionary <string, bool>(); foreach (var support in assemblyNames) { // if there was no mapping found, then we don't care if (!mapping.TryGetAndroidXAssembly(support, out var androidx)) { continue; } ContainsSupportAssemblies = true; Log.LogMessage(MessageImportance.Low, $"Making sure that the Android Support assembly '{support}' has a replacement Android X assembly..."); // make sure the mapped assembly is referenced var exists = assemblyNames.Contains(androidx); androidxAssemblies[androidx] = exists; assemblyPairs[androidx] = support; if (exists) { Log.LogMessage(MessageImportance.Low, $"Found the Android X assembly '{androidx}'."); } else { Log.LogMessage(MessageImportance.Low, $"Missing the Android X assembly '{androidx}'."); hasMissing = true; } } if (hasMissing) { var missing = androidxAssemblies.Where(p => !p.Value).Select(p => p.Key).ToArray(); var tree = PackageDependencyTree.Load(); var reduced = tree.Reduce(missing).ToArray(); var packages = new StringBuilder(); var references = new StringBuilder(); foreach (var assembly in reduced) { mapping.TryGetAndroidXPackage(assembly, out var package); mapping.TryGetAndroidXVersion(assembly, out var version); packages.AppendLine(); packages.Append($" - {package}"); references.AppendLine(); references.Append($" <PackageReference Include=\"{package}\" Version=\"{version}\" />"); } var msg = $"Could not find {missing.Length} Android X assemblies, make sure to install the following NuGet packages:" + packages + Environment.NewLine + $"You can also copy-and-paste the following snippet into your .csproj file:" + references; if (UseWarningsInsteadOfErrors) { Log.LogWarning(msg); } else { Log.LogError(msg); } } return(!hasMissing || UseWarningsInsteadOfErrors); }
bool Execute(DirectoryAssemblyResolver resolver) { Log.LogDebugMessage("ResolveAssemblies Task"); Log.LogDebugMessage(" ReferenceAssembliesDirectory: {0}", ReferenceAssembliesDirectory); Log.LogDebugMessage(" I18nAssemblies: {0}", I18nAssemblies); Log.LogDebugMessage(" LinkMode: {0}", LinkMode); Log.LogDebugTaskItems(" Assemblies:", Assemblies); foreach (var dir in ReferenceAssembliesDirectory.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { resolver.SearchDirectories.Add(dir); } var assemblies = new HashSet <string> (); var topAssemblyReferences = new List <AssemblyDefinition> (); try { foreach (var assembly in Assemblies) { var assembly_path = Path.GetDirectoryName(assembly.ItemSpec); if (!resolver.SearchDirectories.Contains(assembly_path)) { resolver.SearchDirectories.Add(assembly_path); } // Add each user assembly and all referenced assemblies (recursive) var assemblyDef = resolver.Load(assembly.ItemSpec); if (assemblyDef == null) { throw new InvalidOperationException("Failed to load assembly " + assembly.ItemSpec); } topAssemblyReferences.Add(assemblyDef); assemblies.Add(Path.GetFullPath(assemblyDef.MainModule.FullyQualifiedName)); } } catch (Exception ex) { Log.LogError("Exception while loading assemblies: {0}", ex); return(false); } try { foreach (var assembly in topAssemblyReferences) { AddAssemblyReferences(resolver, assemblies, assembly, true); } } catch (Exception ex) { Log.LogError("Exception while loading assemblies: {0}", ex); return(false); } // Add I18N assemblies if needed AddI18nAssemblies(resolver, assemblies); ResolvedAssemblies = assemblies.Select(a => new TaskItem(a)).ToArray(); ResolvedSymbols = assemblies.Select(a => a + ".mdb").Where(a => File.Exists(a)).Select(a => new TaskItem(a)).ToArray(); ResolvedFrameworkAssemblies = ResolvedAssemblies.Where(p => MonoAndroidHelper.IsFrameworkAssembly(p.ItemSpec, true)).ToArray(); ResolvedUserAssemblies = ResolvedAssemblies.Where(p => !MonoAndroidHelper.IsFrameworkAssembly(p.ItemSpec, true)).ToArray(); ResolvedDoNotPackageAttributes = do_not_package_atts.ToArray(); Log.LogDebugTaskItems(" [Output] ResolvedAssemblies:", ResolvedAssemblies); Log.LogDebugTaskItems(" [Output] ResolvedUserAssemblies:", ResolvedUserAssemblies); Log.LogDebugTaskItems(" [Output] ResolvedFrameworkAssemblies:", ResolvedFrameworkAssemblies); Log.LogDebugTaskItems(" [Output] ResolvedDoNotPackageAttributes:", ResolvedDoNotPackageAttributes); return(!Log.HasLoggedErrors); }
bool CreateJavaSources(IEnumerable <TypeDefinition> javaTypes, TypeDefinitionCache cache) { string outputPath = Path.Combine(OutputDirectory, "src"); string monoInit = GetMonoInitSource(AndroidSdkPlatform); bool hasExportReference = ResolvedAssemblies.Any(assembly => Path.GetFileName(assembly.ItemSpec) == "Mono.Android.Export.dll"); bool generateOnCreateOverrides = int.Parse(AndroidSdkPlatform) <= 10; bool ok = true; foreach (var t in javaTypes) { using (var writer = MemoryStreamPool.Shared.CreateStreamWriter()) { try { var jti = new JavaCallableWrapperGenerator(t, Log.LogWarning, cache) { GenerateOnCreateOverrides = generateOnCreateOverrides, ApplicationJavaClass = ApplicationJavaClass, MonoRuntimeInitialization = monoInit, }; jti.Generate(writer); writer.Flush(); var path = jti.GetDestinationPath(outputPath); MonoAndroidHelper.CopyIfStreamChanged(writer.BaseStream, path); if (jti.HasExport && !hasExportReference) { Diagnostic.Error(4210, Properties.Resources.XA4210); } } catch (XamarinAndroidException xae) { ok = false; Log.LogError( subcategory: "", errorCode: "XA" + xae.Code, helpKeyword: string.Empty, file: xae.SourceFile, lineNumber: xae.SourceLine, columnNumber: 0, endLineNumber: 0, endColumnNumber: 0, message: xae.MessageWithoutCode, messageArgs: new object [0] ); } catch (DirectoryNotFoundException ex) { ok = false; if (OS.IsWindows) { Diagnostic.Error(5301, Properties.Resources.XA5301, t.FullName, ex); } else { Diagnostic.Error(4209, Properties.Resources.XA4209, t.FullName, ex); } } catch (Exception ex) { ok = false; Diagnostic.Error(4209, Properties.Resources.XA4209, t.FullName, ex); } } } return(ok); }