public static Assembly Compile(Xamarin.Android.Binder.CodeGeneratorOptions options, string assemblyFileName, IEnumerable <string> AdditionalSourceDirectories, out string[] errors) { var generatedCodePath = options.ManagedCallableWrapperSourceOutputDirectory; var sourceFiles = Directory.EnumerateFiles(generatedCodePath, "*.cs", SearchOption.AllDirectories).ToList(); sourceFiles = sourceFiles.Select(x => Path.GetFullPath(x)).ToList(); var supportFiles = Directory.EnumerateFiles(Path.Combine(Path.GetDirectoryName(supportFilePath), "SupportFiles"), "*.cs", SearchOption.AllDirectories); sourceFiles.AddRange(supportFiles); foreach (var dir in AdditionalSourceDirectories) { var additonal = Directory.EnumerateFiles(dir, "*.cs", SearchOption.AllDirectories); sourceFiles.AddRange(additonal); } CompilerParameters parameters = new CompilerParameters(); parameters.GenerateExecutable = false; parameters.GenerateInMemory = true; parameters.CompilerOptions = "/unsafe"; parameters.OutputAssembly = assemblyFileName; parameters.ReferencedAssemblies.Add(unitTestFrameworkAssemblyPath); var binDir = Path.GetDirectoryName(typeof(BaseGeneratorTest).Assembly.Location); var facDir = GetFacadesPath(); parameters.ReferencedAssemblies.Add(Path.Combine(binDir, "Java.Interop.dll")); parameters.ReferencedAssemblies.Add(Path.Combine(facDir, "System.Runtime.dll")); #if DEBUG parameters.IncludeDebugInformation = true; #else parameters.IncludeDebugInformation = false; #endif CSharpCodeProvider codeProvider = new CSharpCodeProvider(); CompilerResults results = codeProvider.CompileAssemblyFromFile(parameters, sourceFiles.ToArray()); List <string> errs = new List <string> (); foreach (CompilerError message in results.Errors) { if (message.IsWarning) { continue; } errs.Add(message.ToString()); } errors = errs.ToArray(); return(results.CompiledAssembly); }
public static CodeGeneratorOptions Parse(string[] args) { var opts = new CodeGeneratorOptions(); bool show_help = false; var parser = new OptionSet { "Usage: generator.exe OPTIONS+ API_DESCRIPTION", "", "Generates C# source files to bind Java code described by API_DESCRIPTION.", "", "Copyright 2012 Xamarin, Inc.", "", "Options:", { "assembly=", "Fully Qualified Assembly Name ({FQAN}) of the eventual assembly (.dll) that will be built.", v => opts.AssemblyQualifiedName = v }, { "codegen-target=", "{STYLE} of Binding Assembly to generate", v => opts.CodeGenerationTarget = ParseCodeGenerationTarget(v) }, { "fixup=", "XML {FILE} controlling the generated API.\n" + "http://www.mono-project.com/GAPI#Altering_and_Extending_the_API_File", v => opts.FixupFiles.Add(v) }, { "global", "Prefix type names with `global::`.", v => opts.GlobalTypeNames = v != null }, { "javadir=", "Ignored; for compatibility.", v => {} }, { "L=", "{PATH} to look for referenced assemblies..", v => opts.LibraryPaths.Add(v) }, { "o|csdir=", "{DIRECTORY} to place C# source into.", v => opts.ManagedCallableWrapperSourceOutputDirectory = v }, { "public", "Obsolete option. It binds only public types now", v => opts.OnlyBindPublicTypes = v != null }, { "r|ref=", "{ASSEMBLY} to reference.", v => opts.AssemblyReferences.Add(v) }, { "sdk-platform|api-level=", "SDK Platform {VERSION}/API level.", v => opts.ApiLevel = v }, { "lang-features=", "For internal use. (Flags: interface-constants)", v => opts.SupportInterfaceConstants = v?.Contains("interface-constants") == true }, { "preserve-enums", "For internal use.", v => opts.PreserveEnums = v != null }, { "use-short-file-names", "Generates short file name.", v => opts.UseShortFileNames = v != null }, { "product-version=", "Xamarin.Android Major Product Version", (int?v) => opts.ProductVersion = v.HasValue ? v.Value : 0 }, { "v:", "Logging Verbosity", (int?v) => Report.Verbosity = v.HasValue ? v.Value : (Report.Verbosity ?? 0) + 1 }, { "type-map-report=", "Java-Managed Mapping report file.", v => opts.MappingReportFile = v }, { "only-xml-adjuster", "Run only API XML adjuster for class-parse input.", v => opts.OnlyRunApiXmlAdjuster = v != null }, { "xml-adjuster-output=", "specify API XML adjuster output XML for class-parse input.", v => opts.ApiXmlAdjusterOutput = v }, { "h|?|help", "Show this message and exit.", v => show_help = v != null }, "", "C# Enumeration Support:", { "enumdir=", "{DIRECTORY} to write enumeration declarations.", v => opts.EnumOutputDirectory = v }, { "enumfields=", "For internal use.", v => opts.EnumFieldsMapFile = v }, { "enumflags=", "For internal use.", v => opts.EnumFlagsFile = v }, { "enummetadata=", "XML {FILENAME} to create.", v => opts.EnumMetadataOutputFile = v }, { "enummethods=", "For internal use.", v => opts.EnumMethodsMapFile = v }, { "apiversions=", "For internal use.", v => opts.ApiVersionsXmlFile = v }, { "annotations=", "For internal use.", v => opts.AnnotationsZipFiles.Add(v) }, }; var apis = parser.Parse(args); if (args.Length < 2 || show_help) { parser.WriteOptionDescriptions(Console.Out); return(null); } if (apis.Count == 0) { throw new InvalidOperationException("A .xml file must be specified."); } if (apis.Count != 1) { Console.Error.WriteLine("generator: Found {0} API descriptions; only one is supported", apis.Count); foreach (var api in apis) { Console.Error.WriteLine("generator: API description: {0}", api); } throw new InvalidOperationException("Only one .xml file may be specified."); } opts.ApiDescriptionFile = apis [0]; return(opts); }
static void Run(CodeGeneratorOptions options, DirectoryAssemblyResolver resolver) { string assemblyQN = options.AssemblyQualifiedName; string api_level = options.ApiLevel; int product_version = options.ProductVersion; bool preserve_enums = options.PreserveEnums; string csdir = options.ManagedCallableWrapperSourceOutputDirectory ?? "cs"; string javadir = "java"; string enumdir = options.EnumOutputDirectory ?? "enum"; string enum_metadata = options.EnumMetadataOutputFile ?? "enummetadata"; var references = options.AssemblyReferences; string enum_fields_map = options.EnumFieldsMapFile; string enum_flags = options.EnumFlagsFile; string enum_methods_map = options.EnumMethodsMapFile; var fixups = options.FixupFiles; var annotations_zips = options.AnnotationsZipFiles; string filename = options.ApiDescriptionFile; string mapping_file = options.MappingReportFile; bool only_xml_adjuster = options.OnlyRunApiXmlAdjuster; string api_xml_adjuster_output = options.ApiXmlAdjusterOutput; var apiSource = ""; var opt = new CodeGenerationOptions() { CodeGenerationTarget = options.CodeGenerationTarget, UseGlobal = options.GlobalTypeNames, IgnoreNonPublicType = true, UseShortFileNames = options.UseShortFileNames, ProductVersion = options.ProductVersion, SupportInterfaceConstants = options.SupportInterfaceConstants, SupportDefaultInterfaceMethods = options.SupportDefaultInterfaceMethods, SupportNestedInterfaceTypes = options.SupportNestedInterfaceTypes, SupportNullableReferenceTypes = options.SupportNullableReferenceTypes, }; var resolverCache = new TypeDefinitionCache(); // Load reference libraries foreach (var lib in options.LibraryPaths) { resolver.SearchDirectories.Add(lib); } foreach (var reference in references) { resolver.SearchDirectories.Add(Path.GetDirectoryName(reference)); } // Figure out if this is class-parse string apiXmlFile = filename; string apiSourceAttr = null; using (var xr = XmlReader.Create(filename)) { xr.MoveToContent(); apiSourceAttr = xr.GetAttribute("api-source"); } // We don't use shallow referenced types with class-parse because the Adjuster process // enumerates every ctor/method/property/field to build its model, so we will need // every type to be fully populated. opt.UseShallowReferencedTypes = apiSourceAttr != "class-parse"; foreach (var reference in references.Distinct()) { try { Report.Verbose(0, "resolving assembly {0}.", reference); var assembly = resolver.Load(reference); foreach (var md in assembly.Modules) { foreach (var td in md.Types) { // FIXME: at some stage we want to import generic types. // For now generator fails to load generic types that have conflicting type e.g. // AdapterView`1 and AdapterView cannot co-exist. // It is mostly because generator primarily targets jar (no real generics land). var nonGenericOverload = td.HasGenericParameters ? md.GetType(td.FullName.Substring(0, td.FullName.IndexOf('`'))) : null; if (BindSameType(td, nonGenericOverload, resolverCache)) { continue; } ProcessReferencedType(td, opt); } } } catch (Exception ex) { Report.Warning(0, Report.WarningCodeGenerator + 0, ex, "failed to parse assembly {0}: {1}", reference, ex.Message); } } // For class-parse API description, transform it to jar2xml style. if (apiSourceAttr == "class-parse") { apiXmlFile = api_xml_adjuster_output ?? Path.Combine(Path.GetDirectoryName(filename), Path.GetFileName(filename) + ".adjusted"); new Adjuster().Process(filename, opt, opt.SymbolTable.AllRegisteredSymbols(opt).OfType <GenBase> ().ToArray(), apiXmlFile, Report.Verbosity ?? 0); } if (only_xml_adjuster) { return; } // load XML API definition with fixups. Dictionary <string, EnumMappings.EnumDescription> enums = null; EnumMappings enummap = null; if (enum_fields_map != null || enum_methods_map != null) { enummap = new EnumMappings(enumdir, enum_metadata, api_level, preserve_enums); enums = enummap.Process(enum_fields_map, enum_flags, enum_methods_map); fixups.Add(enum_metadata); } Parser p = new Parser(opt); List <GenBase> gens = p.Parse(apiXmlFile, fixups, api_level, product_version); if (gens == null) { return; } apiSource = p.ApiSource; // disable interface default methods here, especially before validation. gens = gens.Where(g => !g.IsObfuscated && g.Visibility != "private").ToList(); foreach (var gen in gens) { gen.StripNonBindables(opt); if (gen.IsGeneratable) { AddTypeToTable(opt, gen); } } // Apply fixups KotlinFixups.Fixup(gens); Validate(gens, opt, new CodeGeneratorContext()); foreach (var api_versions_xml in options.ApiVersionsXmlFiles) { ApiVersionsSupport.AssignApiLevels(gens, api_versions_xml); } foreach (GenBase gen in gens) { gen.FillProperties(); } var cache = new AncestorDescendantCache(gens); foreach (var gen in gens) { gen.UpdateEnums(opt, cache); } foreach (GenBase gen in gens) { gen.FixupMethodOverrides(opt); } foreach (GenBase gen in gens) { gen.FixupExplicitImplementation(); } SealedProtectedFixups.Fixup(gens); GenerateAnnotationAttributes(gens, annotations_zips); //SymbolTable.Dump (); GenerationInfo gen_info = new GenerationInfo(csdir, javadir, assemblyQN); opt.AssemblyName = gen_info.Assembly; if (mapping_file != null) { GenerateMappingReportFile(gens, mapping_file); } new NamespaceMapping(gens).Generate(opt, gen_info); foreach (IGeneratable gen in gens) { if (gen.IsGeneratable) { gen.Generate(opt, gen_info); } } ClassGen.GenerateTypeRegistrations(opt, gen_info); ClassGen.GenerateEnumList(gen_info); // Create the .cs files for the enums var enumFiles = enums == null ? null : enummap.WriteEnumerations(enumdir, enums, FlattenNestedTypes(gens).ToArray(), opt.UseShortFileNames); gen_info.GenerateLibraryProjectFile(options, enumFiles); }
public static CodeGeneratorOptions Parse(string[] args) { var opts = new CodeGeneratorOptions(); bool show_help = false; var parser = new OptionSet { "Usage: generator.exe OPTIONS+ API_DESCRIPTION [@RESPONSE-FILES]", "", "Generates C# source files to bind Java code described by API_DESCRIPTION.", "", "Copyright 2012 Xamarin, Inc.", "", "Options:", { "assembly=", "Fully Qualified Assembly Name ({FQAN}) of the eventual assembly (.dll) that will be built.", v => opts.AssemblyQualifiedName = v }, { "codegen-target=", "{STYLE} of Binding Assembly to generate", v => opts.CodeGenerationTarget = ParseCodeGenerationTarget(v) }, { "fixup=", "XML {FILE} controlling the generated API.\n" + "http://www.mono-project.com/GAPI#Altering_and_Extending_the_API_File", v => opts.FixupFiles.Add(v) }, { "global", "Prefix type names with `global::`.", v => opts.GlobalTypeNames = v != null }, { "javadir=", "Ignored; for compatibility.", v => {} }, { "L=", "{PATH} to look for referenced assemblies..", v => opts.LibraryPaths.Add(v) }, { "o|csdir=", "{DIRECTORY} to place C# source into.", v => opts.ManagedCallableWrapperSourceOutputDirectory = v }, { "public", "Obsolete option. It binds only public types now", v => opts.OnlyBindPublicTypes = v != null }, { "r|ref=", "{ASSEMBLY} to reference.", v => opts.AssemblyReferences.Add(v) }, { "sdk-platform|api-level=", "SDK Platform {VERSION}/API level.", v => opts.ApiLevel = v }, { "lang-features=", "For internal use. (Flags: interface-constants,default-interface-methods,nullable-reference-types)", v => { opts.SupportInterfaceConstants = v?.Contains("interface-constants") == true; opts.SupportDefaultInterfaceMethods = v?.Contains("default-interface-methods") == true; opts.SupportNestedInterfaceTypes = v?.Contains("nested-interface-types") == true; opts.SupportNullableReferenceTypes = v?.Contains("nullable-reference-types") == true; } }, { "preserve-enums", "For internal use.", v => opts.PreserveEnums = v != null }, { "use-short-file-names", "Generates short file name.", v => opts.UseShortFileNames = v != null }, { "product-version=", "Xamarin.Android Major Product Version", (int?v) => opts.ProductVersion = v.HasValue ? v.Value : 0 }, { "v:", "Logging Verbosity", (int?v) => Report.Verbosity = v.HasValue ? v.Value : (Report.Verbosity ?? 0) + 1 }, { "type-map-report=", "Java-Managed Mapping report file.", v => opts.MappingReportFile = v }, { "only-xml-adjuster", "Run only API XML adjuster for class-parse input.", v => opts.OnlyRunApiXmlAdjuster = v != null }, { "xml-adjuster-output=", "specify API XML adjuster output XML for class-parse input.", v => opts.ApiXmlAdjusterOutput = v }, { "h|?|help", "Show this message and exit.", v => show_help = v != null }, "", "Javadoc to C# Documentation Comments Support:", { "doc-comment-verbosity=", "{STYLE} of C# documentation comments to emit.\n" + "Defaults to `full`. {STYLE} may be:\n" + " * `intellisense`: emit <summary>, <param>,\n" + " <returns>, <exception>.\n" + " * `full`: plus <remarks>, <seealso>, ...", v => opts.XmldocStyle = ParseXmldocStyle(v) }, { "with-javadoc-xml=", "{PATH} to `api.xml` containing Javadoc docs in\n`<javadoc/>` elements", v => opts.JavadocXmlFiles.Add(v) }, "", "C# Enumeration Support:", { "enumdir=", "{DIRECTORY} to write enumeration declarations.", v => opts.EnumOutputDirectory = v }, { "enumfields=", "For internal use.", v => opts.EnumFieldsMapFile = v }, { "enumflags=", "For internal use.", v => opts.EnumFlagsFile = v }, { "enummetadata=", "XML {FILENAME} to create.", v => opts.EnumMetadataOutputFile = v }, { "enummethods=", "For internal use.", v => opts.EnumMethodsMapFile = v }, { "apiversions=", "For internal use.", v => opts.ApiVersionsXmlFiles.Add(v) }, { "annotations=", "For internal use.", v => opts.AnnotationsZipFiles.Add(v) }, { "use-legacy-java-resolver", "Uses the legacy ApiXmlAdjuster to resolve Java types, this is a *temporary* fallback in case there are unknown issues with JavaTypeSystem.", v => opts.UseLegacyJavaResolver = v != null }, new ResponseFileSource(), }; var apis = parser.Parse(args); if (args.Length < 2 || show_help) { parser.WriteOptionDescriptions(Console.Out); return(null); } if (apis.Count == 0) { throw new InvalidOperationException("A .xml file must be specified."); } if (apis.Count != 1) { Console.Error.WriteLine("generator: Found {0} API descriptions; only one is supported", apis.Count); foreach (var api in apis) { Console.Error.WriteLine("generator: API description: {0}", api); } throw new InvalidOperationException("Only one .xml file may be specified."); } opts.ApiDescriptionFile = apis [0]; if (opts.SupportDefaultInterfaceMethods && opts.CodeGenerationTarget == CodeGenerationTarget.XamarinAndroid) { Console.Error.WriteLine(Report.FormatCodedMessage(true, Report.ErrorInvalidDIMArgument)); return(null); } return(opts); }