// // Mono.Tools.GacUtil // // Author(s): // Todd Berman // Jackson Harper // // Copyright 2003, 2004 Todd Berman // Copyright 2004 Novell, Inc (http://www.novell.com) // using System; using System.IO; using System.Diagnostics; using System.Text; using System.Collections; using System.Globalization; using System.Runtime.InteropServices; using System.Security.Cryptography; using Mono.Security; using Mono.Security.Cryptography; using IKVM.Reflection; namespace Mono.Tools { public class Driver { private enum Command { Unknown, Install, InstallFromList, Uninstall, UninstallFromList, UninstallSpecific, List, Help } private enum VerificationResult { StrongNamed, WeakNamed, DelaySigned, Skipped } private static bool silent; static bool in_bootstrap; private static Universe _universe; public static int Main (string [] args) { if (args.Length == 0) Usage (); Command command = Command.Unknown; string command_str = null; string libdir; string name, package, gacdir, root; name = package = root = gacdir = null; bool check_refs = false; // Check for silent arg first so we can suppress // warnings during command line parsing if (Array.IndexOf (args, "/silent") > -1 || Array.IndexOf (args, "-silent") > -1) silent = true; for (int i=0; i= args.Length) { Console.WriteLine ("Option " + args [i] + " takes 1 argument"); return 1; } switch (args [i]) { case "-package": case "/package": package = args [++i]; continue; case "-root": case "/root": root = args [++i]; continue; case "-gacdir": case "/gacdir": gacdir = args [++i]; continue; case "/nologo": case "-nologo": // we currently don't display a // logo banner, so ignore it // for command-line compatibility // with MS gacutil continue; } } if (name == null) name = args [i]; else name += args [i]; } if (command == Command.Unknown && IsSwitch (args [0])) { Console.WriteLine ("Unknown command: " + args [0]); return 1; } else if (command == Command.Unknown) { Usage (); } else if (command == Command.Help) { ShowHelp (true); return 1; } if (gacdir == null) { gacdir = GetGacDir (); libdir = GetLibDir (); } else { gacdir = EnsureLib (gacdir); libdir = Path.Combine (gacdir, "mono"); gacdir = Path.Combine (libdir, "gac"); } string link_gacdir = gacdir; string link_libdir = libdir; if (root != null) { libdir = Path.Combine (root, "mono"); gacdir = Path.Combine (libdir, "gac"); } LoadConfig (silent); switch (command) { case Command.Install: if (name == null) { WriteLine ("Option " + command_str + " takes 1 argument"); return 1; } if (!Install (check_refs, name, package, gacdir, link_gacdir, libdir, link_libdir)) return 1; break; case Command.InstallFromList: if (name == null) { WriteLine ("Option " + command_str + " takes 1 argument"); return 1; } if (!InstallFromList (check_refs, name, package, gacdir, link_gacdir, libdir, link_libdir)) return 1; break; case Command.Uninstall: if (name == null) { WriteLine ("Option " + command_str + " takes 1 argument"); return 1; } int uninstallCount = 0; int uninstallFailures = 0; Uninstall (name, package, gacdir, libdir, false, ref uninstallCount, ref uninstallFailures); WriteLine ("Assemblies uninstalled = {0}", uninstallCount); WriteLine ("Failures = {0}", uninstallFailures); if (uninstallFailures > 0) return 1; break; case Command.UninstallFromList: if (name == null) { WriteLine ("Option " + command_str + " takes 1 argument"); return 1; } if (!UninstallFromList (name, package, gacdir, libdir)) return 1; break; case Command.UninstallSpecific: if (name == null) { WriteLine ("Option " + command_str + " takes 1 argument"); return 1; } if (!UninstallSpecific (name, package, gacdir, libdir)) return 1; break; case Command.List: List (name, gacdir); break; } return 0; } static void Copy (string source, string target, bool v) { try { File.Delete (target); } catch {} File.Copy (source, target, v); } private static bool Install (bool check_refs, string name, string package, string gacdir, string link_gacdir, string libdir, string link_libdir) { string failure_msg = "Failure adding assembly {0} to the cache: "; ArrayList resources; if (!File.Exists (name)) { WriteLine (string.Format (failure_msg, name) + "The system cannot find the file specified."); return false; } Assembly assembly = null; AssemblyName an = null; try { assembly = ReflectionOnlyLoadFrom (name); } catch { WriteLine (string.Format (failure_msg, name) + "The file specified is not a valid assembly."); return false; } an = assembly.GetName (); switch (VerifyStrongName (an, name)) { case VerificationResult.StrongNamed: case VerificationResult.Skipped: break; case VerificationResult.WeakNamed: WriteLine (string.Format (failure_msg, name) + "Attempt to install an assembly without a strong name" + (in_bootstrap ? "(continuing anyway)" : string.Empty)); if (!in_bootstrap) return false; break; case VerificationResult.DelaySigned: WriteLine (string.Format (failure_msg, name) + "Strong name cannot be verified for delay-signed assembly" + (in_bootstrap ? "(continuing anyway)" : string.Empty)); if (!in_bootstrap) return false; break; } resources = new ArrayList (); foreach (string res_name in assembly.GetManifestResourceNames ()) { ManifestResourceInfo res_info = assembly.GetManifestResourceInfo (res_name); if ((res_info.ResourceLocation & ResourceLocation.Embedded) == 0) { if (!File.Exists (res_info.FileName)) { WriteLine (string.Format (failure_msg, name) + "The system cannot find resource " + res_info.FileName); return false; } resources.Add (res_info); } } if (check_refs && !CheckReferencedAssemblies (an)) { WriteLine (string.Format (failure_msg, name) + "Attempt to install an assembly that " + "references non strong named assemblies " + "with -check_refs enabled."); return false; } string [] siblings = { ".config", ".mdb" }; string version_token = an.Version + "_" + an.CultureInfo.Name.ToLower (CultureInfo.InvariantCulture) + "_" + GetStringToken (an.GetPublicKeyToken ()); string full_path = Path.Combine (Path.Combine (gacdir, an.Name), version_token); string asmb_file = Path.GetFileName (name); string asmb_path = Path.Combine (full_path, asmb_file); string asmb_name = assembly.GetName ().Name; if (Path.GetFileNameWithoutExtension (asmb_file) != asmb_name) { WriteLine (string.Format (failure_msg, name) + string.Format ("the filename \"{0}\" doesn't match the assembly name \"{1}\"", asmb_file, asmb_name)); return false; } try { if (Directory.Exists (full_path)) { // Wipe out the directory. This way we ensure old assemblies // config files, and AOTd files are removed. Directory.Delete (full_path, true); } Directory.CreateDirectory (full_path); } catch { WriteLine (string.Format (failure_msg, name) + "gac directories could not be created, " + "possibly permission issues."); return false; } Copy (name, asmb_path, true); var name_pdb = Path.ChangeExtension (name, ".pdb"); if (File.Exists (name_pdb)) { Copy (name_pdb, Path.ChangeExtension (asmb_path, ".pdb"), true); } foreach (string ext in siblings) { string sibling = String.Concat (name, ext); if (File.Exists (sibling)) Copy (sibling, String.Concat (asmb_path, ext), true); } foreach (ManifestResourceInfo resource_info in resources) { try { Copy (resource_info.FileName, Path.Combine (full_path, Path.GetFileName (resource_info.FileName)), true); } catch { WriteLine ("ERROR: Could not install resource file " + resource_info.FileName); Environment.Exit (1); } } if (package != null) { string ref_dir = Path.Combine (libdir, package); string ref_path = Path.Combine (ref_dir, asmb_file); if (File.Exists (ref_path)) File.Delete (ref_path); try { Directory.CreateDirectory (ref_dir); } catch { WriteLine ("ERROR: Could not create package dir file."); Environment.Exit (1); } if (Path.DirectorySeparatorChar == '/') { string pkg_path_abs = Path.Combine (gacdir, Path.Combine (an.Name, Path.Combine (version_token, asmb_file))); string pkg_path = AbsoluteToRelativePath (ref_dir, pkg_path_abs); symlink (pkg_path, ref_path); var pdb_pkg_path = Path.ChangeExtension (pkg_path, ".pdb"); var pdb_ref_path = Path.ChangeExtension (ref_path, ".pdb"); if (File.Exists (pdb_pkg_path)) { symlink (pdb_pkg_path, pdb_ref_path); } else { try { File.Delete (pdb_ref_path); } catch { // Ignore error, just delete files that should not be there. } } foreach (string ext in siblings) { string sibling = String.Concat (pkg_path, ext); string sref = String.Concat (ref_path, ext); if (File.Exists (sibling)) symlink (sibling, sref); else { try { File.Delete (sref); } catch { // Ignore error, just delete files that should not be there. } } } WriteLine ("Package exported to: {0} -> {1}", ref_path, pkg_path); } else { // string link_path = Path.Combine (Path.Combine (link_gacdir, an.Name), version_token); // // We can't use 'link_path' here, since it need not be a valid path at the time 'gacutil' // is run, esp. when invoked in a DESTDIR install. Copy (name, ref_path, true); WriteLine ("Package exported to: " + ref_path); } } WriteLine ("Installed {0} into the gac ({1})", name, gacdir); return true; } //from MonoDevelop.Core.FileService unsafe static string AbsoluteToRelativePath (string baseDirectoryPath, string absPath) { if (!Path.IsPathRooted (absPath) || string.IsNullOrEmpty (baseDirectoryPath)) return absPath; absPath = Path.GetFullPath (absPath); baseDirectoryPath = Path.GetFullPath (baseDirectoryPath).TrimEnd (Path.DirectorySeparatorChar); fixed (char* bPtr = baseDirectoryPath, aPtr = absPath) { var bEnd = bPtr + baseDirectoryPath.Length; var aEnd = aPtr + absPath.Length; char* lastStartA = aEnd; char* lastStartB = bEnd; int indx = 0; // search common base path var a = aPtr; var b = bPtr; while (a < aEnd) { if (*a != *b) break; if (IsSeparator (*a)) { indx++; lastStartA = a + 1; lastStartB = b; } a++; b++; if (b >= bEnd) { if (a >= aEnd || IsSeparator (*a)) { indx++; lastStartA = a + 1; lastStartB = b; } break; } } if (indx == 0) return absPath; if (lastStartA >= aEnd) return "."; // handle case a: some/path b: some/path/deeper... if (a >= aEnd) { if (IsSeparator (*b)) { lastStartA = a + 1; lastStartB = b; } } // look how many levels to go up into the base path int goUpCount = 0; while (lastStartB < bEnd) { if (IsSeparator (*lastStartB)) goUpCount++; lastStartB++; } var size = goUpCount * 2 + goUpCount + aEnd - lastStartA; var result = new char [size]; fixed (char* rPtr = result) { // go paths up var r = rPtr; for (int i = 0; i < goUpCount; i++) { *(r++) = '.'; *(r++) = '.'; *(r++) = Path.DirectorySeparatorChar; } // copy the remaining absulute path while (lastStartA < aEnd) *(r++) = *(lastStartA++); } return new string (result); } } static bool IsSeparator (char ch) { return ch == Path.DirectorySeparatorChar || ch == Path.AltDirectorySeparatorChar || ch == Path.VolumeSeparatorChar; } private static void Uninstall (string name, string package, string gacdir, string libdir, bool listMode, ref int uninstalled, ref int failures) { string [] assembly_pieces = name.Split (new char[] { ',' }); Hashtable asm_info = new Hashtable (); foreach (string item in assembly_pieces) { if (item == String.Empty) continue; string[] pieces = item.Trim ().Split (new char[] { '=' }, 2); if(pieces.Length == 1) asm_info ["assembly"] = pieces [0]; else asm_info [pieces[0].Trim ().ToLower (CultureInfo.InvariantCulture)] = pieces [1]; } string assembly_name = (string) asm_info ["assembly"]; string asmdir = Path.Combine (gacdir, assembly_name); if (!Directory.Exists (asmdir)) { if (listMode) { failures++; WriteLine ("Assembly: " + name); } WriteLine ("No assemblies found that match: " + name); return; } string searchString = GetSearchString (asm_info); string [] directories = Directory.GetDirectories (asmdir, searchString); if (directories.Length == 0) { if (listMode) { failures++; WriteLine ("Assembly: " + name); WriteLine ("No assemblies found that match: " + name); } return; } for (int i = 0; i < directories.Length; i++) { if (listMode && i > 0) break; string dir = directories [i]; string extension = null; if (File.Exists (Path.Combine (dir, assembly_name + ".dll"))) { extension = ".dll"; } else if (File.Exists (Path.Combine (dir, assembly_name + ".exe"))) { extension = ".exe"; } else { failures++; WriteLine("Cannot find the assembly: " + assembly_name); continue; } string assembly_filename = assembly_name + extension; AssemblyName an = AssemblyName.GetAssemblyName ( Path.Combine (dir, assembly_filename)); WriteLine ("Assembly: " + an.FullName); Directory.Delete (dir, true); if (package != null) { string link_dir = Path.Combine (libdir, package); string link = Path.Combine (link_dir, assembly_filename); try { File.Delete (link); } catch { // The file might not exist, happens with // the debugger on make uninstall } if (Directory.GetFiles (link_dir).Length == 0) { WriteLine ("Cleaning package directory, it is empty."); try { Directory.Delete (link_dir); } catch { // Workaround: GetFiles does not list Symlinks } } } uninstalled++; WriteLine ("Uninstalled: " + an.FullName); } if (Directory.GetDirectories (asmdir).Length == 0) { WriteLine ("Cleaning assembly dir, it is empty"); try { Directory.Delete (asmdir); } catch { // Workaround: GetFiles does not list Symlinks } } } private static bool UninstallSpecific (string name, string package, string gacdir, string libdir) { string failure_msg = "Failure to remove assembly from the cache: "; if (!File.Exists (name)) { WriteLine (failure_msg + "The system cannot find the file specified."); return false; } AssemblyName an = null; try { an = AssemblyName.GetAssemblyName (name); } catch { WriteLine (failure_msg + "The file specified is not a valid assembly."); return false; } int uninstallCount = 0; int uninstallFailures = 0; Uninstall (an.FullName.Replace (" ", String.Empty), package, gacdir, libdir, true, ref uninstallCount, ref uninstallFailures); WriteLine ("Assemblies uninstalled = {0}", uninstallCount); WriteLine ("Failures = {0}", uninstallFailures); return (uninstallFailures == 0); } private static void List (string name, string gacdir) { WriteLine ("The following assemblies are installed into the GAC:"); if (name != null) { FilteredList (name, gacdir); return; } int count = 0; DirectoryInfo gacinfo = new DirectoryInfo (gacdir); foreach (DirectoryInfo parent in gacinfo.GetDirectories ()) { foreach (DirectoryInfo dir in parent.GetDirectories ()) { string asmb = Path.Combine (Path.Combine (parent.FullName, dir.Name), parent.Name) + ".dll"; if (File.Exists (asmb)) { WriteLine (AsmbNameFromVersionString (parent.Name, dir.Name)); count++; } } } WriteLine ("Number of items = " + count); } private static void FilteredList (string name, string gacdir) { string [] assembly_pieces = name.Split (new char[] { ',' }); Hashtable asm_info = new Hashtable (); foreach (string item in assembly_pieces) { string[] pieces = item.Trim ().Split (new char[] { '=' }, 2); if(pieces.Length == 1) asm_info ["assembly"] = pieces [0]; else asm_info [pieces[0].Trim ().ToLower (CultureInfo.InvariantCulture)] = pieces [1]; } string asmdir = Path.Combine (gacdir, (string) asm_info ["assembly"]); if (!Directory.Exists (asmdir)) { WriteLine ("Number of items = 0"); return; } string search = GetSearchString (asm_info); string [] dir_list = Directory.GetDirectories (asmdir, search); int count = 0; foreach (string dir in dir_list) { string asmb = Path.Combine (dir, (string) asm_info ["assembly"]) + ".dll"; if (File.Exists (asmb)) { WriteLine (AsmbNameFromVersionString ((string) asm_info ["assembly"], new DirectoryInfo (dir).Name)); count++; } } WriteLine ("Number of items = " + count); } private static bool InstallFromList (bool check_refs, string list_file, string package, string gacdir, string link_gacdir, string libdir, string link_libdir) { StreamReader s = null; int processed, failed; string listdir = Path.GetDirectoryName ( Path.GetFullPath (list_file)); processed = failed = 0; try { s = new StreamReader (list_file); string line; while ((line = s.ReadLine ()) != null) { string file = line.Trim (); if (file.Length == 0) continue; string assemblyPath = Path.Combine (listdir, file); if (!Install (check_refs, assemblyPath, package, gacdir, link_gacdir, libdir, link_libdir)) failed++; processed++; } WriteLine ("Assemblies processed = {0}", processed); WriteLine ("Assemblies installed = {0}", processed - failed); WriteLine ("Failures = {0}", failed); return (failed == 0); } catch (IOException) { WriteLine ("Failed to open assemblies list file " + list_file + "."); return false; } finally { if (s != null) s.Close (); } } private static bool UninstallFromList (string list_file, string package, string gacdir, string libdir) { StreamReader s = null; int failed, uninstalled; failed = uninstalled = 0; try { s = new StreamReader (list_file); string line; while ((line = s.ReadLine ()) != null) { string name = line.Trim (); if (name.Length == 0) continue; Uninstall (line, package, gacdir, libdir, true, ref uninstalled, ref failed); } WriteLine ("Assemblies processed = {0}", uninstalled+failed); WriteLine ("Assemblies uninstalled = {0}", uninstalled); WriteLine ("Failures = {0}", failed); return (failed == 0); } catch (IOException) { WriteLine ("Failed to open assemblies list file " + list_file + "."); return false; } finally { if (s != null) s.Close (); } } private static Universe GetUniverse () { if (_universe == null) { _universe = new Universe (UniverseOptions.MetadataOnly); } return _universe; } private static Assembly ReflectionOnlyLoadFrom (string fileName) { return GetUniverse ().LoadFile (fileName); } private static AssemblyName GetCorlibName () { return GetUniverse ().Mscorlib.GetName (); } private static bool CheckReferencedAssemblies (AssemblyName an) { try { Assembly a = ReflectionOnlyLoadFrom (an.CodeBase); AssemblyName corlib = GetCorlibName (); foreach (AssemblyName ref_an in a.GetReferencedAssemblies ()) { if (ref_an.Name == corlib.Name) // Just do a string compare so we can install on diff versions continue; byte [] pt = ref_an.GetPublicKeyToken (); if (pt == null || pt.Length == 0) { WriteLine ("Assembly " + ref_an.Name + " is not strong named."); return false; } } } catch (Exception e) { WriteLine (e.ToString ()); // This should be removed pre beta3 return false; } return true; } private static string GetSearchString (Hashtable asm_info) { if (asm_info.Keys.Count == 1) return "*"; string version, culture, token; version = asm_info ["version"] as string; version = (version == null ? "*" : version + "*"); culture = asm_info ["culture"] as string; culture = (culture == null ? "*" : (culture == "neutral") ? String.Empty : culture.ToLower (CultureInfo.InvariantCulture)); token = asm_info ["publickeytoken"] as string; token = (token == null ? "*" : token.ToLower (CultureInfo.InvariantCulture)); return String.Format ("{0}_{1}_{2}", version, culture, token); } private static string AsmbNameFromVersionString (string name, string str) { string [] pieces = str.Split ('_'); return String.Format ("{0}, Version={1}, Culture={2}, PublicKeyToken={3}", name, pieces [0], (pieces [1] == String.Empty ? "neutral" : pieces [1]), pieces [2]); } static bool LoadConfig (bool quiet) { System.Reflection.MethodInfo config = typeof (System.Environment).GetMethod ("GetMachineConfigPath", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); if (config != null) { string path = (string) config.Invoke (null, null); bool exist = File.Exists (path); if (!quiet && !exist) Console.WriteLine ("Couldn't find machine.config"); StrongNameManager.LoadConfig (path); return exist; } else if (!quiet) Console.WriteLine ("Couldn't resolve machine.config location (corlib issue)"); // default CSP return false; } // modified copy from sn private static VerificationResult VerifyStrongName (AssemblyName an, string assemblyFile) { byte [] publicKey = StrongNameManager.GetMappedPublicKey (an.GetPublicKeyToken ()); if ((publicKey == null) || (publicKey.Length < 12)) { // no mapping publicKey = an.GetPublicKey (); if ((publicKey == null) || (publicKey.Length < 12)) return VerificationResult.WeakNamed; } // Note: MustVerify is based on the original token (by design). Public key // remapping won't affect if the assembly is verified or not. if (StrongNameManager.MustVerify (new System.Reflection.AssemblyName (an.FullName))) { RSA rsa = CryptoConvert.FromCapiPublicKeyBlob (publicKey, 12); StrongName sn = new StrongName (rsa); if (sn.Verify (assemblyFile)) { return VerificationResult.StrongNamed; } else { return VerificationResult.DelaySigned; } } else { return VerificationResult.Skipped; } } private static bool IsSwitch (string arg) { return (arg [0] == '-' || (arg [0] == '/' && !arg.EndsWith (".dll") && !arg.EndsWith (".exe") && arg.IndexOf ('/', 1) < 0 ) ); } private static Command GetCommand (string arg) { Command c = Command.Unknown; switch (arg) { case "-i": case "/i": case "--install": c = Command.Install; break; case "-il": case "/il": case "--install-from-list": c = Command.InstallFromList; break; case "-u": case "/u": case "/uf": case "--uninstall": c = Command.Uninstall; break; case "-ul": case "/ul": case "--uninstall-from-list": c = Command.UninstallFromList; break; case "-us": case "/us": case "--uninstall-specific": c = Command.UninstallSpecific; break; case "-l": case "/l": case "--list": c = Command.List; break; case "-?": case "/?": case "--help": c = Command.Help; break; } return c; } [DllImport ("libc", SetLastError=true)] public static extern int symlink (string oldpath, string newpath); private static string GetGacDir () { System.Reflection.PropertyInfo gac = typeof (System.Environment).GetProperty ("GacPath", System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.NonPublic); if (gac == null) { WriteLine ("ERROR: Mono runtime not detected, please use " + "the mono runtime for gacutil.exe"); Environment.Exit (1); } System.Reflection.MethodInfo get_gac = gac.GetGetMethod (true); return (string) get_gac.Invoke (null, null); } private static string GetLibDir () { System.Reflection.MethodInfo libdir = typeof (System.Environment).GetMethod ("internalGetGacPath", System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.NonPublic); if (libdir == null) { WriteLine ("ERROR: Mono runtime not detected, please use " + "the mono runtime for gacutil.exe"); Environment.Exit (1); } return Path.Combine ((string)libdir.Invoke (null, null), "mono"); } private static string GetStringToken (byte[] tok) { StringBuilder sb = new StringBuilder (); for (int i = 0; i < tok.Length ; i++) sb.Append (tok[i].ToString ("x2")); return sb.ToString (); } private static string EnsureLib (string dir) { DirectoryInfo d = new DirectoryInfo (dir); if (d.Name == "lib") return dir; return Path.Combine (dir, "lib"); } private static void WriteLine () { if (silent) return; Console.WriteLine (); } private static void WriteLine (string line) { if (silent) return; Console.WriteLine (line); } private static void WriteLine (string line, params object [] p) { if (silent) return; Console.WriteLine (line, p); } private static void Usage () { ShowHelp (false); Environment.Exit (1); } private static void ShowHelp (bool detailed) { WriteLine ("Usage: gacutil.exe [ ]"); WriteLine ("Commands:"); WriteLine ("-i [-check_refs] [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]"); WriteLine ("\tInstalls an assembly into the global assembly cache."); if (detailed) { WriteLine ("\t is the name of the file that contains the " + "\tassembly manifest\n" + "\tExample: -i myDll.dll"); } WriteLine (); WriteLine ("-il [-check_refs] [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]"); WriteLine ("\tInstalls one or more assemblies into the global assembly cache."); if (detailed) { WriteLine ("\t is the path to a test file containing a list of\n" + "\tassembly file paths on separate lines.\n" + "\tExample -il assembly_list.txt\n" + "\t\tassembly_list.txt contents:\n" + "\t\tassembly1.dll\n" + "\t\tassembly2.dll"); } WriteLine (); WriteLine ("-u [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]"); WriteLine ("\tUninstalls an assembly from the global assembly cache."); if (detailed) { WriteLine ("\t is the name of the assembly (partial or\n" + "\tfully qualified) to remove from the global assembly cache. If a \n" + "\tpartial name is specified all matching assemblies will be uninstalled.\n" + "\tExample: -u myDll,Version=1.2.1.0"); } WriteLine (); WriteLine ("-ul [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]"); WriteLine ("\tUninstalls one or more assemblies from the global assembly cache."); if (detailed) { WriteLine ("\t is the path to a test file containing a list of\n" + "\tassembly names on separate lines.\n" + "\tExample -ul assembly_list.txt\n" + "\t\tassembly_list.txt contents:\n" + "\t\tassembly1,Version=1.0.0.0,Culture=en,PublicKeyToken=0123456789abcdef\n" + "\t\tassembly2,Version=2.0.0.0,Culture=en,PublicKeyToken=0123456789abcdef"); } WriteLine (); WriteLine ("-us [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]"); WriteLine ("\tUninstalls an assembly using the specifed assemblies full name."); if (detailed) { WriteLine ("\t is the path to an assembly. The full assembly name\n" + "\tis retrieved from the specified assembly if there is an assembly in\n" + "\tthe GAC with a matching name, it is removed.\n" + "\tExample: -us myDll.dll"); } WriteLine (); WriteLine ("-l [assembly_name] [-root ROOTDIR] [-gacdir GACDIR]"); WriteLine ("\tLists the contents of the global assembly cache."); if (detailed) { WriteLine ("\tWhen the parameter is specified only matching\n" + "\tassemblies are listed."); } WriteLine (); WriteLine ("-?"); WriteLine ("\tDisplays a detailed help screen"); WriteLine (); if (!detailed) return; WriteLine ("Options:"); WriteLine ("-package "); WriteLine ("\tUsed to create a directory in prefix/lib/mono with the name NAME, and a\n" + "\tsymlink is created from NAME/assembly_name to the assembly on the GAC.\n" + "\tThis is used so developers can reference a set of libraries at once."); WriteLine (); WriteLine ("-gacdir "); WriteLine ("\tUsed to specify the GACs base directory. Once an assembly has been installed\n" + "\tto a non standard gacdir the MONO_GAC_PREFIX environment variable must be used\n" + "\tto access the assembly."); WriteLine (); WriteLine ("-root "); WriteLine ("\tUsed by developers integrating this with automake tools or packaging tools\n" + "\tthat require a prefix directory to be specified. The root represents the\n" + "\t\"libdir\" component of a prefix (typically prefix/lib)."); WriteLine (); WriteLine ("-check_refs"); WriteLine ("\tUsed to ensure that the assembly being installed into the GAC does not\n" + "\treference any non strong named assemblies. Assemblies being installed to\n" + "\tthe GAC should not reference non strong named assemblies, however the is\n" + "\tan optional check."); WriteLine (); WriteLine ("Ignored Options:"); WriteLine ("-f"); WriteLine ("\tThe Mono gacutil ignores the -f option to maintain commandline compatibility with"); WriteLine ("\tother gacutils. gacutil will always force the installation of a new assembly."); WriteLine (); WriteLine ("-r "); WriteLine ("\tThe Mono gacutil has not implemented traced references and will emit a warning"); WriteLine ("\twhen this option is used."); } } }