Rename the tool
[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 link_path = Path.Combine (Path.Combine (link_gacdir, an.Name), version_token);
256                                 string ref_dir = Path.Combine (libdir, package);
257                                 string ref_path = Path.Combine (ref_dir, asmb_file);
258
259                                 if (File.Exists (ref_path))
260                                         File.Delete (ref_path);
261                                 try {
262                                         Directory.CreateDirectory (ref_dir);
263                                 } catch {
264                                         WriteLine ("ERROR: Could not create package dir file.");
265                                         Environment.Exit (1);
266                                 }
267                                 Symlink (name, Path.Combine (link_path, asmb_file), ref_path);
268
269                                 WriteLine ("Package exported to: " + ref_path + " -> " +
270                                                 Path.Combine (link_path, asmb_file));
271
272                         }
273
274                         WriteLine ("{0} installed into the gac ({1})", an.Name, gacdir);
275                         return true;
276                 }
277
278                 private static bool Uninstall (string name, string package, string gacdir, string libdir)
279                 {
280                         string [] assembly_pieces = name.Split (new char[] { ',' });
281                         Hashtable asm_info = new Hashtable ();
282
283                         foreach (string item in assembly_pieces) {
284                                 string[] pieces = item.Trim ().Split (new char[] { '=' }, 2);
285                                 if(pieces.Length == 1)
286                                         asm_info ["assembly"] = pieces [0];
287                                 else
288                                         asm_info [pieces[0].Trim ().ToLower (CultureInfo.InvariantCulture)] = pieces [1];
289                         }
290
291                         string asmdir = Path.Combine (gacdir, (string) asm_info ["assembly"]);
292                         if (!Directory.Exists (asmdir)) {
293                                 WriteLine ("No assemblies found that match: " + name);
294                                 return false;
295                         }
296
297                         string searchString = GetSearchString (asm_info);
298                         string [] directories = Directory.GetDirectories (asmdir, searchString);
299
300                         foreach (string dir in directories) {
301                                 Directory.Delete (dir, true);
302                                 if (package != null) {
303                                         string link_dir = Path.Combine (libdir, package);
304                                         string link = Path.Combine (link_dir, (string) asm_info ["assembly"] + ".dll");
305                                         File.Delete (link);
306                                         if (Directory.GetFiles (link_dir).Length == 0) {
307                                                 WriteLine ("Cleaning package directory, it is empty.");
308                                                 Directory.Delete (link_dir);
309                                         }
310                                 }
311                                 WriteLine ("Assembly removed from the gac.");
312                         }
313
314                         if(Directory.GetDirectories (asmdir).Length == 0) {
315                                 WriteLine ("Cleaning assembly dir, its empty");
316                                 Directory.Delete (asmdir);
317                         }
318
319                         return true;
320                 }
321
322                 private static bool UninstallSpecific (string name, string package,
323                                 string gacdir, string libdir)
324                 {
325                         string failure_msg = "Failure to remove assembly from the cache: ";
326
327                         if (!File.Exists (name)) {
328                                 WriteLine (failure_msg + "The system cannot find the file specified.");
329                                 return false;
330                         }
331
332                         AssemblyName an = null;
333
334                         try {
335                                 an = AssemblyName.GetAssemblyName (name);
336                         } catch {
337                                 WriteLine (failure_msg + "The file specified is not a valid assembly.");
338                                 return false;
339                         }
340
341                         return Uninstall (an.FullName.Replace (" ", String.Empty),
342                                         package, gacdir, libdir);
343                 }
344
345                 private static void List (string name, string gacdir)
346                 {
347                         WriteLine ("The following assemblies are installed into the GAC:");
348
349                         if (name != null) {
350                                 FilteredList (name, gacdir);
351                                 return;
352                         }
353
354                         int count = 0;
355                         DirectoryInfo gacinfo = new DirectoryInfo (gacdir);
356                         foreach (DirectoryInfo parent in gacinfo.GetDirectories ()) {
357                                 foreach (DirectoryInfo dir in parent.GetDirectories ()) {
358                                         WriteLine (AsmbNameFromVersionString (parent.Name, dir.Name));
359                                         count++;
360                                 }
361                         }
362                         WriteLine ("Number of items = " + count);
363                 }
364
365                 private static void FilteredList (string name, string gacdir)
366                 {
367                         string [] assembly_pieces = name.Split (new char[] { ',' });
368                         Hashtable asm_info = new Hashtable ();
369
370                         foreach (string item in assembly_pieces) {
371                                 string[] pieces = item.Trim ().Split (new char[] { '=' }, 2);
372                                 if(pieces.Length == 1)
373                                         asm_info ["assembly"] = pieces [0];
374                                 else
375                                         asm_info [pieces[0].Trim ().ToLower (CultureInfo.InvariantCulture)] = pieces [1];
376                         }
377                         
378                         string asmdir = Path.Combine (gacdir, (string) asm_info ["assembly"]);
379                         if (!Directory.Exists (asmdir)) {
380                                 WriteLine ("Number of items = 0");
381                                 return;
382                         }
383                         string search = GetSearchString (asm_info);
384                         string [] dir_list = Directory.GetDirectories (asmdir, search);
385
386                         int count = 0;
387                         foreach (string dir in dir_list) {
388                                 WriteLine (AsmbNameFromVersionString ((string) asm_info ["assembly"],
389                                                 new DirectoryInfo (dir).Name));
390                                 count++;
391                         }
392                         WriteLine ("Number of items = " + count);
393                 }
394
395                 private static bool InstallFromList (bool check_refs, string list_file, string package,
396                                 string gacdir, string link_gacdir, string libdir, string link_libdir)
397                 {
398                         StreamReader s = null;
399                         int processed, failed;
400
401                         processed = failed = 0;
402
403                         try {
404                                 s = new StreamReader (list_file);
405
406                                 string line;
407                                 while ((line = s.ReadLine ()) != null) {
408                                         if (!Install (check_refs, line, package, gacdir, link_gacdir,
409                                                             libdir, link_libdir))
410                                                 failed++;
411                                         processed++;
412                                                         
413                                 }
414                         } catch (IOException ioe) {
415                                 WriteLine ("Failed to open assemblies list file " + list_file + ".");
416                                 return false;
417                         } finally {
418                                 if (s != null)
419                                         s.Close ();
420                         }
421
422                         return true;
423                 }
424
425                 private static bool UninstallFromList (string list_file, string package,
426                                 string gacdir, string libdir)
427                 {
428                         StreamReader s = null;
429                         int processed, failed;
430
431                         processed = failed = 0;
432
433                         try {
434                                 s = new StreamReader (list_file);
435
436                                 string line;
437                                 while ((line = s.ReadLine ()) != null) {
438                                         if (!Uninstall (line, package, gacdir, libdir))
439                                                 failed++;
440                                         processed++;
441                                 }
442                         } catch (IOException ioe) {
443                                 WriteLine ("Failed to open assemblies list file " + list_file + ".");
444                                 return false;
445                         } finally {
446                                 if (s != null)
447                                         s.Close ();
448                         }
449
450                         return true;
451                 }
452
453                 private static bool CheckReferencedAssemblies (AssemblyName an)
454                 {
455                         AppDomain d = null;
456                         try {
457                                 Assembly a = Assembly.LoadFrom (an.CodeBase);
458                                 AssemblyName corlib = typeof (object).Assembly.GetName ();
459
460                                 foreach (AssemblyName ref_an in a.GetReferencedAssemblies ()) {
461                                         if (ref_an.Name == corlib.Name) // Just do a string compare so we can install on diff versions
462                                                 continue;
463                                         byte [] pt = ref_an.GetPublicKeyToken ();
464                                         if (pt == null || pt.Length == 0) {
465                                                 WriteLine ("Assembly " + ref_an.Name + " is not strong named.");
466                                                 return false;
467                                         }
468                                 }
469                         } catch  (Exception e) {
470                                 WriteLine (e.ToString ()); // This should be removed pre beta3
471                                 return false;
472                         } finally {
473                                 if (d != null) {
474                                         try {
475                                                 AppDomain.Unload (d);
476                                         } catch { }
477                                 }
478                         }
479
480                         return true;
481                 }
482
483                 private static string GetSearchString (Hashtable asm_info)
484                 {
485                         if (asm_info.Keys.Count == 1)
486                                 return "*";
487                         string version, culture, token;
488
489                         version = asm_info ["version"] as string;
490                         version = (version == null ? "*" : version);
491                         culture = asm_info ["culture"] as string;
492                         culture = (culture == null ? "*" : culture.ToLower (CultureInfo.InvariantCulture));
493                         token = asm_info ["publickeytoken"] as string;
494                         token = (token == null ? "*" : token.ToLower (CultureInfo.InvariantCulture));
495                         
496                         return String.Format ("{0}_{1}_{2}", version, culture, token);
497                 }
498
499                 private static string AsmbNameFromVersionString (string name, string str)
500                 {
501                         string [] pieces = str.Split ('_');
502                         return String.Format ("{0}, Version={1}, Culture={2}, PublicKeyToken={3}",
503                                         name, pieces [0], (pieces [1] == String.Empty ? "neutral" : pieces [1]),
504                                         pieces [2]);
505                 }
506
507                 private static bool IsSwitch (string arg)
508                 {
509                         return (arg [0] == '-' || arg [0] == '/');
510                 }
511
512                 private static Command GetCommand (string arg)
513                 {
514                         Command c = Command.Unknown;
515
516                         switch (arg) {
517                         case "-i":
518                         case "/i":
519                         case "--install":
520                                 c = Command.Install;
521                                 break;
522                         case "-il":
523                         case "/il":
524                         case "--install-from-list":
525                                 c = Command.InstallFromList;
526                                 break;
527                         case "-u":
528                         case "/u":
529                         case "/uf":
530                         case "--uninstall":
531                                 c = Command.Uninstall;
532                                 break;
533                         case "-ul":
534                         case "/ul":
535                         case "--uninstall-from-list":
536                                 c = Command.UninstallFromList;
537                                 break;
538                         case "-us":
539                         case "/us":
540                         case "--uninstall-specific":
541                                 c = Command.UninstallSpecific;
542                                 break;
543                         case "-l":
544                         case "/l":
545                         case "--list":
546                                 c = Command.List;
547                                 break;
548                         case "-?":
549                         case "/?":
550                         case "--help":
551                                 c = Command.Help;
552                                 break;
553                         }
554                         return c;        
555                 }
556
557                 // name = name of dll being installed
558                 // oldpath = path the symlink points to -- cannot be assumed to be a valid file
559                 // newpath = name of symlink
560                 private static void Symlink (string name, string oldpath, string newpath) {
561                         if (Path.DirectorySeparatorChar == '/') {
562                                 symlink (oldpath, newpath);
563                         } else {
564                                 File.Copy (name, newpath);
565                         }
566                 }
567
568                 [DllImport ("libc", SetLastError=true)]
569                 public static extern int symlink (string oldpath, string newpath);
570
571                 private static string GetGacDir () {
572                         PropertyInfo gac = typeof (System.Environment).GetProperty ("GacPath",
573                                         BindingFlags.Static|BindingFlags.NonPublic);
574                         if (gac == null) {
575                                 WriteLine ("ERROR: Mono runtime not detected, please use " +
576                                                 "the mono runtime for gacutil.exe");
577                                 Environment.Exit (1);
578                         }
579                         MethodInfo get_gac = gac.GetGetMethod (true);
580                         return (string) get_gac.Invoke (null, null);
581                 }
582
583                 private static string GetLibDir () {
584                         MethodInfo libdir = typeof (System.Environment).GetMethod ("internalGetGacPath",
585                                         BindingFlags.Static|BindingFlags.NonPublic);
586                         if (libdir == null) {
587                                 WriteLine ("ERROR: Mono runtime not detected, please use " +
588                                                 "the mono runtime for gacutil.exe");
589                                 Environment.Exit (1);
590                         }
591                         return Path.Combine ((string)libdir.Invoke (null, null), "mono");
592                 }
593
594                 private static string GetStringToken (byte[] tok)
595                 {
596                         StringBuilder sb = new StringBuilder ();
597                         for (int i = 0; i < tok.Length ; i++)
598                                 sb.Append (tok[i].ToString ("x2"));
599                         return sb.ToString ();
600                 }
601
602                 private static string CombinePaths (string a, string b)
603                 {
604                         string dsc = Path.DirectorySeparatorChar.ToString ();
605                         string sep = (a.EndsWith (dsc) ? String.Empty : dsc);
606                         string end = (b.StartsWith (dsc) ? b.Substring (1) : b);
607                         return String.Concat (a, sep, end);
608                 }
609
610                 private static string EnsureLib (string dir)
611                 {
612                         DirectoryInfo d = new DirectoryInfo (dir);
613                         if (d.Name == "lib")
614                                 return dir;
615                         return Path.Combine (dir, "lib");
616                 }
617
618                 private static void WriteLine ()
619                 {
620                         if (silent)
621                                 return;
622                         Console.WriteLine ();
623                 }
624
625                 private static void WriteLine (string line)
626                 {
627                         if (silent)
628                                 return;
629                         Console.WriteLine (line);
630                 }
631
632                 private static void WriteLine (string line, params object [] p)
633                 {
634                         if (silent)
635                                 return; 
636                         Console.WriteLine (line, p);
637                 }
638
639                 private static void Usage ()
640                 {
641                         ShowHelp (false);
642                         Environment.Exit (1);
643                 }
644
645                 private static void ShowHelp (bool detailed)
646                 {
647                         WriteLine ("Usage: gacutil.exe <commands> [ <options> ]");
648                         WriteLine ("Commands:");
649
650                         WriteLine ("-i <assembly_path> [-check_refs] [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
651                         WriteLine ("\tInstalls an assembly into the global assembly cache.");
652                         if (detailed) {
653                                 WriteLine ("\t<assembly_path> is the name of the file that contains the " +
654                                                 "\tassembly manifest\n" +
655                                                 "\tExample: -i myDll.dll");
656                         }
657                         WriteLine ();
658
659                         WriteLine ("-il <assembly_list_file> [-check_refs] [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
660                         WriteLine ("\tInstalls one or more assemblies into the global assembly cache.");
661                         if (detailed) {
662                                 WriteLine ("\t<assembly_list_file> is the path to a test file containing a list of\n" +
663                                                 "\tassembly file paths on separate lines.\n" +
664                                                 "\tExample -il assembly_list.txt\n" +
665                                                 "\t\tassembly_list.txt contents:\n" +
666                                                 "\t\tassembly1.dll\n" +
667                                                 "\t\tassembly2.dll");
668                         }
669                         WriteLine ();
670                         
671                         WriteLine ("-u <assembly_display_name> [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
672                         WriteLine ("\tUninstalls an assembly from the global assembly cache.");
673                         if (detailed) {
674                                 WriteLine ("\t<assembly_display_name> is the name of the assembly (partial or\n" +
675                                                 "\tfully qualified) to remove from the global assembly cache. If a \n" +
676                                                 "\tpartial name is specified all matching assemblies will be uninstalled.\n" +
677                                                 "\tExample: -u myDll,Version=1.2.1.0");
678                         }
679                         WriteLine ();
680
681                         WriteLine ("-ul <assembly_list_file> [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
682                         WriteLine ("\tUninstalls one or more assemblies from the global assembly cache.");
683                         if (detailed) {
684                                 WriteLine ("\t<assembly_list_file> is the path to a test file containing a list of\n" +
685                                                 "\tassembly names on separate lines.\n" +
686                                                 "\tExample -ul assembly_list.txt\n" +
687                                                 "\t\tassembly_list.txt contents:\n" +
688                                                 "\t\tassembly1,Version=1.0.0.0,Culture=en,PublicKeyToken=0123456789abcdef\n" +
689                                                 "\t\tassembly2,Version=2.0.0.0,Culture=en,PublicKeyToken=0123456789abcdef");
690                         }
691                         WriteLine ();
692
693                         WriteLine ("-us <assembly_path> [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
694                         WriteLine ("\tUninstalls an assembly using the specifed assemblies full name.");
695                         if (detailed) {
696                                 WriteLine ("\t<assembly path> is the path to an assembly. The full assembly name\n" +
697                                                 "\tis retrieved from the specified assembly if there is an assembly in\n" +
698                                                 "\tthe GAC with a matching name, it is removed.\n" +
699                                                 "\tExample: -us myDll.dll");
700                         }
701                         WriteLine ();
702
703                         WriteLine ("-l [assembly_name] [-root ROOTDIR] [-gacdir GACDIR]");
704                         WriteLine ("\tLists the contents of the global assembly cache.");
705                         if (detailed) {
706                                 WriteLine ("\tWhen the <assembly_name> parameter is specified only matching\n" +
707                                                 "\tassemblies are listed.");
708                         }
709                         WriteLine ();
710
711                         WriteLine ("-?");
712                         WriteLine ("\tDisplays a detailed help screen");
713                         WriteLine ();
714
715                         if (!detailed)
716                                 return;
717
718                         WriteLine ("Options:");
719                         WriteLine ("-package <NAME>");
720                         WriteLine ("\tUsed to create a directory in prefix/lib/mono with the name NAME, and a\n" +
721                                         "\tsymlink is created from NAME/assembly_name to the assembly on the GAC.\n" +
722                                         "\tThis is used so developers can reference a set of libraries at once.");
723                         WriteLine ();
724
725                         WriteLine ("-gacdir <GACDIR>");
726                         WriteLine ("\tUsed to specify the GACs base directory. Once an assembly has been installed\n" +
727                                         "\tto a non standard gacdir the MONO_GAC_PREFIX environment variable must be used\n" +
728                                         "\tto access the assembly.");
729                         WriteLine ();
730
731                         WriteLine ("-root <ROOTDIR>");
732                         WriteLine ("\tUsed by developers integrating this with automake tools or packaging tools\n" +
733                                         "\tthat require a prefix directory to  be specified. The root represents the\n" +
734                                         "\t\"libdir\" component of a prefix (typically prefix/lib).");
735                         WriteLine ();
736
737                         WriteLine ("-check_refs");
738                         WriteLine ("\tUsed to ensure that the assembly being installed into the GAC does not\n" +
739                                         "\treference any non strong named assemblies. Assemblies being installed to\n" +
740                                         "\tthe GAC should not reference non strong named assemblies, however the is\n" +
741                                         "\tan optional check.");
742
743                         WriteLine ();
744                         WriteLine ("Ignored Options:");
745                         WriteLine ("-f");
746                         WriteLine ("\tThe Mono gacutil ignores the -f option to maintian commandline compatibility with");
747                         WriteLine ("\tother gacutils. gacutil will always force the installation of a new assembly.");
748
749                         WriteLine ();
750                         WriteLine ("-r <reference_scheme> <reference_id> <description>");
751                         WriteLine ("\tThe Mono gacutil has not implemented traced references and will emit a warning");
752                         WriteLine ("\twhen this option is used.");
753                 }
754         }
755 }
756