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