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