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