X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Ftools%2Fmkbundle%2Fmkbundle.cs;h=8603736b26bb75cbbb90176beb7240c386a5df01;hb=56779410e8b632f7c3babd8a7734816cfdc1eba8;hp=cc891536b8af26f751fb8fa1aebc08c5a5a42487;hpb=2f1e6655e2c9a9dd28c0a8b14f26d3d6b1c720bd;p=mono.git diff --git a/mcs/tools/mkbundle/mkbundle.cs b/mcs/tools/mkbundle/mkbundle.cs index cc891536b8a..8603736b26b 100755 --- a/mcs/tools/mkbundle/mkbundle.cs +++ b/mcs/tools/mkbundle/mkbundle.cs @@ -3,6 +3,16 @@ // // Based on the `make-bundle' Perl script written by Paolo Molaro (lupus@debian.org) // +// TODO: +// [x] Rename the paths for the zip file that is downloaded +// [x] Update documentation with new flag +// [x] Load internationalized assemblies +// [x] Dependencies - if System.dll -> include Mono.Security.* (not needed, automatic) +// [x] --list-targets should download from a different url +// [x] --fetch-target should unpack zip file +// [x] Update --cross to use not a runtime, but an SDK +// [x] Update --local-targets to show the downloaded SDKs +// // Author: // Miguel de Icaza // @@ -39,6 +49,7 @@ class MakeBundle { static string machine_config_file = null; static string config_dir = null; static string style = "linux"; + static bool bundled_header = false; static string os_message = ""; static bool compress; static bool nomain; @@ -52,6 +63,8 @@ class MakeBundle { static bool custom_mode = true; static string embedded_options = null; static string runtime = null; + static string sdk_path = null; + static string lib_path = null; static Dictionary environment = new Dictionary(); static string [] i18n = new string [] { "West", @@ -74,7 +87,7 @@ class MakeBundle { link_paths.Add ("."); DetectOS (); - + for (int i = 0; i < top; i++){ switch (args [i]){ case "--help": case "-h": case "-?": @@ -121,6 +134,8 @@ class MakeBundle { Help (); return 1; } + if (sdk_path != null || runtime != null) + Error ("You can only specify one of --runtime, --sdk or --cross"); custom_mode = false; autodeps = true; cross_target = args [++i]; @@ -146,10 +161,8 @@ class MakeBundle { alias = lspec.Substring (0, p); path = lspec.Substring (p+1); } - if (!File.Exists (path)){ - Console.Error.WriteLine ($"The specified library file {path} does not exist"); - return 1; - } + if (!File.Exists (path)) + Error ($"The specified library file {path} does not exist"); libraries [alias] = path; break; @@ -162,10 +175,10 @@ class MakeBundle { break; case "--list-targets": + CommandLocalTargets (); var wc = new WebClient (); - var s = wc.DownloadString (new Uri (target_server + "target-list.txt")); - Console.WriteLine ("Cross-compilation targets available:\n" + s); - + var s = wc.DownloadString (new Uri (target_server + "target-sdks.txt")); + Console.WriteLine ("Targets available for download with --fetch-target:\n" + s); return 0; case "--target-server": @@ -191,11 +204,24 @@ class MakeBundle { } embedded_options = args [++i]; break; + case "--sdk": + if (i + 1 == top) { + Help (); + return 1; + } + custom_mode = false; + autodeps = true; + sdk_path = args [++i]; + if (cross_target != null || runtime != null) + Error ("You can only specify one of --runtime, --sdk or --cross"); + break; case "--runtime": if (i+1 == top){ Help (); return 1; } + if (sdk_path != null || cross_target != null) + Error ("You can only specify one of --runtime, --sdk or --cross"); custom_mode = false; autodeps = true; runtime = args [++i]; @@ -283,7 +309,7 @@ class MakeBundle { case "linux": break; default: - Console.Error.WriteLine ("Invalid style '{0}' - only 'windows', 'mac' and 'linux' are supported for --style argument", style); + Error ("Invalid style '{0}' - only 'windows', 'mac' and 'linux' are supported for --style argument", style); return 1; } @@ -322,27 +348,48 @@ class MakeBundle { else environment.Add (env.Substring (0, p), env.Substring (p+1)); break; + case "--bundled-header": + bundled_header = true; + break; default: sources.Add (args [i]); break; } } + // Modern bundling starts here + if (!custom_mode){ + if (runtime != null){ + // Nothing to do here, the user has chosen to manually specify --runtime nad libraries + } else if (sdk_path != null) { + VerifySdk (sdk_path); + } else if (cross_target == "default" || cross_target == null){ + sdk_path = Path.GetFullPath (Path.Combine (Process.GetCurrentProcess().MainModule.FileName, "..", "..")); + VerifySdk (sdk_path); + } else { + sdk_path = Path.Combine (targets_dir, cross_target); + Console.WriteLine ("From: " + sdk_path); + VerifySdk (sdk_path); + } + } if (fetch_target != null){ - var truntime = Path.Combine (targets_dir, fetch_target, "mono"); - Directory.CreateDirectory (Path.GetDirectoryName (truntime)); + var directory = Path.Combine (targets_dir, fetch_target); + var zip_download = Path.Combine (directory, "sdk.zip"); + Directory.CreateDirectory (directory); var wc = new WebClient (); var uri = new Uri ($"{target_server}{fetch_target}"); try { if (!quiet){ - Console.WriteLine ($"Downloading runtime {uri} to {truntime}"); + Console.WriteLine ($"Downloading runtime {uri} to {zip_download}"); } - wc.DownloadFile (uri, truntime); + wc.DownloadFile (uri, zip_download); + ZipFile.ExtractToDirectory(zip_download, directory); + File.Delete (zip_download); } catch { Console.Error.WriteLine ($"Failure to download the specified runtime from {uri}"); - File.Delete (truntime); + File.Delete (zip_download); return 1; } return 0; @@ -359,48 +406,41 @@ class MakeBundle { } List assemblies = LoadAssemblies (sources); + LoadLocalizedAssemblies (assemblies); List files = new List (); foreach (string file in assemblies) if (!QueueAssembly (files, file)) return 1; if (custom_mode) GenerateBundles (files); - else { - if (cross_target == "default") - runtime = null; - else { - if (runtime == null){ - if (cross_target == null){ - Console.Error.WriteLine ("you should specify either a --runtime or a --cross compilation target"); - Environment.Exit (1); - } - runtime = Path.Combine (targets_dir, cross_target, "mono"); - if (!File.Exists (runtime)){ - Console.Error.WriteLine ($"The runtime for the {cross_target} does not exist, use --fetch-target {cross_target} to download first"); - return 1; - } - } else { - if (!File.Exists (runtime)){ - Console.Error.WriteLine ($"The Mono runtime specified with --runtime does not exist"); - return 1; - } - } - - Console.WriteLine ("Using runtime {0} for {1}", runtime, output); - } + else GeneratePackage (files); - } - + + Console.WriteLine ("Generated {0}", output); + return 0; } + static void VerifySdk (string path) + { + if (!Directory.Exists (path)) + Error ($"The specified SDK path does not exist: {path}"); + runtime = Path.Combine (sdk_path, "bin", "mono"); + if (!File.Exists (runtime)) + Error ($"The SDK location does not contain a {path}/bin/mono runtime"); + lib_path = Path.Combine (path, "lib", "mono", "4.5"); + if (!Directory.Exists (lib_path)) + Error ($"The SDK location does not contain a {path}/lib/mono/4.5 directory"); + link_paths.Add (lib_path); + } + static string targets_dir = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal), ".mono", "targets"); static void CommandLocalTargets () { string [] targets; - Console.WriteLine ("Available targets:"); + Console.WriteLine ("Available targets locally:"); Console.WriteLine ("\tdefault\t- Current System Mono"); try { targets = Directory.GetDirectories (targets_dir); @@ -408,7 +448,7 @@ class MakeBundle { return; } foreach (var target in targets){ - var p = Path.Combine (target, "mono"); + var p = Path.Combine (target, "bin", "mono"); if (File.Exists (p)) Console.WriteLine ("\t{0}", Path.GetFileName (target)); } @@ -437,12 +477,18 @@ class MakeBundle { name, size); break; case "windows": + string mangled_symbol_name = ""; + if (Target64BitApplication()) + mangled_symbol_name = name; + else + mangled_symbol_name = "_" + name; + sw.WriteLine ( - ".globl _{0}\n" + + ".globl {0}\n" + "\t.section .rdata,\"dr\"\n" + "\t.align 32\n" + - "_{0}:\n", - name, size); + "{0}:\n", + mangled_symbol_name); break; } } @@ -580,10 +626,12 @@ class MakeBundle { return true; if (!File.Exists (file)){ - Console.Error.WriteLine ("The file {0} does not exist", file); + Error ("The file {0} does not exist", file); return false; } maker.Add (code, file); + // add a space after code (="systemconfig:" or "machineconfig:") + Console.WriteLine (code + " " + file); return true; } @@ -593,21 +641,22 @@ class MakeBundle { if (IsUnix) runtime = Process.GetCurrentProcess().MainModule.FileName; else { - Console.Error.WriteLine ("You must specify at least one runtime with --runtime or --cross"); + Error ("You must specify at least one runtime with --runtime or --cross"); Environment.Exit (1); } } if (!File.Exists (runtime)){ - Console.Error.WriteLine ($"The specified runtime at {runtime} does not exist"); + Error ($"The specified runtime at {runtime} does not exist"); Environment.Exit (1); } if (ctor_func != null){ - Console.Error.WriteLine ("--static-ctor not supported with package bundling, you must use native compilation for this"); + Error ("--static-ctor not supported with package bundling, you must use native compilation for this"); return false; } var maker = new PackageMaker (output); + Console.WriteLine ("Using runtime: " + runtime); maker.AddFile (runtime); foreach (var url in files){ @@ -615,14 +664,20 @@ class MakeBundle { string aname = MakeBundle.GetAssemblyName (fname); maker.Add ("assembly:" + aname, fname); - if (File.Exists (fname + ".config")) + Console.WriteLine (" Assembly: " + fname); + if (File.Exists (fname + ".config")){ maker.Add ("config:" + aname, fname + ".config"); + Console.WriteLine (" Config: " + fname + ".config"); + } } + if (!MaybeAddFile (maker, "systemconfig:", config_file) || !MaybeAddFile (maker, "machineconfig:", machine_config_file)) return false; - if (config_dir != null) + if (config_dir != null){ maker.Add ("config_dir:", config_dir); + Console.WriteLine (" Config_dir: " + config_dir ); + } if (embedded_options != null) maker.AddString ("options:", embedded_options); if (environment.Count > 0){ @@ -631,6 +686,7 @@ class MakeBundle { } if (libraries.Count > 0){ foreach (var alias_and_path in libraries){ + Console.WriteLine (" Library: " + alias_and_path.Value); maker.Add ("library:" + alias_and_path.Key, alias_and_path.Value); } } @@ -643,7 +699,7 @@ class MakeBundle { { string temp_s = "temp.s"; // Path.GetTempFileName (); string temp_c = "temp.c"; - string temp_o = "temp.o"; + string temp_o = (style != "windows") ? "temp.o" : "temp.s.obj"; if (compile_only) temp_c = output; @@ -658,10 +714,10 @@ class MakeBundle { using (StreamWriter tc = new StreamWriter (File.Create (temp_c))) { string prog = null; -#if XAMARIN_ANDROID - tc.WriteLine ("/* This source code was produced by mkbundle, do not edit */"); - tc.WriteLine ("\n#ifndef NULL\n#define NULL (void *)0\n#endif"); - tc.WriteLine (@" + if (bundled_header) { + tc.WriteLine ("/* This source code was produced by mkbundle, do not edit */"); + tc.WriteLine ("\n#ifndef NULL\n#define NULL (void *)0\n#endif"); + tc.WriteLine (@" typedef struct { const char *name; const unsigned char *data; @@ -670,10 +726,10 @@ typedef struct { void mono_register_bundled_assemblies (const MonoBundledAssembly **assemblies); void mono_register_config_for_assembly (const char* assembly_name, const char* config_xml); "); -#else - tc.WriteLine ("#include "); - tc.WriteLine ("#include \n"); -#endif + } else { + tc.WriteLine ("#include "); + tc.WriteLine ("#include \n"); + } if (compress) { tc.WriteLine ("typedef struct _compressed_data {"); @@ -780,7 +836,7 @@ void mono_register_config_for_assembly (const char* assembly_name, cons try { conf = File.OpenRead (config_file); } catch { - Error (String.Format ("Failure to open {0}", config_file)); + Error ("Failure to open {0}", config_file); return; } if (!quiet) @@ -799,7 +855,7 @@ void mono_register_config_for_assembly (const char* assembly_name, cons try { conf = File.OpenRead (machine_config_file); } catch { - Error (String.Format ("Failure to open {0}", machine_config_file)); + Error ("Failure to open {0}", machine_config_file); return; } if (!quiet) @@ -865,8 +921,7 @@ void mono_register_config_for_assembly (const char* assembly_name, cons tc.Close (); - string assembler = GetEnv("AS", "as"); - string as_cmd = String.Format("{0} -o {1} {2} ", assembler, temp_o, temp_s); + string as_cmd = GetAssemblerCommand (temp_s, temp_o); Execute(as_cmd); if (compile_only) @@ -875,90 +930,40 @@ void mono_register_config_for_assembly (const char* assembly_name, cons if (!quiet) Console.WriteLine("Compiling:"); - if (style == "windows") - { - - Func quote = (pp) => { return "\"" + pp + "\""; }; - - string compiler = GetEnv("CC", "cl.exe"); - string winsdkPath = GetEnv("WINSDK", @"C:\Program Files (x86)\Windows Kits\8.1"); - string vsPath = GetEnv("VSINCLUDE", @"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC"); - string monoPath = GetEnv("MONOPREFIX", @"C:\Program Files (x86)\Mono"); - - string[] includes = new string[] {winsdkPath + @"\Include\um", winsdkPath + @"\Include\shared", vsPath + @"\include", monoPath + @"\include\mono-2.0", "." }; - // string[] libs = new string[] { winsdkPath + @"\Lib\winv6.3\um\x86" , vsPath + @"\lib" }; - var linkLibraries = new string[] { "kernel32.lib", - "version.lib", - "Ws2_32.lib", - "Mswsock.lib", - "Psapi.lib", - "shell32.lib", - "OleAut32.lib", - "ole32.lib", - "winmm.lib", - "user32.lib", - "libvcruntime.lib", - "advapi32.lib", - "OLDNAMES.lib", - "libucrt.lib" }; - - string glue_obj = "mkbundle_glue.obj"; - string monoLib; - - if (static_link) - monoLib = LocateFile (monoPath + @"\lib\monosgen-2.0-static.lib"); - - else { - Console.WriteLine ("WARNING: Dynamically linking the Mono runtime on Windows is not a tested option."); - monoLib = LocateFile (monoPath + @"\lib\monosgen-2.0.lib"); - LocateFile (monoPath + @"\lib\monosgen-2.0.dll"); // in this case, the .lib is just the import library, and the .dll is also needed + if (style == "windows") { + ToolchainProgram compiler = GetCCompiler (); + if (compiler != null && compiler.ParentSDK != null) { + if (compiler.IsVSToolChain && VisualStudioSDKHelper.GetInstance ().IsVisualStudio12 (compiler.ParentSDK) && static_link) + Console.WriteLine ( @"Warning: Static linking the Mono runtime on Windows using VC " + + @"12.0, won't support static Mono runtime library distributed in Mono SDK since it has " + + @"been build using different C-runtime version. If build results in " + + @"link errors related to C-runtime functions, please rebuild mono runtime distribution " + + @"using targeted VC compiler and linker and rerun using MONOPREFIX."); } - var compilerArgs = new List(); - compilerArgs.Add("/MT"); - - foreach (string include in includes) - compilerArgs.Add(String.Format ("/I {0}", quote (include))); - + bool staticLinkCRuntime = GetEnv ("VCCRT", "MD") != "MD"; if (!nomain || custom_main != null) { - compilerArgs.Add(quote(temp_c)); - compilerArgs.Add(quote(temp_o)); - if (custom_main != null) - compilerArgs.Add(quote(custom_main)); - compilerArgs.Add(quote(monoLib)); - compilerArgs.Add("/link"); - compilerArgs.Add("/NODEFAULTLIB"); - compilerArgs.Add("/SUBSYSTEM:windows"); - compilerArgs.Add("/ENTRY:mainCRTStartup"); - compilerArgs.AddRange(linkLibraries); - compilerArgs.Add("/out:"+ output); - - string cl_cmd = String.Format("{0} {1}", compiler, String.Join(" ", compilerArgs.ToArray())); - Execute (cl_cmd); - } - else - { - // we are just creating a .lib - compilerArgs.Add("/c"); // compile only - compilerArgs.Add(temp_c); - compilerArgs.Add(String.Format("/Fo" + glue_obj)); // .obj output name - - string cl_cmd = String.Format("{0} {1}", compiler, String.Join(" ", compilerArgs.ToArray())); + string cl_cmd = GetCompileAndLinkCommand (compiler, temp_c, temp_o, custom_main, static_link, staticLinkCRuntime, output); Execute (cl_cmd); + } else { + string temp_c_o = ""; + try { + string cl_cmd = GetLibrarianCompilerCommand (compiler, temp_c, static_link, staticLinkCRuntime, out temp_c_o); + Execute(cl_cmd); + + ToolchainProgram librarian = GetLibrarian (); + string lib_cmd = GetLibrarianLinkerCommand (librarian, new string[] { temp_o, temp_c_o }, static_link, staticLinkCRuntime, output); + Execute (lib_cmd); + } finally { + File.Delete (temp_c_o); + } - string librarian = GetEnv ("LIB", "lib.exe"); - var librarianArgs = new List (); - librarianArgs.Add (String.Format ("/out:{0}.lib" + output)); - librarianArgs.Add (temp_o); - librarianArgs.Add (glue_obj); - librarianArgs.Add (monoLib); - string lib_cmd = String.Format("{0} {1}", librarian, String.Join(" ", librarianArgs.ToArray())); - Execute (lib_cmd); } } else { string zlib = (compress ? "-lz" : ""); + string objc = (style == "osx" ? "-framework CoreFoundation -lobjc" : ""); string debugging = "-g"; string cc = GetEnv("CC", "cc"); string cmd = null; @@ -972,16 +977,16 @@ void mono_register_config_for_assembly (const char* assembly_name, cons smonolib = "`pkg-config --variable=libdir mono-2`/libmono-2.0.a "; else smonolib = "-Wl,-Bstatic -lmono-2.0 -Wl,-Bdynamic "; - cmd = String.Format("{4} -o '{2}' -Wall `pkg-config --cflags mono-2` {0} {3} " + + cmd = String.Format("{4} -o '{2}' -Wall {5} `pkg-config --cflags mono-2` {0} {3} " + "`pkg-config --libs-only-L mono-2` " + smonolib + "`pkg-config --libs-only-l mono-2 | sed -e \"s/\\-lmono-2.0 //\"` {1}", - temp_c, temp_o, output, zlib, cc); + temp_c, temp_o, output, zlib, cc, objc); } else { - cmd = String.Format("{4} " + debugging + " -o '{2}' -Wall {0} `pkg-config --cflags --libs mono-2` {3} {1}", - temp_c, temp_o, output, zlib, cc); + cmd = String.Format("{4} " + debugging + " -o '{2}' -Wall {5} {0} `pkg-config --cflags --libs mono-2` {3} {1}", + temp_c, temp_o, output, zlib, cc, objc); } Execute (cmd); } @@ -1008,11 +1013,9 @@ void mono_register_config_for_assembly (const char* assembly_name, cons List assemblies = new List (); bool error = false; - var other = i18n.Select (x=> "I18N." + x + (x.Length > 0 ? "." : "") + "dll"); - - foreach (string name in sources.Concat (other)){ + foreach (string name in sources){ try { - Assembly a = LoadAssembly (name); + Assembly a = LoadAssemblyFile (name); if (a == null){ error = true; @@ -1031,17 +1034,61 @@ void mono_register_config_for_assembly (const char* assembly_name, cons } } - if (error) + if (error) { + Error ("Couldn't load one or more of the assemblies."); Environment.Exit (1); + } return assemblies; } + + static void LoadLocalizedAssemblies (List assemblies) + { + var other = i18n.Select (x => "I18N." + x + (x.Length > 0 ? "." : "") + "dll"); + string error = null; + + foreach (string name in other) { + try { + Assembly a = LoadAssembly (name); + + if (a == null) { + error = "Failed to load " + name; + continue; + } + + assemblies.Add (a.CodeBase); + } catch (Exception) { + if (skip_scan) { + if (!quiet) + Console.WriteLine ("File will not be scanned: {0}", name); + assemblies.Add (new Uri (new FileInfo (name).FullName).ToString ()); + } else { + throw; + } + } + } + + if (error != null) { + Console.Error.WriteLine ("Failure to load i18n assemblies, the following directories were searched for the assemblies:"); + foreach (var path in link_paths){ + Console.Error.WriteLine (" Path: " + path); + } + if (custom_mode){ + Console.WriteLine ("In Custom mode, you need to provide the directory to lookup assemblies from using -L"); + } + + Error ("Couldn't load one or more of the i18n assemblies: " + error); + Environment.Exit (1); + } + } + static readonly Universe universe = new Universe (); static readonly Dictionary loaded_assemblies = new Dictionary (); public static string GetAssemblyName (string path) { + string resourcePathSeparator = style == "windows" ? "\\\\" : "/"; string name = Path.GetFileName (path); // A bit of a hack to support satellite assemblies. They all share the same name but @@ -1051,7 +1098,7 @@ void mono_register_config_for_assembly (const char* assembly_name, cons string dir = Path.GetDirectoryName (path); int idx = dir.LastIndexOf (Path.DirectorySeparatorChar); if (idx >= 0) { - name = dir.Substring (idx + 1) + Path.DirectorySeparatorChar + name; + name = dir.Substring (idx + 1) + resourcePathSeparator + name; Console.WriteLine ($"Storing satellite assembly '{path}' with name '{name}'"); } else if (!quiet) Console.WriteLine ($"Warning: satellite assembly {path} doesn't have locale path prefix, name conflicts possible"); @@ -1070,7 +1117,7 @@ void mono_register_config_for_assembly (const char* assembly_name, cons var name = GetAssemblyName (path); string found; if (loaded_assemblies.TryGetValue (name, out found)) { - Error (string.Format ("Duplicate assembly name `{0}'. Both `{1}' and `{2}' use same assembly name.", name, path, found)); + Error ("Duplicate assembly name `{0}'. Both `{1}' and `{2}' use same assembly name.", name, path, found); return false; } @@ -1081,9 +1128,18 @@ void mono_register_config_for_assembly (const char* assembly_name, cons return true; try { Assembly a = universe.LoadFile (path); + if (a == null) { + Error ("Unable to to load assembly `{0}'", path); + return false; + } foreach (AssemblyName an in a.GetReferencedAssemblies ()) { - a = universe.Load (an.FullName); + a = LoadAssembly (an.Name); + if (a == null) { + Error ("Unable to load assembly `{0}' referenced by `{1}'", an.Name, path); + return false; + } + if (!QueueAssembly (files, a.CodeBase)) return false; } @@ -1095,56 +1151,63 @@ void mono_register_config_for_assembly (const char* assembly_name, cons return true; } - static Assembly LoadAssembly (string assembly) + // + // Loads an assembly from a specific path + // + static Assembly LoadAssemblyFile (string assembly) { - Assembly a; + Assembly a = null; try { - char[] path_chars = { '/', '\\' }; - - if (assembly.IndexOfAny (path_chars) != -1) { - a = universe.LoadFile (assembly); - } else { - string ass = assembly; - if (ass.EndsWith (".dll")) - ass = assembly.Substring (0, assembly.Length - 4); - a = universe.Load (ass); - } - return a; - } catch (FileNotFoundException){ - string total_log = ""; - - foreach (string dir in link_paths){ - string full_path = Path.Combine (dir, assembly); - if (!assembly.EndsWith (".dll") && !assembly.EndsWith (".exe")) - full_path += ".dll"; - - try { - a = universe.LoadFile (full_path); - return a; - } catch (FileNotFoundException ff) { - total_log += ff.FusionLog; - continue; - } - } - Error ("Cannot find assembly `" + assembly + "'" ); if (!quiet) - Console.WriteLine ("Log: \n" + total_log); + Console.WriteLine ("Attempting to load assembly: {0}", assembly); + a = universe.LoadFile (assembly); + if (!quiet) + Console.WriteLine ("Assembly {0} loaded successfully.", assembly); + + } catch (FileNotFoundException){ + Error ($"Cannot find assembly `{assembly}'"); } catch (IKVM.Reflection.BadImageFormatException f) { if (skip_scan) throw; - Error ("Cannot load assembly (bad file format) " + f.Message); + Error ($"Cannot load assembly (bad file format) " + f.Message); } catch (FileLoadException f){ - Error ("Cannot load assembly " + f.Message); + Error ($"Cannot load assembly " + f.Message); } catch (ArgumentNullException){ - Error("Cannot load assembly (null argument)"); + Error( $"Cannot load assembly (null argument)"); } - return null; + return a; } - static void Error (string msg) + // + // Loads an assembly from any of the link directories provided + // + static Assembly LoadAssembly (string assembly) + { + string total_log = ""; + foreach (string dir in link_paths){ + string full_path = Path.Combine (dir, assembly); + if (!quiet) + Console.WriteLine ("Attempting to load assembly from: " + full_path); + if (!assembly.EndsWith (".dll") && !assembly.EndsWith (".exe")) + full_path += ".dll"; + + try { + var a = universe.LoadFile (full_path); + return a; + } catch (FileNotFoundException ff) { + total_log += ff.FusionLog; + continue; + } + } + if (!quiet) + Console.WriteLine ("Log: \n" + total_log); + return null; + } + + static void Error (string msg, params object [] args) { - Console.Error.WriteLine ("ERROR: " + msg); + Console.Error.WriteLine ("ERROR: {0}", string.Format (msg, args)); Environment.Exit (1); } @@ -1162,16 +1225,19 @@ void mono_register_config_for_assembly (const char* assembly_name, cons " --skip-scan Skip scanning assemblies that could not be loaded (but still embed them).\n" + " --i18n ENCODING none, all or comma separated list of CJK, MidWest, Other, Rare, West.\n" + " -v Verbose output\n" + + " --bundled-header Do not attempt to include 'mono-config.h'. Define the entry points directly in the generated code\n" + "\n" + "--simple Simple mode does not require a C toolchain and can cross compile\n" + " --cross TARGET Generates a binary for the given TARGET\n"+ " --env KEY=VALUE Hardcodes an environment variable for the target\n" + + " --fetch-target NAME Downloads the target SDK from the remote server\n" + " --library [LIB,]PATH Bundles the specified dynamic library to be used at runtime\n" + " LIB is optional shortname for file located at PATH\n" + " --list-targets Lists available targets on the remote server\n" + " --local-targets Lists locally available targets\n" + " --options OPTIONS Embed the specified Mono command line options on target\n" + " --runtime RUNTIME Manually specifies the Mono runtime to use\n" + + " --sdk PATH Use a Mono SDK root location instead of a target\n" + " --target-server URL Specified a server to download targets from, default is " + target_server + "\n" + "\n" + "--custom Builds a custom launcher, options for --custom\n" + @@ -1302,7 +1368,7 @@ void mono_register_config_for_assembly (const char* assembly_name, cons p.WaitForExit (); int ret = p.ExitCode; if (ret != 0){ - Error (String.Format("[Fail] {0}", ret)); + Error ("[Fail] {0}", ret); } } } @@ -1334,4 +1400,1226 @@ void mono_register_config_for_assembly (const char* assembly_name, cons else throw new FileNotFoundException(default_path); } + + static string GetAssemblerCommand (string sourceFile, string objectFile) + { + if (style == "windows") { + string additionalArguments = ""; + var assembler = GetAssemblerCompiler (); + if (assembler.Name.Contains ("clang.exe")) + //Clang uses additional arguments. + additionalArguments = "-c -x assembler"; + + return String.Format ("\"{0}\" {1} -o {2} {3} ", assembler.Path, additionalArguments, objectFile, sourceFile); + } else { + return String.Format ("{0} -o {1} {2} ", GetEnv ("AS", "as"), objectFile, sourceFile); + } + } + +#region WindowsToolchainSupport + + class StringVersionComparer : IComparer { + public int Compare (string stringA, string stringB) + { + Version versionA; + Version versionB; + + var versionAMatch = System.Text.RegularExpressions.Regex.Match (stringA, @"\d+(\.\d +) + "); + if (versionAMatch.Success) + stringA = versionAMatch.ToString (); + + var versionBMatch = System.Text.RegularExpressions.Regex.Match (stringB, @"\d+(\.\d+)+"); + if (versionBMatch.Success) + stringB = versionBMatch.ToString (); + + if (Version.TryParse (stringA, out versionA) && Version.TryParse (stringB, out versionB)) + return versionA.CompareTo (versionB); + + return string.Compare (stringA, stringB, StringComparison.OrdinalIgnoreCase); + } + } + + class InstalledSDKInfo { + public InstalledSDKInfo (string name, string version, string installationFolder) + { + this.Name = name; + this.Version = Version.Parse (version); + this.InstallationFolder = installationFolder; + this.IsSubVersion = false; + this.AdditionalSDKs = new List (); + } + + public InstalledSDKInfo (string name, string version, string installationFolder, bool isSubVersion) + : this (name, version, installationFolder) + { + this.IsSubVersion = isSubVersion; + } + + public InstalledSDKInfo (string name, string version, string installationFolder, bool isSubVersion, InstalledSDKInfo parentSDK) + : this (name, version, installationFolder, isSubVersion) + { + this.ParentSDK = parentSDK; + } + + public string Name { get; set; } + public Version Version { get; set; } + public string InstallationFolder { get; set; } + public bool IsSubVersion { get; set; } + public List AdditionalSDKs { get; } + public InstalledSDKInfo ParentSDK { get; set; } + } + + class ToolchainProgram { + public ToolchainProgram (string name, string path) + { + this.Name = name; + this.Path = path; + } + + public ToolchainProgram (string name, string path, InstalledSDKInfo parentSDK) + : this (name, path) + { + this.ParentSDK = parentSDK; + } + + public Func QuoteArg = arg => "\"" + arg + "\""; + public string Name { get; set; } + public string Path { get; set; } + public InstalledSDKInfo ParentSDK { get; set; } + public bool IsVSToolChain { get { return (Name.Contains ("cl.exe") || Name.Contains ("lib.exe")); } } + public bool IsGCCToolChain { get { return !IsVSToolChain; } } + } + + class SDKHelper { + + protected Microsoft.Win32.RegistryKey GetToolchainRegistrySubKey (string subKey) + { + Microsoft.Win32.RegistryKey key = null; + + if (Environment.Is64BitProcess) { + key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SOFTWARE\Wow6432Node" + subKey) ?? + Microsoft.Win32.Registry.CurrentUser.OpenSubKey (@"SOFTWARE\Wow6432Node" + subKey); + } + + if (key == null) { + key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SOFTWARE" + subKey) ?? + Microsoft.Win32.Registry.CurrentUser.OpenSubKey (@"SOFTWARE" + subKey); + } + + return key; + } + } + + class WindowsSDKHelper : SDKHelper { + + List installedWindowsSDKs; + List installedCRuntimeSDKs; + InstalledSDKInfo installedWindowsSDK; + InstalledSDKInfo installedCRuntimeSDK; + + static WindowsSDKHelper singletonInstance = new WindowsSDKHelper (); + static public WindowsSDKHelper GetInstance () + { + return singletonInstance; + } + + Dictionary GetInstalledWindowsKitRootFolders () + { + var rootFolders = new Dictionary (); + + using (var subKey = GetToolchainRegistrySubKey (@"\Microsoft\Microsoft SDKs\Windows\")) { + if (subKey != null) { + foreach (var keyName in subKey.GetSubKeyNames ()) { + var keyNameIsVersion = System.Text.RegularExpressions.Regex.Match (keyName, @"\d+(\.\d+)+"); + if (keyNameIsVersion.Success) { + var installFolder = (string)Microsoft.Win32.Registry.GetValue (subKey.ToString () + @"\" + keyName, "InstallationFolder", ""); + if (!rootFolders.ContainsKey (installFolder)) + rootFolders.Add (installFolder, keyNameIsVersion.ToString ()); + } + } + } + } + + using (var subKey = GetToolchainRegistrySubKey (@"\Microsoft\Windows Kits\Installed Roots")) { + if (subKey != null) { + foreach (var valueName in subKey.GetValueNames ()) { + var valueNameIsKitsRoot = System.Text.RegularExpressions.Regex.Match (valueName, @"KitsRoot\d*"); + if (valueNameIsKitsRoot.Success) { + var installFolder = (string)Microsoft.Win32.Registry.GetValue (subKey.ToString (), valueName, ""); + if (!rootFolders.ContainsKey (installFolder)) { + var valueNameIsVersion = System.Text.RegularExpressions.Regex.Match (valueName, @"\d+(\.*\d+)+"); + if (valueNameIsVersion.Success) + rootFolders.Add (installFolder, valueNameIsVersion.ToString ()); + else + rootFolders.Add (installFolder, ""); + } + } + } + } + } + + return rootFolders; + } + + void InitializeInstalledWindowsKits () + { + if (installedWindowsSDKs == null && installedCRuntimeSDKs == null) { + List windowsSDKs = new List (); + List cRuntimeSDKs = new List (); + var rootFolders = GetInstalledWindowsKitRootFolders (); + foreach (var winKitRoot in rootFolders) { + // Try to locate Windows and CRuntime SDKs. + string winKitRootDir = winKitRoot.Key; + string winKitRootVersion = winKitRoot.Value; + string winKitIncludeDir = Path.Combine (winKitRootDir, "include"); + + //Search for installed SDK versions. + if (Directory.Exists (winKitIncludeDir)) { + var winKitIncludeDirInfo = new DirectoryInfo (winKitIncludeDir); + var versions = winKitIncludeDirInfo.GetDirectories ( + "*.*", SearchOption.TopDirectoryOnly).OrderByDescending (p => p.Name, new StringVersionComparer ()); + + foreach (var version in versions) { + string versionedWindowsSDKHeaderPath = Path.Combine (version.FullName, "um", "windows.h"); + string versionedCRuntimeSDKHeaderPath = Path.Combine (version.FullName, "ucrt", "stdlib.h"); + var hasSubVersion = System.Text.RegularExpressions.Regex.Match (version.Name, @"\d+(\.\d+)+"); + if (hasSubVersion.Success) { + if (File.Exists (versionedWindowsSDKHeaderPath)) + //Found a specific Windows SDK sub version. + windowsSDKs.Add (new InstalledSDKInfo ("WindowsSDK", hasSubVersion.ToString (), winKitRootDir, true)); + if (File.Exists (versionedCRuntimeSDKHeaderPath)) + //Found a specific CRuntime SDK sub version. + cRuntimeSDKs.Add (new InstalledSDKInfo ("CRuntimeSDK", hasSubVersion.ToString (), winKitRootDir, true)); + } + } + } + + // Try to find SDK without specific sub version. + string windowsSDKHeaderPath = Path.Combine (winKitIncludeDir, "um", "windows.h"); + if (File.Exists (windowsSDKHeaderPath)) + //Found a Windows SDK version. + windowsSDKs.Add (new InstalledSDKInfo ("WindowsSDK", winKitRootVersion, winKitRootDir, false)); + + string cRuntimeSDKHeaderPath = Path.Combine (winKitIncludeDir, "ucrt", "stdlib.h"); + if (File.Exists (cRuntimeSDKHeaderPath)) + //Found a CRuntime SDK version. + cRuntimeSDKs.Add (new InstalledSDKInfo ("CRuntimeSDK", winKitRootVersion, winKitRootDir, false)); + } + + // Sort based on version. + windowsSDKs = windowsSDKs.OrderByDescending (p => p.Version.ToString (), new StringVersionComparer ()).ToList (); + cRuntimeSDKs = cRuntimeSDKs.OrderByDescending (p => p.Version.ToString (), new StringVersionComparer ()).ToList (); + + installedWindowsSDKs = windowsSDKs; + installedCRuntimeSDKs = cRuntimeSDKs; + + if (!quiet && installedWindowsSDKs != null) { + Console.WriteLine ("--- Windows SDK's ---"); + foreach (var windowsSDK in installedWindowsSDKs) { + Console.WriteLine ("Path: " + windowsSDK.InstallationFolder); + Console.WriteLine ("Version: " + windowsSDK.Version); + } + Console.WriteLine ("---------------"); + } + + if (!quiet && installedCRuntimeSDKs != null) { + Console.WriteLine ("--- C-Runtime SDK's ---"); + foreach (var cRuntimeSDK in installedCRuntimeSDKs) { + Console.WriteLine ("Path: " + cRuntimeSDK.InstallationFolder); + Console.WriteLine ("Version: " + cRuntimeSDK.Version); + if (cRuntimeSDK.ParentSDK != null) { + Console.WriteLine ("Parent SDK Path: " + cRuntimeSDK.ParentSDK.InstallationFolder); + Console.WriteLine ("Parent SDK Version: " + cRuntimeSDK.ParentSDK.Version); + } + } + Console.WriteLine ("---------------"); + } + } + + return; + } + + List GetInstalledWindowsSDKs () + { + if (installedWindowsSDKs == null) + InitializeInstalledWindowsKits (); + + return installedWindowsSDKs; + } + + List GetInstalledCRuntimeSDKs () + { + if (installedCRuntimeSDKs == null) + InitializeInstalledWindowsKits (); + + return installedCRuntimeSDKs; + } + + InstalledSDKInfo GetInstalledWindowsSDK () + { + if (installedWindowsSDK == null) { + string winSDKDir = ""; + InstalledSDKInfo windowsSDK = null; + List windowsSDKs = GetInstalledWindowsSDKs (); + + // Check that env doesn't already include needed values. + winSDKDir = GetEnv ("WINSDK", ""); + if (winSDKDir.Length == 0) + // If executed from a VS developer command prompt, SDK dir set in env. + winSDKDir = GetEnv ("WindowsSdkDir", ""); + + // Check that env doesn't already include needed values. + // If executed from a VS developer command prompt, SDK version set in env. + var winSDKVersion = System.Text.RegularExpressions.Regex.Match (GetEnv ("WindowsSdkVersion", ""), @"\d+(\.\d+)+"); + + if (winSDKDir.Length != 0 && windowsSDKs != null) { + // Find installed SDK based on requested info. + if (winSDKVersion.Success) + windowsSDK = windowsSDKs.Find (x => (x.InstallationFolder == winSDKDir && x.Version.ToString () == winSDKVersion.ToString ())); + else + windowsSDK = windowsSDKs.Find (x => x.InstallationFolder == winSDKDir); + } + + if (windowsSDK == null && winSDKVersion.Success && windowsSDKs != null) + // Find installed SDK based on requested info. + windowsSDK = windowsSDKs.Find (x => x.Version.ToString () == winSDKVersion.ToString ()); + + if (windowsSDK == null && windowsSDKs != null) + // Get latest installed verison. + windowsSDK = windowsSDKs.First (); + + installedWindowsSDK = windowsSDK; + } + + return installedWindowsSDK; + } + + string FindCRuntimeSDKIncludePath (InstalledSDKInfo sdk) + { + string cRuntimeIncludePath = Path.Combine (sdk.InstallationFolder, "include"); + if (sdk.IsSubVersion) + cRuntimeIncludePath = Path.Combine (cRuntimeIncludePath, sdk.Version.ToString ()); + + cRuntimeIncludePath = Path.Combine (cRuntimeIncludePath, "ucrt"); + if (!Directory.Exists (cRuntimeIncludePath)) + cRuntimeIncludePath = ""; + + return cRuntimeIncludePath; + } + + string FindCRuntimeSDKLibPath (InstalledSDKInfo sdk) + { + string cRuntimeLibPath = Path.Combine (sdk.InstallationFolder, "lib"); + if (sdk.IsSubVersion) + cRuntimeLibPath = Path.Combine (cRuntimeLibPath, sdk.Version.ToString ()); + + cRuntimeLibPath = Path.Combine (cRuntimeLibPath, "ucrt", Target64BitApplication () ? "x64" : "x86"); + if (!Directory.Exists (cRuntimeLibPath)) + cRuntimeLibPath = ""; + + return cRuntimeLibPath; + } + + InstalledSDKInfo GetInstalledCRuntimeSDK () + { + if (installedCRuntimeSDK == null) { + InstalledSDKInfo cRuntimeSDK = null; + var windowsSDK = GetInstalledWindowsSDK (); + var cRuntimeSDKs = GetInstalledCRuntimeSDKs (); + + if (windowsSDK != null && cRuntimeSDKs != null) { + cRuntimeSDK = cRuntimeSDKs.Find (x => x.Version.ToString () == windowsSDK.Version.ToString ()); + if (cRuntimeSDK == null && cRuntimeSDKs.Count != 0) + cRuntimeSDK = cRuntimeSDKs.First (); + + installedCRuntimeSDK = cRuntimeSDK; + } + } + + return installedCRuntimeSDK; + } + + public void AddWindowsSDKIncludePaths (List includePaths) + { + InstalledSDKInfo winSDK = GetInstalledWindowsSDK (); + if (winSDK != null) { + string winSDKIncludeDir = Path.Combine (winSDK.InstallationFolder, "include"); + + if (winSDK.IsSubVersion) + winSDKIncludeDir = Path.Combine (winSDKIncludeDir, winSDK.Version.ToString ()); + + // Include sub folders. + if (Directory.Exists (winSDKIncludeDir)) { + includePaths.Add (Path.Combine (winSDKIncludeDir, "um")); + includePaths.Add (Path.Combine (winSDKIncludeDir, "shared")); + includePaths.Add (Path.Combine (winSDKIncludeDir, "winrt")); + } + } + + return; + } + + public void AddWindowsSDKLibPaths (List libPaths) + { + InstalledSDKInfo winSDK = GetInstalledWindowsSDK (); + if (winSDK != null) { + string winSDKLibDir = Path.Combine (winSDK.InstallationFolder, "lib"); + + if (winSDK.IsSubVersion) { + winSDKLibDir = Path.Combine (winSDKLibDir, winSDK.Version.ToString ()); + } else { + // Older WinSDK's header folders are not versioned, but installed libraries are, use latest availalble version for now. + var winSDKLibDirInfo = new DirectoryInfo (winSDKLibDir); + var versions = winSDKLibDirInfo.GetDirectories ("*.*", SearchOption.TopDirectoryOnly).OrderByDescending (p => p.Name, new StringVersionComparer ()); + if (versions != null && versions.First () != null) + winSDKLibDir = versions.First ().FullName; + } + + //Enumerat lib sub folders. + if (Directory.Exists (winSDKLibDir)) + libPaths.Add (Path.Combine (winSDKLibDir, "um", Target64BitApplication () ? "x64" : "x86")); + } + + return; + } + + public void AddCRuntimeSDKIncludePaths (List includePaths) + { + InstalledSDKInfo cRuntimeSDK = GetInstalledCRuntimeSDK (); + if (cRuntimeSDK != null) { + string cRuntimeSDKIncludeDir = FindCRuntimeSDKIncludePath (cRuntimeSDK); + + if (cRuntimeSDKIncludeDir.Length != 0) + includePaths.Add (cRuntimeSDKIncludeDir); + } + + return; + } + + public void AddCRuntimeSDKLibPaths (List libPaths) + { + InstalledSDKInfo cRuntimeSDK = GetInstalledCRuntimeSDK (); + if (cRuntimeSDK != null) { + string cRuntimeSDKLibDir = FindCRuntimeSDKLibPath (cRuntimeSDK); + + if (cRuntimeSDKLibDir.Length != 0) + libPaths.Add (cRuntimeSDKLibDir); + } + + return; + } + } + + class VisualStudioSDKHelper : SDKHelper { + + List installedVisualStudioSDKs; + InstalledSDKInfo installedVisualStudioSDK; + + static VisualStudioSDKHelper singletonInstance = new VisualStudioSDKHelper (); + static public VisualStudioSDKHelper GetInstance () + { + return singletonInstance; + } + + List InitializeInstalledVisualStudioSDKs () + { + if (installedVisualStudioSDKs == null) { + List sdks = new List (); + + using (var subKey = GetToolchainRegistrySubKey (@"\Microsoft\VisualStudio\SxS\VS7")) { + if (subKey != null) { + foreach (var keyName in subKey.GetValueNames ()) { + var vsInstalltionFolder = (string)Microsoft.Win32.Registry.GetValue (subKey.ToString (), keyName, ""); + if (Directory.Exists (vsInstalltionFolder)) { + var vsSDK = new InstalledSDKInfo ("VisualStudio", keyName, vsInstalltionFolder, false); + var vcInstallationFolder = Path.Combine (vsInstalltionFolder, "VC"); + + if (Directory.Exists (vcInstallationFolder)) + vsSDK.AdditionalSDKs.Add (new InstalledSDKInfo ("VisualStudioVC", keyName, vcInstallationFolder, false, vsSDK)); + + sdks.Add (vsSDK); + } + } + } + } + + // TODO: Add VS15 SetupConfiguration support. + // To reduce dependecies use vswhere.exe, if available. + + // Sort based on version. + sdks = sdks.OrderByDescending (p => p.Version.ToString (), new StringVersionComparer ()).ToList (); + installedVisualStudioSDKs = sdks; + } + + return installedVisualStudioSDKs; + } + + string FindVisualStudioVCFolderPath (InstalledSDKInfo vcSDK, string subPath) + { + string folderPath = ""; + if (vcSDK != null && vcSDK.ParentSDK != null) { + if (IsVisualStudio12 (vcSDK.ParentSDK) || IsVisualStudio14 (vcSDK.ParentSDK)) { + folderPath = Path.Combine (vcSDK.InstallationFolder, subPath); + } else if (IsVisualStudio15 (vcSDK.ParentSDK)) { + string msvcVersionPath = Path.Combine (vcSDK.InstallationFolder, "Tools", "MSVC"); + + // Add latest found version of MSVC toolchain. + if (Directory.Exists (msvcVersionPath)) { + var msvcVersionDirInfo = new DirectoryInfo (msvcVersionPath); + var versions = msvcVersionDirInfo.GetDirectories ("*.*", SearchOption.TopDirectoryOnly).OrderByDescending (p => p.Name, new StringVersionComparer ()); + + foreach (var version in versions) { + msvcVersionPath = Path.Combine (version.FullName, subPath); + if (Directory.Exists (msvcVersionPath)) { + folderPath = msvcVersionPath; + break; + } + } + } + } + } + + return folderPath; + } + + string FindVisualStudioVCLibSubPath (InstalledSDKInfo vcSDK) + { + string subPath = ""; + + if (vcSDK != null && vcSDK.ParentSDK != null) { + if (IsVisualStudio12 (vcSDK.ParentSDK) || IsVisualStudio14 (vcSDK.ParentSDK)) + subPath = Target64BitApplication () ? @"lib\amd64" : "lib"; + else if (IsVisualStudio15 (vcSDK.ParentSDK)) + subPath = Target64BitApplication () ? @"lib\x64" : @"lib\x86"; + } + + return subPath; + } + + public InstalledSDKInfo GetInstalledVisualStudioSDK () + { + if (installedVisualStudioSDK == null) { + List visualStudioSDKs = InitializeInstalledVisualStudioSDKs (); + InstalledSDKInfo visualStudioSDK = null; + + // Check that env doesn't already include needed values. + // If executed from a VS developer command prompt, Visual Studio install dir set in env. + string vsVersion = GetEnv ("VisualStudioVersion", ""); + + if (vsVersion.Length != 0 && visualStudioSDKs != null) + // Find installed SDK based on requested info. + visualStudioSDK = visualStudioSDKs.Find (x => x.Version.ToString () == vsVersion); + + if (visualStudioSDK == null && visualStudioSDKs != null) + // Get latest installed verison. + visualStudioSDK = visualStudioSDKs.First (); + + installedVisualStudioSDK = visualStudioSDK; + } + + return installedVisualStudioSDK; + } + + public InstalledSDKInfo GetInstalledVisualStudioVCSDK () + { + InstalledSDKInfo visualStudioVCSDK = null; + + // Check that env doesn't already include needed values. + // If executed from a VS developer command prompt, Visual Studio install dir set in env. + string vcInstallDir = GetEnv ("VCINSTALLDIR", ""); + if (vcInstallDir.Length != 0) { + List installedVisualStudioSDKs = InitializeInstalledVisualStudioSDKs (); + if (installedVisualStudioSDKs != null) { + foreach (var currentInstalledSDK in installedVisualStudioSDKs) { + // Find installed SDK based on requested info. + visualStudioVCSDK = currentInstalledSDK.AdditionalSDKs.Find (x => x.InstallationFolder == vcInstallDir); + if (visualStudioVCSDK != null) + break; + } + } + } + + // Get latest installed VS VC SDK version. + if (visualStudioVCSDK == null) { + var visualStudioSDK = GetInstalledVisualStudioSDK (); + if (visualStudioSDK != null) + visualStudioVCSDK = visualStudioSDK.AdditionalSDKs.Find (x => x.Name == "VisualStudioVC"); + } + + return visualStudioVCSDK; + } + + public bool IsVisualStudio12 (InstalledSDKInfo vsSDK) + { + return vsSDK.Version.Major == 12 || vsSDK.Version.Major == 2013; + } + + public bool IsVisualStudio14 (InstalledSDKInfo vsSDK) + { + return vsSDK.Version.Major == 14 || vsSDK.Version.Major == 2015; + } + + public bool IsVisualStudio15 (InstalledSDKInfo vsSDK) + { + return vsSDK.Version.Major == 15 || vsSDK.Version.Major == 2017; + } + + public void AddVisualStudioVCIncludePaths (List includePaths) + { + // Check that env doesn't already include needed values. + string vcIncludeDir = GetEnv ("VSINCLUDE", ""); + if (vcIncludeDir.Length == 0) { + var visualStudioVCSDK = GetInstalledVisualStudioVCSDK (); + vcIncludeDir = FindVisualStudioVCFolderPath (visualStudioVCSDK, "include"); + } + + if (vcIncludeDir.Length != 0) + includePaths.Add (vcIncludeDir); + + return; + } + + public void AddVisualStudioVCLibPaths (List libPaths) + { + // Check that env doesn't already include needed values. + string vcLibDir = GetEnv ("VSLIB", ""); + if (vcLibDir.Length == 0) { + var vcSDK = GetInstalledVisualStudioVCSDK (); + vcLibDir = FindVisualStudioVCFolderPath (vcSDK, FindVisualStudioVCLibSubPath (vcSDK)); + } + + if (vcLibDir.Length != 0) + libPaths.Add (vcLibDir); + + return; + } + } + + class VCToolchainProgram { + + protected ToolchainProgram toolchain; + public virtual bool IsVersion (InstalledSDKInfo vcSDK) { return false; } + public virtual ToolchainProgram FindVCToolchainProgram (InstalledSDKInfo vcSDK) { return null; } + } + + class VC12ToolchainProgram : VCToolchainProgram { + + public override bool IsVersion (InstalledSDKInfo vcSDK) + { + return VisualStudioSDKHelper.GetInstance ().IsVisualStudio12 (vcSDK); + } + + protected ToolchainProgram FindVCToolchainProgram (string tool, InstalledSDKInfo vcSDK) + { + if (toolchain == null) { + string toolPath = ""; + if (!string.IsNullOrEmpty (vcSDK?.InstallationFolder)) { + if (Target64BitApplication ()) + toolPath = Path.Combine (new string [] { vcSDK.InstallationFolder, "bin", "amd64", tool }); + else + toolPath = Path.Combine (new string [] { vcSDK.InstallationFolder, "bin", tool }); + + if (!File.Exists (toolPath)) + toolPath = ""; + } + + toolchain = new ToolchainProgram (tool, toolPath, vcSDK); + } + + return toolchain; + } + } + + class VC15ToolchainProgram : VCToolchainProgram { + public override bool IsVersion (InstalledSDKInfo vcSDK) + { + return VisualStudioSDKHelper.GetInstance ().IsVisualStudio15 (vcSDK); + } + + protected ToolchainProgram FindVCToolchainProgram (string tool, InstalledSDKInfo vcSDK) + { + if (toolchain == null) { + string toolPath = ""; + if (!string.IsNullOrEmpty (vcSDK?.InstallationFolder)) { + string toolsVersionFilePath = Path.Combine (vcSDK.InstallationFolder, "Auxiliary", "Build", "Microsoft.VCToolsVersion.default.txt"); + string toolsVersion = File.ReadAllLines (toolsVersionFilePath).ElementAt (0).Trim (); + string toolsVersionPath = Path.Combine (vcSDK.InstallationFolder, "Tools", "MSVC", toolsVersion); + + if (Target64BitApplication ()) + toolPath = Path.Combine (toolsVersionPath, "bin", "HostX64", "x64", tool); + else + toolPath = Path.Combine (toolsVersionPath, "bin", "HostX86", "x86", tool); + } + + toolchain = new ToolchainProgram (tool, toolPath, vcSDK); + } + + return toolchain; + } + } + + class VC12Compiler : VC12ToolchainProgram { + public override ToolchainProgram FindVCToolchainProgram (InstalledSDKInfo vcSDK) + { + return FindVCToolchainProgram ("cl.exe", vcSDK); + } + } + + class VC14Compiler : VC12Compiler { + public override bool IsVersion (InstalledSDKInfo vcSDK) + { + return VisualStudioSDKHelper.GetInstance ().IsVisualStudio14 (vcSDK); + } + } + + class VC15Compiler : VC15ToolchainProgram { + public override ToolchainProgram FindVCToolchainProgram (InstalledSDKInfo vcSDK) + { + return FindVCToolchainProgram ("cl.exe", vcSDK); + } + } + + class VC12Librarian : VC12ToolchainProgram { + public override ToolchainProgram FindVCToolchainProgram (InstalledSDKInfo vcSDK) + { + return FindVCToolchainProgram ("lib.exe", vcSDK); + } + } + + class VC14Librarian : VC12Compiler { + public override bool IsVersion (InstalledSDKInfo vcSDK) + { + return VisualStudioSDKHelper.GetInstance ().IsVisualStudio14 (vcSDK); + } + } + + class VC15Librarian : VC15ToolchainProgram { + public override ToolchainProgram FindVCToolchainProgram (InstalledSDKInfo vcSDK) + { + return FindVCToolchainProgram ("lib.exe", vcSDK); + } + } + + class VC14Clang : VCToolchainProgram { + public override bool IsVersion (InstalledSDKInfo vcSDK) + { + return VisualStudioSDKHelper.GetInstance ().IsVisualStudio14 (vcSDK); + } + + public override ToolchainProgram FindVCToolchainProgram (InstalledSDKInfo vcSDK) + { + if (toolchain == null) { + string clangPath = ""; + if (!string.IsNullOrEmpty (vcSDK?.InstallationFolder)) { + clangPath = Path.Combine (new string [] { vcSDK.InstallationFolder, "ClangC2", "bin", Target64BitApplication () ? "amd64" : "x86", "clang.exe" }); + + if (!File.Exists (clangPath)) + clangPath = ""; + } + + toolchain = new ToolchainProgram ("clang.exe", clangPath, vcSDK); + } + + return toolchain; + } + } + + class VC15Clang : VCToolchainProgram { + public override bool IsVersion (InstalledSDKInfo vcSDK) + { + return VisualStudioSDKHelper.GetInstance ().IsVisualStudio15 (vcSDK); + } + + public override ToolchainProgram FindVCToolchainProgram (InstalledSDKInfo vcSDK) + { + if (toolchain == null) { + string clangPath = ""; + if (!string.IsNullOrEmpty (vcSDK?.InstallationFolder)) { + string clangVersionFilePath = Path.Combine (vcSDK.InstallationFolder, "Auxiliary", "Build", "Microsoft.ClangC2Version.default.txt"); + string clangVersion = File.ReadAllLines (clangVersionFilePath).ElementAt (0).Trim (); + string clangVersionPath = Path.Combine (vcSDK.InstallationFolder, "Tools", "ClangC2", clangVersion); + + clangPath = Path.Combine (clangVersionPath, "bin", Target64BitApplication () ? "HostX64" : "HostX86", "clang.exe"); + } + + toolchain = new ToolchainProgram ("clang.exe", clangPath, vcSDK); + } + + return toolchain; + } + } + + class VisualStudioSDKToolchainHelper { + List vcCompilers = new List (); + List vcLibrarians = new List (); + List vcClangCompilers = new List (); + + public VisualStudioSDKToolchainHelper () + { + vcCompilers.Add (new VC12Compiler ()); + vcCompilers.Add (new VC14Compiler ()); + vcCompilers.Add (new VC15Compiler ()); + + vcLibrarians.Add (new VC12Librarian ()); + vcLibrarians.Add (new VC14Librarian ()); + vcLibrarians.Add (new VC15Librarian ()); + + vcClangCompilers.Add (new VC14Clang ()); + vcClangCompilers.Add (new VC15Clang ()); + } + + static VisualStudioSDKToolchainHelper singletonInstance = new VisualStudioSDKToolchainHelper (); + static public VisualStudioSDKToolchainHelper GetInstance () + { + return singletonInstance; + } + + ToolchainProgram GetVCToolChainProgram (List programs) + { + ToolchainProgram program = null; + var vcSDK = VisualStudioSDKHelper.GetInstance ().GetInstalledVisualStudioVCSDK (); + if (vcSDK?.ParentSDK != null) { + foreach (var item in programs) { + if (item.IsVersion (vcSDK.ParentSDK)) { + program = item.FindVCToolchainProgram (vcSDK); + break; + } + } + } + + return program; + } + + public ToolchainProgram GetVCCompiler () + { + return GetVCToolChainProgram (vcCompilers); + } + + public ToolchainProgram GetVCLibrarian () + { + return GetVCToolChainProgram (vcLibrarians); + } + + public ToolchainProgram GetVCClangCompiler () + { + return GetVCToolChainProgram (vcClangCompilers); + } + } + + static bool Target64BitApplication () + { + // Should probably handled the --cross and sdk parameters. + return Environment.Is64BitProcess; + } + + static string GetMonoDir () + { + // Check that env doesn't already include needed values. + string monoInstallDir = GetEnv ("MONOPREFIX", ""); + if (monoInstallDir.Length == 0) { + using (var baseKey = Microsoft.Win32.RegistryKey.OpenBaseKey (Microsoft.Win32.RegistryHive.LocalMachine, + Target64BitApplication () ? Microsoft.Win32.RegistryView.Registry64 : Microsoft.Win32.RegistryView.Registry32)) { + + if (baseKey != null) { + using (var subKey = baseKey.OpenSubKey (@"SOFTWARE\Mono")) { + if (subKey != null) + monoInstallDir = (string)subKey.GetValue ("SdkInstallRoot", ""); + } + } + } + } + + return monoInstallDir; + } + + static void AddMonoIncludePaths (List includePaths) + { + includePaths.Add (Path.Combine (GetMonoDir (), @"include\mono-2.0")); + return; + } + + static void AddMonoLibPaths (List libPaths) + { + libPaths.Add (Path.Combine (GetMonoDir (), "lib")); + return; + } + + static void AddIncludePaths (List includePaths) + { + // Check that env doesn't already include needed values. + // If executed from a VS developer command prompt, all includes are already setup in env. + string includeEnv = GetEnv ("INCLUDE", ""); + if (includeEnv.Length == 0) { + VisualStudioSDKHelper.GetInstance ().AddVisualStudioVCIncludePaths (includePaths); + WindowsSDKHelper.GetInstance ().AddCRuntimeSDKIncludePaths (includePaths); + WindowsSDKHelper.GetInstance ().AddWindowsSDKIncludePaths (includePaths); + } + + AddMonoIncludePaths (includePaths); + includePaths.Add ("."); + + return; + } + + static void AddLibPaths (List libPaths) + { + // Check that env doesn't already include needed values. + // If executed from a VS developer command prompt, all libs are already setup in env. + string libEnv = GetEnv ("LIB", ""); + if (libEnv.Length == 0) { + VisualStudioSDKHelper.GetInstance ().AddVisualStudioVCLibPaths (libPaths); + WindowsSDKHelper.GetInstance ().AddCRuntimeSDKLibPaths (libPaths); + WindowsSDKHelper.GetInstance ().AddWindowsSDKLibPaths (libPaths); + } + + AddMonoLibPaths (libPaths); + libPaths.Add ("."); + + return; + } + + static void AddVCSystemLibraries (ToolchainProgram program, bool staticLinkMono, bool staticLinkCRuntime, List linkerArgs) + { + linkerArgs.Add ("kernel32.lib"); + linkerArgs.Add ("version.lib"); + linkerArgs.Add ("ws2_32.lib"); + linkerArgs.Add ("mswsock.lib"); + linkerArgs.Add ("psapi.lib"); + linkerArgs.Add ("shell32.lib"); + linkerArgs.Add ("oleaut32.lib"); + linkerArgs.Add ("ole32.lib"); + linkerArgs.Add ("winmm.lib"); + linkerArgs.Add ("user32.lib"); + linkerArgs.Add ("advapi32.lib"); + + if (program != null && program.ParentSDK != null && VisualStudioSDKHelper.GetInstance ().IsVisualStudio12 (program.ParentSDK)) { + if (staticLinkCRuntime) { + // Static release c-runtime support. + linkerArgs.Add ("libcmt.lib"); + linkerArgs.Add ("oldnames.lib"); + } else { + // Dynamic release c-runtime support. + linkerArgs.Add ("msvcrt.lib"); + linkerArgs.Add ("oldnames.lib"); + } + } else { + if (staticLinkCRuntime) { + // Static release c-runtime support. + linkerArgs.Add ("libucrt.lib"); + linkerArgs.Add ("libvcruntime.lib"); + linkerArgs.Add ("libcmt.lib"); + linkerArgs.Add ("oldnames.lib"); + } else { + // Dynamic release c-runtime support. + linkerArgs.Add ("ucrt.lib"); + linkerArgs.Add ("vcruntime.lib"); + linkerArgs.Add ("msvcrt.lib"); + linkerArgs.Add ("oldnames.lib"); + } + } + + return; + } + + static void AddGCCSystemLibraries (ToolchainProgram program, bool staticLinkMono, bool staticLinkCRuntime, List linkerArgs) + { + if (MakeBundle.compress) + linkerArgs.Add ("-lz"); + + return; + } + + static void AddSystemLibraries (ToolchainProgram program, bool staticLinkMono, bool staticLinkCRuntime, List linkerArgs) + { + if (program.IsVSToolChain) + AddVCSystemLibraries (program, staticLinkMono, staticLinkCRuntime, linkerArgs); + else + AddGCCSystemLibraries (program, staticLinkMono, staticLinkCRuntime, linkerArgs); + + return; + } + + static void AddMonoLibraries (ToolchainProgram program, bool staticLinkMono, bool staticLinkCRuntime, List linkerArguments) + { + bool vsToolChain = program.IsVSToolChain; + string libPrefix = !vsToolChain ? "-l" : ""; + string libExtension = vsToolChain ? ".lib" : ""; + string monoLibrary = GetEnv ("LIBMONO", ""); + + if (monoLibrary.Length == 0) { + if (staticLinkMono) { + if (program.IsGCCToolChain) { + Console.WriteLine ( @"Warning: Static linking using default Visual Studio build libmono-static-sgen" + + @"might cause link errors when using GCC toolchain."); + } + monoLibrary = "libmono-static-sgen"; + } else { + monoLibrary = "mono-2.0-sgen"; + } + } + + if (!Path.IsPathRooted (monoLibrary)) { + if (!monoLibrary.EndsWith (libExtension)) + monoLibrary = monoLibrary + libExtension; + + linkerArguments.Add (libPrefix + monoLibrary); + } else { + linkerArguments.Add (monoLibrary); + } + + return; + } + + static void AddVCCompilerArguments (ToolchainProgram program, bool staticLinkMono, bool staticLinkCRuntime, List compilerArgs) + { + List includePaths = new List (); + AddIncludePaths (includePaths); + + if (staticLinkCRuntime) + // Add targeted c-runtime (MT = static release). + compilerArgs.Add ("/MT"); + else + // Add targeted c-runtime (MD = dynamic release). + compilerArgs.Add ("/MD"); + + // Add include search paths. + foreach (string include in includePaths) + compilerArgs.Add (String.Format("/I {0}", program.QuoteArg (include))); + + return; + } + + static void AddGCCCompilerArguments (ToolchainProgram program, bool staticLinkMono, bool staticLinkCRuntime, List compilerArgs) + { + List includePaths = new List (); + AddMonoIncludePaths (includePaths); + + // Add include search paths. + foreach (string include in includePaths) + compilerArgs.Add (String.Format ("-I {0}", program.QuoteArg (include))); + + return; + } + + static void AddCompilerArguments (ToolchainProgram program, bool staticLinkMono, bool staticLinkCRuntime, List compilerArgs) + { + if (program.IsVSToolChain) + AddVCCompilerArguments (program, staticLinkMono, staticLinkCRuntime, compilerArgs); + else + AddGCCCompilerArguments (program, staticLinkMono, staticLinkCRuntime, compilerArgs); + + return; + } + + static void AddVCLinkerArguments (ToolchainProgram linker, bool staticLinkMono, bool staticLinkCRuntime, string customMain, string outputFile, List linkerArgs) + { + linkerArgs.Add ("/link"); + + var subsystem = GetEnv ("VCSUBSYSTEM", "windows"); + linkerArgs.Add ("/SUBSYSTEM:" + subsystem); + + if (customMain != null && customMain.Length != 0) + linkerArgs.Add (linker.QuoteArg (customMain)); + else + linkerArgs.Add ("/ENTRY:mainCRTStartup"); + + // Ignore other c-runtime directives from linked libraries. + linkerArgs.Add ("/NODEFAULTLIB"); + + AddMonoLibraries (linker, staticLinkMono, staticLinkCRuntime, linkerArgs); + AddSystemLibraries (linker, staticLinkMono, staticLinkCRuntime, linkerArgs); + + // Add library search paths. + List libPaths = new List (); + AddLibPaths (libPaths); + + foreach (string lib in libPaths) + linkerArgs.Add (String.Format ("/LIBPATH:{0}", linker.QuoteArg (lib))); + + // Linker output target. + linkerArgs.Add ("/OUT:" + linker.QuoteArg (outputFile)); + + return; + } + + static void AddGCCLinkerArguments (ToolchainProgram linker, bool staticLinkMono, bool staticLinkCRuntime, string customMain, string outputFile, List linkerArgs) + { + // Add library search paths. + List libPaths = new List (); + AddMonoLibPaths (libPaths); + + foreach (string lib in libPaths) + linkerArgs.Add (String.Format ("-L {0}", linker.QuoteArg (lib))); + + // Add libraries. + if (staticLinkMono) + linkerArgs.Add ("-Wl,-Bstatic"); + + AddMonoLibraries (linker, staticLinkMono, staticLinkCRuntime, linkerArgs); + + if (staticLinkMono) + linkerArgs.Add ("-Wl,-Bdynamic"); + + AddSystemLibraries (linker, staticLinkMono, staticLinkCRuntime, linkerArgs); + + // Linker output target. + linkerArgs.Add ("-o " + linker.QuoteArg (outputFile)); + } + + static void AddLinkerArguments (ToolchainProgram program, bool staticLinkMono, bool staticLinkCRuntime, string customMain, string outputFile, List linkerArgs) + { + if (program.IsVSToolChain) + AddVCLinkerArguments (program, staticLinkMono, staticLinkCRuntime, customMain, outputFile, linkerArgs); + else + AddGCCLinkerArguments (program, staticLinkMono, staticLinkCRuntime, customMain, outputFile, linkerArgs); + + return; + } + + static void AddVCLibrarianCompilerArguments (ToolchainProgram compiler, string sourceFile, bool staticLinkMono, bool staticLinkCRuntime, List compilerArgs, out string objectFile) + { + compilerArgs.Add ("/c"); + compilerArgs.Add (compiler.QuoteArg (sourceFile)); + + objectFile = sourceFile + ".obj"; + compilerArgs.Add (String.Format ("/Fo" + compiler.QuoteArg (objectFile))); + + return; + } + + static void AddGCCLibrarianCompilerArguments (ToolchainProgram compiler, string sourceFile, bool staticLinkMono, bool staticLinkCRuntime, List compilerArgs, out string objectFile) + { + compilerArgs.Add ("-c"); + compilerArgs.Add (compiler.QuoteArg (sourceFile)); + + objectFile = sourceFile + ".o"; + compilerArgs.Add (String.Format ("-o " + compiler.QuoteArg (objectFile))); + + return; + } + + static void AddVCLibrarianLinkerArguments (ToolchainProgram librarian, string [] objectFiles, bool staticLinkMono, bool staticLinkCRuntime, string outputFile, List librarianArgs) + { + foreach (var objectFile in objectFiles) + librarianArgs.Add (librarian.QuoteArg (objectFile)); + + // Add library search paths. + List libPaths = new List (); + AddLibPaths (libPaths); + + foreach (string lib in libPaths) { + librarianArgs.Add (String.Format ("/LIBPATH:{0}", librarian.QuoteArg (lib))); + } + + AddMonoLibraries (librarian, staticLinkMono, staticLinkCRuntime, librarianArgs); + + librarianArgs.Add ("/OUT:" + librarian.QuoteArg (output)); + + return; + } + + static void AddGCCLibrarianLinkerArguments (ToolchainProgram librarian, string [] objectFiles, bool staticLinkMono, bool staticLinkCRuntime, string outputFile, List librarianArgs) + { + foreach (var objectFile in objectFiles) + librarianArgs.Add (librarian.QuoteArg (objectFile)); + + // Add library search paths. + List libPaths = new List (); + AddMonoLibPaths (libPaths); + + foreach (string lib in libPaths) + librarianArgs.Add (String.Format ("-L {0}", librarian.QuoteArg (lib))); + + AddMonoLibraries (librarian, staticLinkMono, staticLinkCRuntime, librarianArgs); + + librarianArgs.Add ("-o " + librarian.QuoteArg (output)); + + return; + } + + static ToolchainProgram GetAssemblerCompiler () + { + // First check if env is set (old behavior) and use that. + string assembler = GetEnv ("AS", ""); + if (assembler.Length != 0) + return new ToolchainProgram ("AS", assembler); + + var vcClangAssembler = VisualStudioSDKToolchainHelper.GetInstance ().GetVCClangCompiler (); + if (vcClangAssembler == null) + // Fallback to GNU assembler if clang for VS was not installed. + // Why? because mkbundle generates GNU assembler not compilable by VS tools like ml. + return new ToolchainProgram ("AS", "as.exe"); + + return vcClangAssembler; + } + + static ToolchainProgram GetCCompiler () + { + // First check if env is set (old behavior) and use that. + string compiler = GetEnv ("CC", ""); + if (compiler.Length != 0) + return new ToolchainProgram ("CC", compiler); + + var vcCompiler = VisualStudioSDKToolchainHelper.GetInstance ().GetVCCompiler (); + if (vcCompiler == null) + // Fallback to cl.exe if VC compiler was not installed. + return new ToolchainProgram ("cl.exe", "cl.exe"); + + return vcCompiler; + } + + static ToolchainProgram GetLibrarian () + { + ToolchainProgram vcLibrarian = VisualStudioSDKToolchainHelper.GetInstance ().GetVCLibrarian (); + if (vcLibrarian == null) + // Fallback to lib.exe if VS was not installed. + return new ToolchainProgram ("lib.exe", "lib.exe"); + + return vcLibrarian; + } + + static string GetCompileAndLinkCommand (ToolchainProgram compiler, string sourceFile, string objectFile, string customMain, bool staticLinkMono, bool staticLinkCRuntime, string outputFile) + { + var compilerArgs = new List (); + + AddCompilerArguments (compiler, staticLinkMono, staticLinkCRuntime, compilerArgs); + + // Add source file to compile. + compilerArgs.Add (compiler.QuoteArg (sourceFile)); + + // Add assembled object file. + compilerArgs.Add (compiler.QuoteArg (objectFile)); + + // Add linker arguments. + AddLinkerArguments (compiler, staticLinkMono, staticLinkCRuntime, customMain, outputFile, compilerArgs); + + return String.Format ("{0} {1}", compiler.QuoteArg (compiler.Path), String.Join (" ", compilerArgs.ToArray ())); + } + + static string GetLibrarianCompilerCommand (ToolchainProgram compiler, string sourceFile, bool staticLinkMono, bool staticLinkCRuntime, out string objectFile) + { + var compilerArgs = new List (); + + AddCompilerArguments (compiler, staticLinkMono, staticLinkCRuntime, compilerArgs); + + if (compiler.IsVSToolChain) + AddVCLibrarianCompilerArguments (compiler, sourceFile, staticLinkMono, staticLinkCRuntime, compilerArgs, out objectFile); + else + AddGCCLibrarianCompilerArguments (compiler, sourceFile, staticLinkMono, staticLinkCRuntime, compilerArgs, out objectFile); + + return String.Format ("{0} {1}", compiler.QuoteArg (compiler.Path), String.Join (" ", compilerArgs.ToArray ())); + } + + static string GetLibrarianLinkerCommand (ToolchainProgram librarian, string [] objectFiles, bool staticLinkMono, bool staticLinkCRuntime, string outputFile) + { + var librarianArgs = new List (); + + if (librarian.IsVSToolChain) + AddVCLibrarianLinkerArguments (librarian, objectFiles, staticLinkMono, staticLinkCRuntime, outputFile, librarianArgs); + else + AddGCCLibrarianLinkerArguments (librarian, objectFiles, staticLinkMono, staticLinkCRuntime, outputFile, librarianArgs); + + return String.Format ("{0} {1}", librarian.QuoteArg (librarian.Path), String.Join (" ", librarianArgs.ToArray ())); + } +#endregion }