More methods extracted in BindingTouch [Generator] (#19635)

Extracted a good amount of code from Main3 which should make it easier
to read. Tried to keep changes minimal.

---------

Co-authored-by: GitHub Actions Autoformatter <github-actions-autoformatter@xamarin.com>
This commit is contained in:
dustin-wojciechowski 2024-01-09 09:56:07 -08:00 коммит произвёл GitHub
Родитель fd1e7746a8
Коммит 448698a8b8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 188 добавлений и 139 удалений

Просмотреть файл

@ -137,123 +137,113 @@ public class BindingTouch : IDisposable {
return touch.Main3 (args);
}
public OptionSet CreateOptionSet (BindingTouchConfig config)
public bool TryCreateOptionSet (BindingTouchConfig config, string [] args)
{
return new OptionSet () {
{ "h|?|help", "Displays the help", v => config.ShowHelp = true },
{ "a", "Include alpha bindings (Obsolete).", v => {}, true },
{ "outdir=", "Sets the output directory for the temporary binding files", v => { config.BindingFilesOutputDirectory = v; }},
{ "o|out=", "Sets the name of the output library", v => outfile = v },
{ "tmpdir=", "Sets the working directory for temp files", v => { config.TemporaryFileDirectory = v; config.DeleteTemporaryFiles = false; }},
{ "debug", "Generates a debugging build of the binding", v => config.IsDebug = true },
{ "sourceonly=", "Only generates the source", v => config.GeneratedFileList = v },
{ "ns=", "Sets the namespace for storing helper classes", v => config.HelperClassNamespace = v },
{ "unsafe", "Sets the unsafe flag for the build", v=> config.IsUnsafe = true },
{ "core", "Use this to build product assemblies", v => BindThirdPartyLibrary = false },
{ "r|reference=", "Adds a reference", v => references.Add (v) },
{ "lib=", "Adds the directory to the search path for the compiler", v => LibraryManager.Libraries.Add (v) },
{ "compiler=", "Sets the compiler to use (Obsolete) ", v => compiler = v, true },
{ "compile-command=", "Sets the command to execute the C# compiler (this be an executable + arguments).", v =>
{
if (!StringUtils.TryParseArguments (v, out compile_command, out var ex))
throw ErrorHelper.CreateError (27, "--compile-command", ex);
}
},
{ "sdk=", "Sets the .NET SDK to use (Obsolete)", v => {}, true },
{ "new-style", "Build for Unified (Obsolete).", v => { Console.WriteLine ("The --new-style option is obsolete and ignored."); }, true},
{ "d=", "Defines a symbol", v => config.Defines.Add (v) },
{ "api=", "Adds a API definition source file", v => config.ApiSources.Add (v) },
{ "s=", "Adds a source file required to build the API", v => config.CoreSources.Add (v) },
{ "q", "Quiet", v => ErrorHelper.Verbosity-- },
{ "v", "Sets verbose mode", v => ErrorHelper.Verbosity++ },
{ "x=", "Adds the specified file to the build, used after the core files are compiled", v => config.ExtraSources.Add (v) },
{ "e", "Generates smaller classes that can not be subclassed (previously called 'external mode')", v => config.IsExternal = true },
{ "p", "Sets private mode", v => config.IsPublicMode = false },
{ "baselib=", "Sets the base library", v => config.Baselibdll = v },
{ "attributelib=", "Sets the attribute library", v => config.Attributedll = v },
{ "use-zero-copy", v=> config.UseZeroCopy = true },
{ "nostdlib", "Does not reference mscorlib.dll library", l => config.OmitStandardLibrary = true },
{ "no-mono-path", "Launches compiler with empty MONO_PATH", l => { }, true },
{ "native-exception-marshalling", "Enable the marshalling support for Objective-C exceptions", (v) => { /* no-op */} },
{ "inline-selectors:", "If Selector.GetHandle is inlined and does not need to be cached (enabled by default in Xamarin.iOS, disabled in Xamarin.Mac)",
v => config.InlineSelectors = string.Equals ("true", v, StringComparison.OrdinalIgnoreCase) || string.IsNullOrEmpty (v)
},
{ "process-enums", "Process enums as bindings, not external, types.", v => config.ProcessEnums = true },
{ "link-with=,", "Link with a native library {0:FILE} to the binding, embedded as a resource named {1:ID}",
(path, id) => {
if (path is null || path.Length == 0)
throw new Exception ("-link-with=FILE,ID requires a filename.");
if (id is null || id.Length == 0)
id = Path.GetFileName (path);
if (config.LinkWith.Contains (id))
throw new Exception ("-link-with=FILE,ID cannot assign the same resource id to multiple libraries.");
config.Resources.Add (string.Format ("-res:{0},{1}", path, id));
config.LinkWith.Add (id);
}
},
{ "unified-full-profile", "Launches compiler pointing to XM Full Profile", l => { /* no-op*/ }, true },
{ "unified-mobile-profile", "Launches compiler pointing to XM Mobile Profile", l => { /* no-op*/ }, true },
{ "target-framework=", "Specify target framework to use. Always required, and the currently supported values are: 'Xamarin.iOS,v1.0', 'Xamarin.TVOS,v1.0', 'Xamarin.WatchOS,v1.0', 'XamMac,v1.0', 'Xamarin.Mac,Version=v2.0,Profile=Mobile', 'Xamarin.Mac,Version=v4.5,Profile=Full' and 'Xamarin.Mac,Version=v4.5,Profile=System')", v => config.TargetFramework = v },
{ "warnaserror:", "An optional comma-separated list of warning codes that should be reported as errors (if no warnings are specified all warnings are reported as errors).", v => {
try {
if (!string.IsNullOrEmpty (v)) {
foreach (var code in v.Split (new char [] { ',' }, StringSplitOptions.RemoveEmptyEntries))
ErrorHelper.SetWarningLevel (ErrorHelper.WarningLevel.Error, int.Parse (code));
} else {
ErrorHelper.SetWarningLevel (ErrorHelper.WarningLevel.Error);
}
} catch (Exception ex) {
throw ErrorHelper.CreateError (26, ex.Message);
}
}
},
{ "nowarn:", "An optional comma-separated list of warning codes to ignore (if no warnings are specified all warnings are ignored).", v => {
try {
if (!string.IsNullOrEmpty (v)) {
foreach (var code in v.Split (new char [] { ',' }, StringSplitOptions.RemoveEmptyEntries))
ErrorHelper.SetWarningLevel (ErrorHelper.WarningLevel.Disable, int.Parse (code));
} else {
ErrorHelper.SetWarningLevel (ErrorHelper.WarningLevel.Disable);
}
} catch (Exception ex) {
throw ErrorHelper.CreateError (26, ex.Message);
}
}
},
{ "no-nfloat-using:", "If a global using alias directive for 'nfloat = System.Runtime.InteropServices.NFloat' should automatically be created.", (v) => {
noNFloatUsing = string.Equals ("true", v, StringComparison.OrdinalIgnoreCase) || string.IsNullOrEmpty (v);
}
},
{ "compiled-api-definition-assembly=", "An assembly with the compiled api definitions.", (v) => compiled_api_definition_assembly = v },
new Mono.Options.ResponseFileSource (),
};
}
int Main3 (string [] args)
{
ErrorHelper.ClearWarningLevels ();
BindingTouchConfig config = new ();
OptionSet os = CreateOptionSet (config);
try {
config.Sources = os.Parse (args);
config.OptionSet = new OptionSet () {
{ "h|?|help", "Displays the help", v => config.ShowHelp = true },
{ "a", "Include alpha bindings (Obsolete).", v => {}, true },
{ "outdir=", "Sets the output directory for the temporary binding files", v => { config.BindingFilesOutputDirectory = v; }},
{ "o|out=", "Sets the name of the output library", v => outfile = v },
{ "tmpdir=", "Sets the working directory for temp files", v => { config.TemporaryFileDirectory = v; config.DeleteTemporaryFiles = false; }},
{ "debug", "Generates a debugging build of the binding", v => config.IsDebug = true },
{ "sourceonly=", "Only generates the source", v => config.GeneratedFileList = v },
{ "ns=", "Sets the namespace for storing helper classes", v => config.HelperClassNamespace = v },
{ "unsafe", "Sets the unsafe flag for the build", v=> config.IsUnsafe = true },
{ "core", "Use this to build product assemblies", v => BindThirdPartyLibrary = false },
{ "r|reference=", "Adds a reference", v => references.Add (v) },
{ "lib=", "Adds the directory to the search path for the compiler", v => LibraryManager.Libraries.Add (v) },
{ "compiler=", "Sets the compiler to use (Obsolete) ", v => compiler = v, true },
{ "compile-command=", "Sets the command to execute the C# compiler (this be an executable + arguments).", v =>
{
if (!StringUtils.TryParseArguments (v, out compile_command, out var ex))
throw ErrorHelper.CreateError (27, "--compile-command", ex);
}
},
{ "sdk=", "Sets the .NET SDK to use (Obsolete)", v => {}, true },
{ "new-style", "Build for Unified (Obsolete).", v => { Console.WriteLine ("The --new-style option is obsolete and ignored."); }, true},
{ "d=", "Defines a symbol", v => config.Defines.Add (v) },
{ "api=", "Adds a API definition source file", v => config.ApiSources.Add (v) },
{ "s=", "Adds a source file required to build the API", v => config.CoreSources.Add (v) },
{ "q", "Quiet", v => ErrorHelper.Verbosity-- },
{ "v", "Sets verbose mode", v => ErrorHelper.Verbosity++ },
{ "x=", "Adds the specified file to the build, used after the core files are compiled", v => config.ExtraSources.Add (v) },
{ "e", "Generates smaller classes that can not be subclassed (previously called 'external mode')", v => config.IsExternal = true },
{ "p", "Sets private mode", v => config.IsPublicMode = false },
{ "baselib=", "Sets the base library", v => config.Baselibdll = v },
{ "attributelib=", "Sets the attribute library", v => config.Attributedll = v },
{ "use-zero-copy", v=> config.UseZeroCopy = true },
{ "nostdlib", "Does not reference mscorlib.dll library", l => config.OmitStandardLibrary = true },
{ "no-mono-path", "Launches compiler with empty MONO_PATH", l => { }, true },
{ "native-exception-marshalling", "Enable the marshalling support for Objective-C exceptions", (v) => { /* no-op */} },
{ "inline-selectors:", "If Selector.GetHandle is inlined and does not need to be cached (enabled by default in Xamarin.iOS, disabled in Xamarin.Mac)",
v => config.InlineSelectors = string.Equals ("true", v, StringComparison.OrdinalIgnoreCase) || string.IsNullOrEmpty (v)
},
{ "process-enums", "Process enums as bindings, not external, types.", v => config.ProcessEnums = true },
{ "link-with=,", "Link with a native library {0:FILE} to the binding, embedded as a resource named {1:ID}",
(path, id) => {
if (path is null || path.Length == 0)
throw new Exception ("-link-with=FILE,ID requires a filename.");
if (id is null || id.Length == 0)
id = Path.GetFileName (path);
if (config.LinkWith.Contains (id))
throw new Exception ("-link-with=FILE,ID cannot assign the same resource id to multiple libraries.");
config.Resources.Add (string.Format ("-res:{0},{1}", path, id));
config.LinkWith.Add (id);
}
},
{ "unified-full-profile", "Launches compiler pointing to XM Full Profile", l => { /* no-op*/ }, true },
{ "unified-mobile-profile", "Launches compiler pointing to XM Mobile Profile", l => { /* no-op*/ }, true },
{ "target-framework=", "Specify target framework to use. Always required, and the currently supported values are: 'Xamarin.iOS,v1.0', 'Xamarin.TVOS,v1.0', 'Xamarin.WatchOS,v1.0', 'XamMac,v1.0', 'Xamarin.Mac,Version=v2.0,Profile=Mobile', 'Xamarin.Mac,Version=v4.5,Profile=Full' and 'Xamarin.Mac,Version=v4.5,Profile=System')", v => config.TargetFramework = v },
{ "warnaserror:", "An optional comma-separated list of warning codes that should be reported as errors (if no warnings are specified all warnings are reported as errors).", v => {
try {
if (!string.IsNullOrEmpty (v)) {
foreach (var code in v.Split (new char [] { ',' }, StringSplitOptions.RemoveEmptyEntries))
ErrorHelper.SetWarningLevel (ErrorHelper.WarningLevel.Error, int.Parse (code));
} else {
ErrorHelper.SetWarningLevel (ErrorHelper.WarningLevel.Error);
}
} catch (Exception ex) {
throw ErrorHelper.CreateError (26, ex.Message);
}
}
},
{ "nowarn:", "An optional comma-separated list of warning codes to ignore (if no warnings are specified all warnings are ignored).", v => {
try {
if (!string.IsNullOrEmpty (v)) {
foreach (var code in v.Split (new char [] { ',' }, StringSplitOptions.RemoveEmptyEntries))
ErrorHelper.SetWarningLevel (ErrorHelper.WarningLevel.Disable, int.Parse (code));
} else {
ErrorHelper.SetWarningLevel (ErrorHelper.WarningLevel.Disable);
}
} catch (Exception ex) {
throw ErrorHelper.CreateError (26, ex.Message);
}
}
},
{ "no-nfloat-using:", "If a global using alias directive for 'nfloat = System.Runtime.InteropServices.NFloat' should automatically be created.", (v) => {
noNFloatUsing = string.Equals ("true", v, StringComparison.OrdinalIgnoreCase) || string.IsNullOrEmpty (v);
}
},
{ "compiled-api-definition-assembly=", "An assembly with the compiled api definitions.", (v) => compiled_api_definition_assembly = v },
new Mono.Options.ResponseFileSource (),
};
config.Sources = config.OptionSet.Parse (args);
} catch (Exception e) {
Console.Error.WriteLine ("{0}: {1}", ToolName, e.Message);
Console.Error.WriteLine ("see {0} --help for more information", ToolName);
return 1;
return false;
}
if (config.ShowHelp) {
ShowHelp (os);
return 0;
}
libraryInfo = LibraryInfo.LibraryInfoBuilder.Build (references, config);
CurrentPlatform = LibraryManager.DetermineCurrentPlatform (TargetFramework.Platform);
return true;
}
public bool TryInitializeApi (BindingTouchConfig config, [NotNullWhen (true)] out Api? api)
{
api = null;
if (config.Sources.Count > 0) {
config.ApiSources.Insert (0, config.Sources [0]);
for (int i = 1; i < config.Sources.Count; i++)
@ -262,8 +252,8 @@ public class BindingTouch : IDisposable {
if (config.ApiSources.Count == 0) {
Console.WriteLine ("Error: no api file provided");
ShowHelp (os);
return 1;
ShowHelp (config.OptionSet);
return false;
}
if (config.TemporaryFileDirectory is null)
@ -274,11 +264,13 @@ public class BindingTouch : IDisposable {
if (outfile is null)
outfile = firstApiDefinitionName + ".dll";
var refs = references.Select ((v) => "-r:" + v);
var paths = LibraryManager.Libraries.Select ((v) => "-lib:" + v);
config.References = references.Select ((v) => "-r:" + v);
config.Paths = LibraryManager.Libraries.Select ((v) => "-lib:" + v);
try {
var tmpass = GetCompiledApiBindingsAssembly (LibraryInfo, config.TemporaryFileDirectory, refs, LibraryInfo.OmitStandardLibrary, config.ApiSources, config.CoreSources, config.Defines, paths);
var tmpass = GetCompiledApiBindingsAssembly (LibraryInfo, config, config.TemporaryFileDirectory,
config.References, LibraryInfo.OmitStandardLibrary, config.ApiSources, config.CoreSources, config.Defines,
config.Paths);
universe = new MetadataLoadContext (
new SearchPathsAssemblyResolver (
LibraryManager.GetLibraryDirectories (LibraryInfo, CurrentPlatform).ToArray (),
@ -286,31 +278,22 @@ public class BindingTouch : IDisposable {
"mscorlib"
);
if (!TryLoadApi (tmpass, out Assembly? apiAssembly) || !TryLoadApi (LibraryInfo.BaseLibDll, out Assembly? baselib))
return 1;
if (!TryLoadApi (tmpass, out Assembly? apiAssembly) ||
!TryLoadApi (LibraryInfo.BaseLibDll, out Assembly? baselib))
return false;
Frameworks = new Frameworks (CurrentPlatform);
// Explicitly load our attribute library so that IKVM doesn't try (and fail) to find it.
universe.LoadFromAssemblyPath (LibraryManager.GetAttributeLibraryPath (LibraryInfo, CurrentPlatform));
typeCache ??= new (universe, Frameworks, CurrentPlatform, apiAssembly, universe.CoreAssembly, baselib, BindThirdPartyLibrary);
typeCache ??= new (universe, Frameworks, CurrentPlatform, apiAssembly, universe.CoreAssembly, baselib,
BindThirdPartyLibrary);
attributeManager ??= new (typeCache);
typeManager ??= new (this);
foreach (var linkWith in AttributeManager.GetCustomAttributes<LinkWithAttribute> (apiAssembly)) {
#if NET
if (string.IsNullOrEmpty (linkWith.LibraryName))
#else
if (linkWith.LibraryName is null || string.IsNullOrEmpty (linkWith.LibraryName))
#endif
continue;
if (!config.LinkWith.Contains (linkWith.LibraryName)) {
Console.Error.WriteLine ("Missing native library {0}, please use `--link-with' to specify the path to this library.", linkWith.LibraryName);
return 1;
}
}
if (!TestLinkWith (apiAssembly, config))
return false;
foreach (var r in references) {
// IKVM has a bug where it doesn't correctly compare assemblies, which means it
@ -334,13 +317,47 @@ public class BindingTouch : IDisposable {
}
}
var api = TypeManager.ParseApi (apiAssembly, config.ProcessEnums);
api = TypeManager.ParseApi (apiAssembly, config.ProcessEnums);
namespaceCache ??= new NamespaceCache (
CurrentPlatform,
config.HelperClassNamespace ?? firstApiDefinitionName,
LibraryManager.DetermineSkipSystemDrawing (LibraryInfo.TargetFramework)
);
} catch (Exception ex) {
ErrorHelper.Show (ex);
return false;
}
return true;
}
int Main3 (string [] args)
{
ErrorHelper.ClearWarningLevels ();
BindingTouchConfig config = new ();
if (!TryCreateOptionSet (config, args))
return 1;
if (config.ShowHelp) {
ShowHelp (config.OptionSet);
return 0;
}
libraryInfo = LibraryInfo.LibraryInfoBuilder.Build (references, config);
CurrentPlatform = LibraryManager.DetermineCurrentPlatform (TargetFramework.Platform);
if (!TryInitializeApi (config, out Api? api) || !TryGenerate (config, api))
return 1;
return 0;
}
bool TryGenerate (BindingTouchConfig config, Api api)
{
try {
var g = new Generator (this, api, config.IsPublicMode, config.IsExternal, config.IsDebug) {
BaseDir = config.BindingFilesOutputDirectory ?? config.TemporaryFileDirectory,
ZeroCopyStrings = config.UseZeroCopy,
@ -354,7 +371,7 @@ public class BindingTouch : IDisposable {
foreach (var x in g.GeneratedFiles.OrderBy ((v) => v))
f.WriteLine (x);
}
return 0;
return true;
}
var cargs = new List<string> ();
@ -370,7 +387,7 @@ public class BindingTouch : IDisposable {
cargs.AddRange (g.GeneratedFiles);
cargs.AddRange (config.CoreSources);
cargs.AddRange (config.ExtraSources);
cargs.AddRange (refs);
cargs.AddRange (config.References);
cargs.Add ("-r:" + LibraryInfo.BaseLibDll);
cargs.AddRange (config.Resources);
if (LibraryInfo.OmitStandardLibrary) {
@ -384,14 +401,36 @@ public class BindingTouch : IDisposable {
Compile (cargs, 1000, config.TemporaryFileDirectory);
} finally {
if (config.DeleteTemporaryFiles)
if (config.DeleteTemporaryFiles && config.TemporaryFileDirectory is not null)
Directory.Delete (config.TemporaryFileDirectory, true);
}
return 0;
return true;
}
bool TestLinkWith (Assembly apiAssembly, BindingTouchConfig config)
{
foreach (var linkWith in AttributeManager.GetCustomAttributes<LinkWithAttribute> (apiAssembly)) {
#if NET
if (string.IsNullOrEmpty (linkWith.LibraryName))
#else
if (linkWith.LibraryName is null || string.IsNullOrEmpty (linkWith.LibraryName))
#endif
continue;
if (!config.LinkWith.Contains (linkWith.LibraryName)) {
Console.Error.WriteLine (
"Missing native library {0}, please use `--link-with' to specify the path to this library.",
linkWith.LibraryName);
return false; // return 1;
}
}
return true;
}
// If anything is modified in this function, check if the _CompileApiDefinitions MSBuild target needs to be updated as well.
string GetCompiledApiBindingsAssembly (LibraryInfo libraryInfo, string tmpdir, IEnumerable<string> refs, bool nostdlib, List<string> api_sources, List<string> core_sources, List<string> defines, IEnumerable<string> paths)
string GetCompiledApiBindingsAssembly (LibraryInfo libraryInfo, BindingTouchConfig config, string tmpdir, IEnumerable<string> refs, bool nostdlib, List<string> api_sources, List<string> core_sources, List<string> defines, IEnumerable<string> paths)
{
if (!string.IsNullOrEmpty (compiled_api_definition_assembly))
return compiled_api_definition_assembly;
@ -408,7 +447,7 @@ public class BindingTouch : IDisposable {
cargs.Add ("-nowarn:436");
cargs.Add ("-out:" + tmpass);
cargs.Add ("-r:" + LibraryManager.GetAttributeLibraryPath (libraryInfo, CurrentPlatform));
cargs.AddRange (refs);
cargs.AddRange (config.References);
cargs.Add ("-r:" + libraryInfo.BaseLibDll);
foreach (var def in defines)
cargs.Add ("-define:" + def);
@ -432,8 +471,10 @@ public class BindingTouch : IDisposable {
return tmpass;
}
void AddNFloatUsing (List<string> cargs, string tmpdir)
void AddNFloatUsing (List<string> cargs, string? tmpdir)
{
if (tmpdir is null)
return;
#if NET
if (noNFloatUsing)
return;
@ -443,8 +484,11 @@ public class BindingTouch : IDisposable {
#endif
}
void Compile (List<string> arguments, int errorCode, string tmpdir)
void Compile (List<string> arguments, int errorCode, string? tmpdir)
{
if (tmpdir is null)
return;
var responseFile = Path.Combine (tmpdir, $"compile-{errorCode}.rsp");
// The /noconfig argument is not allowed in a response file, so don't put it there.
var responseFileArguments = arguments

Просмотреть файл

@ -1,4 +1,6 @@
using System.Collections.Generic;
using Mono.Options;
#nullable enable
public class BindingTouchConfig {
@ -26,4 +28,7 @@ public class BindingTouchConfig {
public string? TargetFramework = null;
public string? Baselibdll = null;
public string? Attributedll = null;
public IEnumerable<string> Paths = new List<string> ();
public IEnumerable<string> References = new List<string> ();
public OptionSet OptionSet = new ();
}