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