2005-05-13 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / tools / gacutil / driver.cs
1 //
2 // Mono.Tools.GacUtil
3 //
4 // Author(s):
5 //  Todd Berman <tberman@sevenl.net>
6 //  Jackson Harper <jackson@ximian.com>
7 //
8 // Copyright 2003, 2004 Todd Berman 
9 // Copyright 2004 Novell, Inc (http://www.novell.com)
10 //
11
12
13 using System;
14 using System.IO;
15 using System.Text;
16 using System.Reflection;
17 using System.Collections;
18 using System.Globalization;
19 using System.Runtime.InteropServices;
20
21 using Mono.Security;
22
23 namespace Mono.Tools {
24
25         public class Driver {
26
27                 private enum Command {
28                         Unknown,
29                         Install,
30                         InstallFromList,
31                         Uninstall,
32                         UninstallFromList,
33                         UninstallSpecific,
34                         List,
35                         Help
36                 }
37
38                 private static bool silent;
39
40                 public static int Main (string [] args)
41                 {
42                         if (args.Length == 0)
43                                 Usage ();
44
45                         Command command = Command.Unknown;
46                         string command_str = null;
47
48                         string libdir;
49                         string name, package, gacdir, root;
50                         name = package = root = gacdir = null;
51                         bool check_refs = false;
52
53                         // Check for silent arg first so we can supress
54                         // warnings during command line parsing
55                         if (Array.IndexOf (args, "/silent") > -1 || Array.IndexOf (args, "-silent") > -1)
56                                 silent = true;
57
58                         for (int i=0; i<args.Length; i++) {
59                                 if (IsSwitch (args [i])) {
60
61                                         // for cmd line compatibility with other gacutils
62                                         // we always force it though
63                                         if (args [i] == "-f" || args [i] == "/f")
64                                                 continue;
65
66                                         // Ignore this option for now, although we might implement it someday
67                                         if (args [i] == "/r") {
68                                                 WriteLine ("WARNING: gacutil does not support traced references." +
69                                                         "This option is being ignored.");
70                                                 i += 3;
71                                                 continue;
72                                         }
73
74                                         // This is already handled we just dont want to choke on it
75                                         if (args [i] == "-silent" || args [i] == "/silent")
76                                                 continue; 
77
78                                         if (args [i] == "-check_refs" || args [i] == "/check_refs") {
79                                                 check_refs = true;
80                                                 continue;
81                                         }
82
83                                         if (command == Command.Unknown) {
84                                                 command = GetCommand (args [i]);
85                                                 if (command != Command.Unknown) {
86                                                         command_str = args [i];
87                                                         continue;
88                                                 }
89                                         }
90
91                                         if (i + 1 >= args.Length) {
92                                                 Console.WriteLine ("Option " + args [i] + " takes 1 argument");
93                                                 return 1;
94                                         }
95
96                                         switch (args [i]) {
97                                         case "-package":
98                                         case "/package":
99                                                 package = args [++i];
100                                                 break;
101                                         case "-root":
102                                         case "/root":
103                                                 root = args [++i];
104                                                 break;
105                                         case "-gacdir":
106                                         case "/gacdir":
107                                                 gacdir = args [++i];
108                                                 break;
109                                         }
110                                         continue;
111                                 }
112                                 if (name == null)
113                                         name = args [i];
114                                 else
115                                         name += args [i];
116                         }
117
118                         if (command == Command.Unknown && IsSwitch (args [0])) {
119                                 Console.WriteLine ("Unknown command: " + args [0]);
120                                 return 1;
121                         } else if (command == Command.Unknown) {
122                                 Usage ();
123                         } else if (command == Command.Help) {
124                                 ShowHelp (true);
125                                 return 1;
126                         }
127
128                         if (gacdir == null) {
129                                 gacdir = GetGacDir ();
130                                 libdir = GetLibDir ();
131                         } else {
132                                 gacdir = EnsureLib (gacdir);
133                                 libdir = Path.Combine (gacdir, "mono");
134                                 gacdir = Path.Combine (libdir, "gac");
135                         }
136
137                         string link_gacdir = gacdir;
138                         string link_libdir = libdir;
139                         if (root != null) {
140                                 libdir = Path.Combine (root, "mono");
141                                 gacdir = Path.Combine (libdir, "gac");
142                         }
143
144                         switch (command) {
145                         case Command.Install:
146                                 if (name == null) {
147                                         WriteLine ("Option " + command_str + " takes 1 argument");
148                                         return 1;
149                                 }
150                                 if (!Install (check_refs, name, package, gacdir, link_gacdir, libdir, link_libdir))
151                                         return 1;
152                                 break;
153                         case Command.InstallFromList:
154                                 if (name == null) {
155                                         WriteLine ("Option " + command_str + " takes 1 argument");
156                                         return 1;
157                                 }
158                                 if (!InstallFromList (check_refs, name, package, gacdir, link_gacdir, libdir, link_libdir))
159                                         return 1;
160                                 break;
161                         case Command.Uninstall:
162                                 if (name == null) {
163                                         WriteLine ("Option " + command_str + " takes 1 argument");
164                                         return 1;
165                                 }
166                                 if (!Uninstall (name, package, gacdir, libdir))
167                                         Environment.Exit (1);
168                                 break;
169                         case Command.UninstallFromList:
170                                 if (name == null) {
171                                         WriteLine ("Option " + command_str + " takes 1 argument");
172                                         return 1;
173                                 }
174                                 if (!UninstallFromList (name, package, gacdir, libdir))
175                                         return 1;
176                                 break;
177                         case Command.UninstallSpecific:
178                                 if (name == null) {
179                                         WriteLine ("Opetion " + command_str + " takes 1 argument");
180                                         return 1;
181                                 }
182                                 if (!UninstallSpecific (name, package, gacdir, libdir))
183                                         return 1;
184                                 break;
185                         case Command.List:
186                                 List (name, gacdir);
187                                 break;
188                         }
189
190                         return 0;
191                 }
192
193                 private static bool Install (bool check_refs, string name, string package,
194                                 string gacdir, string link_gacdir, string libdir, string link_libdir)
195                 {
196                         string failure_msg = "Failure adding assembly to the cache: ";
197
198                         if (!File.Exists (name)) {
199                                 WriteLine (failure_msg + "The system cannot find the file specified.");
200                                 Environment.Exit (1);
201                         }
202
203                         AssemblyName an = null;
204                         byte [] pub_tok;
205
206                         try {
207                                 an = AssemblyName.GetAssemblyName (name);
208                         } catch {
209                                 WriteLine (failure_msg + "The file specified is not a valid assembly.");
210                                 return false;
211                         }
212
213                         pub_tok = an.GetPublicKeyToken ();
214                         if (pub_tok == null || pub_tok.Length == 0) {
215                                 WriteLine (failure_msg + "Attempt to install an assembly without a strong name.");
216                                 return false;
217                         }
218
219                         if (check_refs && !CheckReferencedAssemblies (an)) {
220                                 WriteLine (failure_msg + "Attempt to install an assembly that references non " +
221                                                 "strong named assemblies with -check_refs enabled.");
222                                 return false;
223                         }
224
225                         string [] siblings = { ".config", ".mdb" };
226                         string version_token = an.Version + "_" +
227                                                an.CultureInfo.Name.ToLower (CultureInfo.InvariantCulture) + "_" +
228                                                GetStringToken (pub_tok);
229                         string full_path = Path.Combine (Path.Combine (gacdir, an.Name), version_token);
230                         string asmb_file = Path.GetFileName (name);
231                         string asmb_path = Path.Combine (full_path, asmb_file);
232
233                         try {
234                                 if (Directory.Exists (full_path)) {
235                                         // Wipe out the directory. This way we ensure old assemblies    
236                                         // config files, and AOTd files are removed.
237                                         Directory.Delete (full_path, true);
238                                 }
239                                 Directory.CreateDirectory (full_path);
240                         } catch {
241                                 WriteLine (failure_msg + "gac directories could not be created, " +
242                                                 "possibly permission issues.");
243                                 return false;
244                         }
245
246                         File.Copy (name, asmb_path, true);
247
248                         foreach (string ext in siblings) {
249                                 string sibling = String.Concat (name, ext);
250                                 if (File.Exists (sibling))
251                                         File.Copy (sibling, String.Concat (asmb_path, ext), true);
252                         }
253
254                         if (package != null) {
255                                 string ref_dir = Path.Combine (libdir, package);
256                                 string ref_path = Path.Combine (ref_dir, asmb_file);
257
258                                 if (File.Exists (ref_path))
259                                         File.Delete (ref_path);
260                                 try {
261                                         Directory.CreateDirectory (ref_dir);
262                                 } catch {
263                                         WriteLine ("ERROR: Could not create package dir file.");
264                                         Environment.Exit (1);
265                                 }
266                                 if (Path.DirectorySeparatorChar == '/') {
267                                         string pkg_path = "../gac/" + an.Name + "/" + version_token + "/" + asmb_file;
268                                         symlink (pkg_path, ref_path);
269                                         WriteLine ("Package exported to: {0} -> {1}", ref_path, pkg_path);
270                                 } else {
271                                         // string link_path = Path.Combine (Path.Combine (link_gacdir, an.Name), version_token);
272                                         //
273                                         // We can't use 'link_path' here, since it need not be a valid path at the time 'gacutil'
274                                         // is run, esp. when invoked in a DESTDIR install.
275                                         File.Copy (name, ref_path);
276                                         WriteLine ("Package exported to: " + ref_path);
277                                 }
278                         }
279
280                         WriteLine ("{0} installed into the gac ({1})", an.Name, gacdir);
281                         return true;
282                 }
283
284                 private static bool Uninstall (string name, string package, string gacdir, string libdir)
285                 {
286                         string [] assembly_pieces = name.Split (new char[] { ',' });
287                         Hashtable asm_info = new Hashtable ();
288
289                         foreach (string item in assembly_pieces) {
290                                 string[] pieces = item.Trim ().Split (new char[] { '=' }, 2);
291                                 if(pieces.Length == 1)
292                                         asm_info ["assembly"] = pieces [0];
293                                 else
294                                         asm_info [pieces[0].Trim ().ToLower (CultureInfo.InvariantCulture)] = pieces [1];
295                         }
296
297                         string asmdir = Path.Combine (gacdir, (string) asm_info ["assembly"]);
298                         if (!Directory.Exists (asmdir)) {
299                                 WriteLine ("No assemblies found that match: " + name);
300                                 return false;
301                         }
302
303                         string searchString = GetSearchString (asm_info);
304                         string [] directories = Directory.GetDirectories (asmdir, searchString);
305
306                         foreach (string dir in directories) {
307                                 Directory.Delete (dir, true);
308                                 if (package != null) {
309                                         string link_dir = Path.Combine (libdir, package);
310                                         string link = Path.Combine (link_dir, (string) asm_info ["assembly"] + ".dll");
311                                         File.Delete (link);
312                                         if (Directory.GetFiles (link_dir).Length == 0) {
313                                                 WriteLine ("Cleaning package directory, it is empty.");
314                                                 Directory.Delete (link_dir);
315                                         }
316                                 }
317                                 WriteLine ("Assembly removed from the gac.");
318                         }
319
320                         if(Directory.GetDirectories (asmdir).Length == 0) {
321                                 WriteLine ("Cleaning assembly dir, its empty");
322                                 Directory.Delete (asmdir);
323                         }
324
325                         return true;
326                 }
327
328                 private static bool UninstallSpecific (string name, string package,
329                                 string gacdir, string libdir)
330                 {
331                         string failure_msg = "Failure to remove assembly from the cache: ";
332
333                         if (!File.Exists (name)) {
334                                 WriteLine (failure_msg + "The system cannot find the file specified.");
335                                 return false;
336                         }
337
338                         AssemblyName an = null;
339
340                         try {
341                                 an = AssemblyName.GetAssemblyName (name);
342                         } catch {
343                                 WriteLine (failure_msg + "The file specified is not a valid assembly.");
344                                 return false;
345                         }
346
347                         return Uninstall (an.FullName.Replace (" ", String.Empty),
348                                         package, gacdir, libdir);
349                 }
350
351                 private static void List (string name, string gacdir)
352                 {
353                         WriteLine ("The following assemblies are installed into the GAC:");
354
355                         if (name != null) {
356                                 FilteredList (name, gacdir);
357                                 return;
358                         }
359
360                         int count = 0;
361                         DirectoryInfo gacinfo = new DirectoryInfo (gacdir);
362                         foreach (DirectoryInfo parent in gacinfo.GetDirectories ()) {
363                                 foreach (DirectoryInfo dir in parent.GetDirectories ()) {
364                                         string asmb = Path.Combine (Path.Combine (parent.FullName, dir.Name), parent.Name) + ".dll";
365                                         if (File.Exists (asmb)) {
366                                                 WriteLine (AsmbNameFromVersionString (parent.Name, dir.Name));
367                                                 count++;
368                                         }
369                                 }
370                         }
371                         WriteLine ("Number of items = " + count);
372                 }
373
374                 private static void FilteredList (string name, string gacdir)
375                 {
376                         string [] assembly_pieces = name.Split (new char[] { ',' });
377                         Hashtable asm_info = new Hashtable ();
378
379                         foreach (string item in assembly_pieces) {
380                                 string[] pieces = item.Trim ().Split (new char[] { '=' }, 2);
381                                 if(pieces.Length == 1)
382                                         asm_info ["assembly"] = pieces [0];
383                                 else
384                                         asm_info [pieces[0].Trim ().ToLower (CultureInfo.InvariantCulture)] = pieces [1];
385                         }
386                         
387                         string asmdir = Path.Combine (gacdir, (string) asm_info ["assembly"]);
388                         if (!Directory.Exists (asmdir)) {
389                                 WriteLine ("Number of items = 0");
390                                 return;
391                         }
392                         string search = GetSearchString (asm_info);
393                         string [] dir_list = Directory.GetDirectories (asmdir, search);
394
395                         int count = 0;
396                         foreach (string dir in dir_list) {
397                                 string asmb = Path.Combine (dir, (string) asm_info ["assembly"]) + ".dll";
398                                 if (File.Exists (asmb)) {
399                                         WriteLine (AsmbNameFromVersionString ((string) asm_info ["assembly"],
400                                                                    new DirectoryInfo (dir).Name));
401                                         count++;
402                                 }
403                         }
404                         WriteLine ("Number of items = " + count);
405                 }
406
407                 private static bool InstallFromList (bool check_refs, string list_file, string package,
408                                 string gacdir, string link_gacdir, string libdir, string link_libdir)
409                 {
410                         StreamReader s = null;
411                         int processed, failed;
412
413                         processed = failed = 0;
414
415                         try {
416                                 s = new StreamReader (list_file);
417
418                                 string line;
419                                 while ((line = s.ReadLine ()) != null) {
420                                         if (!Install (check_refs, line, package, gacdir, link_gacdir,
421                                                             libdir, link_libdir))
422                                                 failed++;
423                                         processed++;
424                                                         
425                                 }
426                         } catch (IOException ioe) {
427                                 WriteLine ("Failed to open assemblies list file " + list_file + ".");
428                                 return false;
429                         } finally {
430                                 if (s != null)
431                                         s.Close ();
432                         }
433
434                         return true;
435                 }
436
437                 private static bool UninstallFromList (string list_file, string package,
438                                 string gacdir, string libdir)
439                 {
440                         StreamReader s = null;
441                         int processed, failed;
442
443                         processed = failed = 0;
444
445                         try {
446                                 s = new StreamReader (list_file);
447
448                                 string line;
449                                 while ((line = s.ReadLine ()) != null) {
450                                         if (!Uninstall (line, package, gacdir, libdir))
451                                                 failed++;
452                                         processed++;
453                                 }
454                         } catch (IOException ioe) {
455                                 WriteLine ("Failed to open assemblies list file " + list_file + ".");
456                                 return false;
457                         } finally {
458                                 if (s != null)
459                                         s.Close ();
460                         }
461
462                         return true;
463                 }
464
465                 private static bool CheckReferencedAssemblies (AssemblyName an)
466                 {
467                         AppDomain d = null;
468                         try {
469                                 Assembly a = Assembly.LoadFrom (an.CodeBase);
470                                 AssemblyName corlib = typeof (object).Assembly.GetName ();
471
472                                 foreach (AssemblyName ref_an in a.GetReferencedAssemblies ()) {
473                                         if (ref_an.Name == corlib.Name) // Just do a string compare so we can install on diff versions
474                                                 continue;
475                                         byte [] pt = ref_an.GetPublicKeyToken ();
476                                         if (pt == null || pt.Length == 0) {
477                                                 WriteLine ("Assembly " + ref_an.Name + " is not strong named.");
478                                                 return false;
479                                         }
480                                 }
481                         } catch  (Exception e) {
482                                 WriteLine (e.ToString ()); // This should be removed pre beta3
483                                 return false;
484                         } finally {
485                                 if (d != null) {
486                                         try {
487                                                 AppDomain.Unload (d);
488                                         } catch { }
489                                 }
490                         }
491
492                         return true;
493                 }
494
495                 private static string GetSearchString (Hashtable asm_info)
496                 {
497                         if (asm_info.Keys.Count == 1)
498                                 return "*";
499                         string version, culture, token;
500
501                         version = asm_info ["version"] as string;
502                         version = (version == null ? "*" : version);
503                         culture = asm_info ["culture"] as string;
504                         culture = (culture == null ? "*" : culture.ToLower (CultureInfo.InvariantCulture));
505                         token = asm_info ["publickeytoken"] as string;
506                         token = (token == null ? "*" : token.ToLower (CultureInfo.InvariantCulture));
507                         
508                         return String.Format ("{0}_{1}_{2}", version, culture, token);
509                 }
510
511                 private static string AsmbNameFromVersionString (string name, string str)
512                 {
513                         string [] pieces = str.Split ('_');
514                         return String.Format ("{0}, Version={1}, Culture={2}, PublicKeyToken={3}",
515                                         name, pieces [0], (pieces [1] == String.Empty ? "neutral" : pieces [1]),
516                                         pieces [2]);
517                 }
518
519                 private static bool IsSwitch (string arg)
520                 {
521                         return (arg [0] == '-' || arg [0] == '/');
522                 }
523
524                 private static Command GetCommand (string arg)
525                 {
526                         Command c = Command.Unknown;
527
528                         switch (arg) {
529                         case "-i":
530                         case "/i":
531                         case "--install":
532                                 c = Command.Install;
533                                 break;
534                         case "-il":
535                         case "/il":
536                         case "--install-from-list":
537                                 c = Command.InstallFromList;
538                                 break;
539                         case "-u":
540                         case "/u":
541                         case "/uf":
542                         case "--uninstall":
543                                 c = Command.Uninstall;
544                                 break;
545                         case "-ul":
546                         case "/ul":
547                         case "--uninstall-from-list":
548                                 c = Command.UninstallFromList;
549                                 break;
550                         case "-us":
551                         case "/us":
552                         case "--uninstall-specific":
553                                 c = Command.UninstallSpecific;
554                                 break;
555                         case "-l":
556                         case "/l":
557                         case "--list":
558                                 c = Command.List;
559                                 break;
560                         case "-?":
561                         case "/?":
562                         case "--help":
563                                 c = Command.Help;
564                                 break;
565                         }
566                         return c;        
567                 }
568
569                 [DllImport ("libc", SetLastError=true)]
570                 public static extern int symlink (string oldpath, string newpath);
571
572                 private static string GetGacDir () {
573                         PropertyInfo gac = typeof (System.Environment).GetProperty ("GacPath",
574                                         BindingFlags.Static|BindingFlags.NonPublic);
575                         if (gac == null) {
576                                 WriteLine ("ERROR: Mono runtime not detected, please use " +
577                                                 "the mono runtime for gacutil.exe");
578                                 Environment.Exit (1);
579                         }
580                         MethodInfo get_gac = gac.GetGetMethod (true);
581                         return (string) get_gac.Invoke (null, null);
582                 }
583
584                 private static string GetLibDir () {
585                         MethodInfo libdir = typeof (System.Environment).GetMethod ("internalGetGacPath",
586                                         BindingFlags.Static|BindingFlags.NonPublic);
587                         if (libdir == null) {
588                                 WriteLine ("ERROR: Mono runtime not detected, please use " +
589                                                 "the mono runtime for gacutil.exe");
590                                 Environment.Exit (1);
591                         }
592                         return Path.Combine ((string)libdir.Invoke (null, null), "mono");
593                 }
594
595                 private static string GetStringToken (byte[] tok)
596                 {
597                         StringBuilder sb = new StringBuilder ();
598                         for (int i = 0; i < tok.Length ; i++)
599                                 sb.Append (tok[i].ToString ("x2"));
600                         return sb.ToString ();
601                 }
602
603                 private static string CombinePaths (string a, string b)
604                 {
605                         string dsc = Path.DirectorySeparatorChar.ToString ();
606                         string sep = (a.EndsWith (dsc) ? String.Empty : dsc);
607                         string end = (b.StartsWith (dsc) ? b.Substring (1) : b);
608                         return String.Concat (a, sep, end);
609                 }
610
611                 private static string EnsureLib (string dir)
612                 {
613                         DirectoryInfo d = new DirectoryInfo (dir);
614                         if (d.Name == "lib")
615                                 return dir;
616                         return Path.Combine (dir, "lib");
617                 }
618
619                 private static void WriteLine ()
620                 {
621                         if (silent)
622                                 return;
623                         Console.WriteLine ();
624                 }
625
626                 private static void WriteLine (string line)
627                 {
628                         if (silent)
629                                 return;
630                         Console.WriteLine (line);
631                 }
632
633                 private static void WriteLine (string line, params object [] p)
634                 {
635                         if (silent)
636                                 return; 
637                         Console.WriteLine (line, p);
638                 }
639
640                 private static void Usage ()
641                 {
642                         ShowHelp (false);
643                         Environment.Exit (1);
644                 }
645
646                 private static void ShowHelp (bool detailed)
647                 {
648                         WriteLine ("Usage: gacutil.exe <commands> [ <options> ]");
649                         WriteLine ("Commands:");
650
651                         WriteLine ("-i <assembly_path> [-check_refs] [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
652                         WriteLine ("\tInstalls an assembly into the global assembly cache.");
653                         if (detailed) {
654                                 WriteLine ("\t<assembly_path> is the name of the file that contains the " +
655                                                 "\tassembly manifest\n" +
656                                                 "\tExample: -i myDll.dll");
657                         }
658                         WriteLine ();
659
660                         WriteLine ("-il <assembly_list_file> [-check_refs] [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
661                         WriteLine ("\tInstalls one or more assemblies into the global assembly cache.");
662                         if (detailed) {
663                                 WriteLine ("\t<assembly_list_file> is the path to a test file containing a list of\n" +
664                                                 "\tassembly file paths on separate lines.\n" +
665                                                 "\tExample -il assembly_list.txt\n" +
666                                                 "\t\tassembly_list.txt contents:\n" +
667                                                 "\t\tassembly1.dll\n" +
668                                                 "\t\tassembly2.dll");
669                         }
670                         WriteLine ();
671                         
672                         WriteLine ("-u <assembly_display_name> [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
673                         WriteLine ("\tUninstalls an assembly from the global assembly cache.");
674                         if (detailed) {
675                                 WriteLine ("\t<assembly_display_name> is the name of the assembly (partial or\n" +
676                                                 "\tfully qualified) to remove from the global assembly cache. If a \n" +
677                                                 "\tpartial name is specified all matching assemblies will be uninstalled.\n" +
678                                                 "\tExample: -u myDll,Version=1.2.1.0");
679                         }
680                         WriteLine ();
681
682                         WriteLine ("-ul <assembly_list_file> [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
683                         WriteLine ("\tUninstalls one or more assemblies from the global assembly cache.");
684                         if (detailed) {
685                                 WriteLine ("\t<assembly_list_file> is the path to a test file containing a list of\n" +
686                                                 "\tassembly names on separate lines.\n" +
687                                                 "\tExample -ul assembly_list.txt\n" +
688                                                 "\t\tassembly_list.txt contents:\n" +
689                                                 "\t\tassembly1,Version=1.0.0.0,Culture=en,PublicKeyToken=0123456789abcdef\n" +
690                                                 "\t\tassembly2,Version=2.0.0.0,Culture=en,PublicKeyToken=0123456789abcdef");
691                         }
692                         WriteLine ();
693
694                         WriteLine ("-us <assembly_path> [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
695                         WriteLine ("\tUninstalls an assembly using the specifed assemblies full name.");
696                         if (detailed) {
697                                 WriteLine ("\t<assembly path> is the path to an assembly. The full assembly name\n" +
698                                                 "\tis retrieved from the specified assembly if there is an assembly in\n" +
699                                                 "\tthe GAC with a matching name, it is removed.\n" +
700                                                 "\tExample: -us myDll.dll");
701                         }
702                         WriteLine ();
703
704                         WriteLine ("-l [assembly_name] [-root ROOTDIR] [-gacdir GACDIR]");
705                         WriteLine ("\tLists the contents of the global assembly cache.");
706                         if (detailed) {
707                                 WriteLine ("\tWhen the <assembly_name> parameter is specified only matching\n" +
708                                                 "\tassemblies are listed.");
709                         }
710                         WriteLine ();
711
712                         WriteLine ("-?");
713                         WriteLine ("\tDisplays a detailed help screen");
714                         WriteLine ();
715
716                         if (!detailed)
717                                 return;
718
719                         WriteLine ("Options:");
720                         WriteLine ("-package <NAME>");
721                         WriteLine ("\tUsed to create a directory in prefix/lib/mono with the name NAME, and a\n" +
722                                         "\tsymlink is created from NAME/assembly_name to the assembly on the GAC.\n" +
723                                         "\tThis is used so developers can reference a set of libraries at once.");
724                         WriteLine ();
725
726                         WriteLine ("-gacdir <GACDIR>");
727                         WriteLine ("\tUsed to specify the GACs base directory. Once an assembly has been installed\n" +
728                                         "\tto a non standard gacdir the MONO_GAC_PREFIX environment variable must be used\n" +
729                                         "\tto access the assembly.");
730                         WriteLine ();
731
732                         WriteLine ("-root <ROOTDIR>");
733                         WriteLine ("\tUsed by developers integrating this with automake tools or packaging tools\n" +
734                                         "\tthat require a prefix directory to  be specified. The root represents the\n" +
735                                         "\t\"libdir\" component of a prefix (typically prefix/lib).");
736                         WriteLine ();
737
738                         WriteLine ("-check_refs");
739                         WriteLine ("\tUsed to ensure that the assembly being installed into the GAC does not\n" +
740                                         "\treference any non strong named assemblies. Assemblies being installed to\n" +
741                                         "\tthe GAC should not reference non strong named assemblies, however the is\n" +
742                                         "\tan optional check.");
743
744                         WriteLine ();
745                         WriteLine ("Ignored Options:");
746                         WriteLine ("-f");
747                         WriteLine ("\tThe Mono gacutil ignores the -f option to maintian commandline compatibility with");
748                         WriteLine ("\tother gacutils. gacutil will always force the installation of a new assembly.");
749
750                         WriteLine ();
751                         WriteLine ("-r <reference_scheme> <reference_id> <description>");
752                         WriteLine ("\tThe Mono gacutil has not implemented traced references and will emit a warning");
753                         WriteLine ("\twhen this option is used.");
754                 }
755         }
756 }
757