move GCLatencyMode to System.Runtime
[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.Diagnostics;
16 using System.Text;
17 using System.Reflection;
18 using System.Collections;
19 using System.Globalization;
20 using System.Runtime.InteropServices;
21 using System.Security.Cryptography;
22
23 using Mono.Security;
24 using Mono.Security.Cryptography;
25
26 namespace Mono.Tools {
27
28         public class Driver {
29
30                 private enum Command {
31                         Unknown,
32                         Install,
33                         InstallFromList,
34                         Uninstall,
35                         UninstallFromList,
36                         UninstallSpecific,
37                         List,
38                         Help
39                 }
40
41                 private enum VerificationResult
42                 {
43                         StrongNamed,
44                         WeakNamed,
45                         DelaySigned,
46                         Skipped
47                 }
48
49                 private static bool silent;
50                 static bool in_bootstrap;
51
52                 public static int Main (string [] args)
53                 {
54                         if (args.Length == 0)
55                                 Usage ();
56
57                         Command command = Command.Unknown;
58                         string command_str = null;
59
60                         string libdir;
61                         string name, package, gacdir, root;
62                         name = package = root = gacdir = null;
63                         bool check_refs = false;
64
65                         // Check for silent arg first so we can suppress
66                         // warnings during command line parsing
67                         if (Array.IndexOf (args, "/silent") > -1 || Array.IndexOf (args, "-silent") > -1)
68                                 silent = true;
69
70                         for (int i=0; i<args.Length; i++) {
71                                 if (IsSwitch (args [i])) {
72
73                                         // for cmd line compatibility with other gacutils
74                                         // we always force it though
75                                         if (args [i] == "-f" || args [i] == "/f")
76                                                 continue;
77
78                                         // Ignore this option for now, although we might implement it someday
79                                         if (args [i] == "/r") {
80                                                 WriteLine ("WARNING: gacutil does not support traced references." +
81                                                         "This option is being ignored.");
82                                                 i += 3;
83                                                 continue;
84                                         }
85
86                                         // This is already handled we just dont want to choke on it
87                                         if (args [i] == "-silent" || args [i] == "/silent")
88                                                 continue; 
89
90                                         if (args [i] == "-check_refs" || args [i] == "/check_refs") {
91                                                 check_refs = true;
92                                                 continue;
93                                         }
94
95                                         if (args [i] == "-bootstrap" || args [i] == "/bootstrap") {
96                                                 in_bootstrap = true;
97                                                 continue;
98                                         }
99
100                                         if (command == Command.Unknown) {
101                                                 command = GetCommand (args [i]);
102                                                 if (command != Command.Unknown) {
103                                                         command_str = args [i];
104                                                         continue;
105                                                 }
106                                         }
107
108                                         if (i + 1 >= args.Length) {
109                                                 Console.WriteLine ("Option " + args [i] + " takes 1 argument");
110                                                 return 1;
111                                         }
112
113                                         switch (args [i]) {
114                                         case "-package":
115                                         case "/package":
116                                                 package = args [++i];
117                                                 continue;
118                                         case "-root":
119                                         case "/root":
120                                                 root = args [++i];
121                                                 continue;
122                                         case "-gacdir":
123                                         case "/gacdir":
124                                                 gacdir = args [++i];
125                                                 continue;
126                                         case "/nologo":
127                                         case "-nologo":
128                                                 // we currently don't display a
129                                                 // logo banner, so ignore it
130                                                 // for command-line compatibility
131                                                 // with MS gacutil
132                                                 continue;
133                                         }
134                                 }
135                                 if (name == null)
136                                         name = args [i];
137                                 else
138                                         name += args [i];
139                         }
140
141                         if (command == Command.Unknown && IsSwitch (args [0])) {
142                                 Console.WriteLine ("Unknown command: " + args [0]);
143                                 return 1;
144                         } else if (command == Command.Unknown) {
145                                 Usage ();
146                         } else if (command == Command.Help) {
147                                 ShowHelp (true);
148                                 return 1;
149                         }
150
151                         if (gacdir == null) {
152                                 gacdir = GetGacDir ();
153                                 libdir = GetLibDir ();
154                         } else {
155                                 gacdir = EnsureLib (gacdir);
156                                 libdir = Path.Combine (gacdir, "mono");
157                                 gacdir = Path.Combine (libdir, "gac");
158                         }
159
160                         string link_gacdir = gacdir;
161                         string link_libdir = libdir;
162                         if (root != null) {
163                                 libdir = Path.Combine (root, "mono");
164                                 gacdir = Path.Combine (libdir, "gac");
165                         }
166
167                         LoadConfig (silent);
168
169                         switch (command) {
170                         case Command.Install:
171                                 if (name == null) {
172                                         WriteLine ("Option " + command_str + " takes 1 argument");
173                                         return 1;
174                                 }
175                                 if (!Install (check_refs, name, package, gacdir, link_gacdir, libdir, link_libdir))
176                                         return 1;
177                                 break;
178                         case Command.InstallFromList:
179                                 if (name == null) {
180                                         WriteLine ("Option " + command_str + " takes 1 argument");
181                                         return 1;
182                                 }
183                                 if (!InstallFromList (check_refs, name, package, gacdir, link_gacdir, libdir, link_libdir))
184                                         return 1;
185                                 break;
186                         case Command.Uninstall:
187                                 if (name == null) {
188                                         WriteLine ("Option " + command_str + " takes 1 argument");
189                                         return 1;
190                                 }
191                                 int uninstallCount = 0;
192                                 int uninstallFailures = 0;
193                                 Uninstall (name, package, gacdir, libdir, false,
194                                         ref uninstallCount, ref uninstallFailures);
195                                 WriteLine ("Assemblies uninstalled = {0}", uninstallCount);
196                                 WriteLine ("Failures = {0}", uninstallFailures);
197                                 if (uninstallFailures > 0)
198                                         return 1;
199                                 break;
200                         case Command.UninstallFromList:
201                                 if (name == null) {
202                                         WriteLine ("Option " + command_str + " takes 1 argument");
203                                         return 1;
204                                 }
205                                 if (!UninstallFromList (name, package, gacdir, libdir))
206                                         return 1;
207                                 break;
208                         case Command.UninstallSpecific:
209                                 if (name == null) {
210                                         WriteLine ("Option " + command_str + " takes 1 argument");
211                                         return 1;
212                                 }
213                                 if (!UninstallSpecific (name, package, gacdir, libdir))
214                                         return 1;
215                                 break;
216                         case Command.List:
217                                 List (name, gacdir);
218                                 break;
219                         }
220
221                         return 0;
222                 }
223
224                 static void Copy (string source, string target, bool v)
225                 {
226                         try {
227                                 File.Delete (target);
228                         } catch {}
229                         File.Copy (source, target, v);
230                 }
231                 
232                 private static bool Install (bool check_refs, string name, string package,
233                                 string gacdir, string link_gacdir, string libdir, string link_libdir)
234                 {
235                         string failure_msg = "Failure adding assembly {0} to the cache: ";
236                         ArrayList resources;
237
238                         if (!File.Exists (name)) {
239                                 WriteLine (string.Format (failure_msg, name) + "The system cannot find the file specified.");
240                                 return false;
241                         }
242
243                         Assembly assembly = null;
244                         AssemblyName an = null;
245
246                         try {
247                                 assembly = Assembly.LoadFrom (name);
248                         } catch {
249                                 WriteLine (string.Format (failure_msg, name) + "The file specified is not a valid assembly.");
250                                 return false;
251                         }
252
253                         an = assembly.GetName ();
254
255                         switch (VerifyStrongName (an, name)) {
256                         case VerificationResult.StrongNamed:
257                         case VerificationResult.Skipped:
258                                 break;
259                         case VerificationResult.WeakNamed:
260                                 WriteLine (string.Format (failure_msg, name) + "Attempt to install an assembly without a strong name"
261                                         + (in_bootstrap ? "(continuing anyway)" : string.Empty));
262                                 if (!in_bootstrap)
263                                         return false;
264                                 break;
265                         case VerificationResult.DelaySigned:
266                                 WriteLine (string.Format (failure_msg, name) + "Strong name cannot be verified for delay-signed assembly"
267                                         + (in_bootstrap ? "(continuing anyway)" : string.Empty));
268                                 if (!in_bootstrap)
269                                         return false;
270                                 break;
271                         }
272
273                         resources = new ArrayList ();
274                         foreach (string res_name in assembly.GetManifestResourceNames ()) {
275                                 ManifestResourceInfo res_info = assembly.GetManifestResourceInfo (res_name);
276                                 
277                                 if ((res_info.ResourceLocation & ResourceLocation.Embedded) == 0) {
278                                         if (!File.Exists (res_info.FileName)) {
279                                                 WriteLine (string.Format (failure_msg, name) + "The system cannot find resource " + res_info.FileName);
280                                                 return false;
281                                         }
282
283                                         resources.Add (res_info);
284                                 }
285                         }
286
287                         if (check_refs && !CheckReferencedAssemblies (an)) {
288                                 WriteLine (string.Format (failure_msg, name) +
289                                         "Attempt to install an assembly that " +
290                                         "references non strong named assemblies " +
291                                         "with -check_refs enabled.");
292                                 return false;
293                         }
294
295                         string [] siblings = { ".config", ".mdb" };
296                         string version_token = an.Version + "_" +
297                                                an.CultureInfo.Name.ToLower (CultureInfo.InvariantCulture) + "_" +
298                                                GetStringToken (an.GetPublicKeyToken ());
299                         string full_path = Path.Combine (Path.Combine (gacdir, an.Name), version_token);
300                         string asmb_file = Path.GetFileName (name);
301                         string asmb_path = Path.Combine (full_path, asmb_file);
302
303                         try {
304                                 if (Directory.Exists (full_path)) {
305                                         // Wipe out the directory. This way we ensure old assemblies    
306                                         // config files, and AOTd files are removed.
307                                         Directory.Delete (full_path, true);
308                                 }
309                                 Directory.CreateDirectory (full_path);
310                         } catch {
311                                 WriteLine (string.Format (failure_msg, name) +
312                                         "gac directories could not be created, " +
313                                         "possibly permission issues.");
314                                 return false;
315                         }
316
317                         Copy (name, asmb_path, true);
318
319                         foreach (string ext in siblings) {
320                                 string sibling = String.Concat (name, ext);
321                                 if (File.Exists (sibling))
322                                         Copy (sibling, String.Concat (asmb_path, ext), true);
323                         }
324
325                         foreach (ManifestResourceInfo resource_info in resources) {
326                                 try {
327                                         Copy (resource_info.FileName, Path.Combine (full_path, Path.GetFileName (resource_info.FileName)), true);
328                                 } catch {
329                                         WriteLine ("ERROR: Could not install resource file " + resource_info.FileName);
330                                         Environment.Exit (1);
331                                 }
332                         }
333
334                         if (package != null) {
335                                 string ref_dir = Path.Combine (libdir, package);
336                                 string ref_path = Path.Combine (ref_dir, asmb_file);
337
338                                 if (File.Exists (ref_path))
339                                         File.Delete (ref_path);
340                                 try {
341                                         Directory.CreateDirectory (ref_dir);
342                                 } catch {
343                                         WriteLine ("ERROR: Could not create package dir file.");
344                                         Environment.Exit (1);
345                                 }
346                                 if (Path.DirectorySeparatorChar == '/') {
347                                         string pkg_path = "../gac/" + an.Name + "/" + version_token + "/" + asmb_file;
348                                         symlink (pkg_path, ref_path);
349
350                                         foreach (string ext in siblings) {
351                                                 string sibling = String.Concat (pkg_path, ext);
352                                                 string sref = String.Concat (ref_path, ext);
353                                                 if (File.Exists (sibling))
354                                                         symlink (sibling, sref);
355                                                 else {
356                                                         try {
357                                                                 File.Delete (sref);
358                                                         } catch {
359                                                                 // Ignore error, just delete files that should not be there.
360                                                         }
361                                                 }
362                                         }
363                                         WriteLine ("Package exported to: {0} -> {1}", ref_path, pkg_path);
364                                 } else {
365                                         // string link_path = Path.Combine (Path.Combine (link_gacdir, an.Name), version_token);
366                                         //
367                                         // We can't use 'link_path' here, since it need not be a valid path at the time 'gacutil'
368                                         // is run, esp. when invoked in a DESTDIR install.
369                                         Copy (name, ref_path, true);
370                                         WriteLine ("Package exported to: " + ref_path);
371                                 }
372                         }
373
374                         WriteLine ("Installed {0} into the gac ({1})", name,
375                                 gacdir);
376
377                         return true;
378                 }
379
380                 private static void Uninstall (string name, string package, string gacdir, string libdir, bool listMode, ref int uninstalled, ref int failures)
381                 {
382                         string [] assembly_pieces = name.Split (new char[] { ',' });
383                         Hashtable asm_info = new Hashtable ();
384
385                         foreach (string item in assembly_pieces) {
386                                 if (item == String.Empty) continue;
387                                 string[] pieces = item.Trim ().Split (new char[] { '=' }, 2);
388                                 if(pieces.Length == 1)
389                                         asm_info ["assembly"] = pieces [0];
390                                 else
391                                         asm_info [pieces[0].Trim ().ToLower (CultureInfo.InvariantCulture)] = pieces [1];
392                         }
393
394                         string assembly_name = (string) asm_info ["assembly"];
395                         string asmdir = Path.Combine (gacdir, assembly_name);
396                         if (!Directory.Exists (asmdir)) {
397                                 if (listMode) {
398                                         failures++;
399                                         WriteLine ("Assembly: " + name);
400                                 }
401                                 WriteLine ("No assemblies found that match: " + name);
402                                 return;
403                         }
404
405                         string searchString = GetSearchString (asm_info);
406                         string [] directories = Directory.GetDirectories (asmdir, searchString);
407
408                         if (directories.Length == 0) {
409                                 if (listMode) {
410                                         failures++;
411                                         WriteLine ("Assembly: " + name);
412                                         WriteLine ("No assemblies found that match: " + name);
413                                 }
414                                 return;
415                         }
416
417                         for (int i = 0; i < directories.Length; i++) {
418                                 if (listMode && i > 0)
419                                         break;
420
421                                 string dir = directories [i];
422
423                                 AssemblyName an = AssemblyName.GetAssemblyName (
424                                         Path.Combine (dir, assembly_name + ".dll"));
425                                 WriteLine ("Assembly: " + an.FullName);
426
427                                 Directory.Delete (dir, true);
428                                 if (package != null) {
429                                         string link_dir = Path.Combine (libdir, package);
430                                         string link = Path.Combine (link_dir, assembly_name + ".dll");
431                                         try { 
432                                                 File.Delete (link);
433                                         } catch {
434                                                 // The file might not exist, happens with
435                                                 // the debugger on make uninstall
436                                         }
437                                         
438                                         if (Directory.GetFiles (link_dir).Length == 0) {
439                                                 WriteLine ("Cleaning package directory, it is empty.");
440                                                 try {
441                                                         Directory.Delete (link_dir);
442                                                 } catch {
443                                                         // Workaround: GetFiles does not list Symlinks
444                                                 }
445                                         }
446                                 }
447
448                                 uninstalled++;
449                                 WriteLine ("Uninstalled: " + an.FullName);
450                         }
451
452                         if (Directory.GetDirectories (asmdir).Length == 0) {
453                                 WriteLine ("Cleaning assembly dir, it is empty");
454                                 try {
455                                         Directory.Delete (asmdir);
456                                 } catch {
457                                         // Workaround: GetFiles does not list Symlinks
458                                 }
459                         }
460                 }
461
462                 private static bool UninstallSpecific (string name, string package,
463                                 string gacdir, string libdir)
464                 {
465                         string failure_msg = "Failure to remove assembly from the cache: ";
466
467                         if (!File.Exists (name)) {
468                                 WriteLine (failure_msg + "The system cannot find the file specified.");
469                                 return false;
470                         }
471
472                         AssemblyName an = null;
473
474                         try {
475                                 an = AssemblyName.GetAssemblyName (name);
476                         } catch {
477                                 WriteLine (failure_msg + "The file specified is not a valid assembly.");
478                                 return false;
479                         }
480
481                         int uninstallCount = 0;
482                         int uninstallFailures = 0;
483                         Uninstall (an.FullName.Replace (" ", String.Empty),
484                                 package, gacdir, libdir, true, ref uninstallCount,
485                                 ref uninstallFailures);
486                         WriteLine ("Assemblies uninstalled = {0}", uninstallCount);
487                         WriteLine ("Failures = {0}", uninstallFailures);
488                         return (uninstallFailures == 0);
489                 }
490
491                 private static void List (string name, string gacdir)
492                 {
493                         WriteLine ("The following assemblies are installed into the GAC:");
494
495                         if (name != null) {
496                                 FilteredList (name, gacdir);
497                                 return;
498                         }
499
500                         int count = 0;
501                         DirectoryInfo gacinfo = new DirectoryInfo (gacdir);
502                         foreach (DirectoryInfo parent in gacinfo.GetDirectories ()) {
503                                 foreach (DirectoryInfo dir in parent.GetDirectories ()) {
504                                         string asmb = Path.Combine (Path.Combine (parent.FullName, dir.Name), parent.Name) + ".dll";
505                                         if (File.Exists (asmb)) {
506                                                 WriteLine (AsmbNameFromVersionString (parent.Name, dir.Name));
507                                                 count++;
508                                         }
509                                 }
510                         }
511                         WriteLine ("Number of items = " + count);
512                 }
513
514                 private static void FilteredList (string name, string gacdir)
515                 {
516                         string [] assembly_pieces = name.Split (new char[] { ',' });
517                         Hashtable asm_info = new Hashtable ();
518
519                         foreach (string item in assembly_pieces) {
520                                 string[] pieces = item.Trim ().Split (new char[] { '=' }, 2);
521                                 if(pieces.Length == 1)
522                                         asm_info ["assembly"] = pieces [0];
523                                 else
524                                         asm_info [pieces[0].Trim ().ToLower (CultureInfo.InvariantCulture)] = pieces [1];
525                         }
526                         
527                         string asmdir = Path.Combine (gacdir, (string) asm_info ["assembly"]);
528                         if (!Directory.Exists (asmdir)) {
529                                 WriteLine ("Number of items = 0");
530                                 return;
531                         }
532                         string search = GetSearchString (asm_info);
533                         string [] dir_list = Directory.GetDirectories (asmdir, search);
534
535                         int count = 0;
536                         foreach (string dir in dir_list) {
537                                 string asmb = Path.Combine (dir, (string) asm_info ["assembly"]) + ".dll";
538                                 if (File.Exists (asmb)) {
539                                         WriteLine (AsmbNameFromVersionString ((string) asm_info ["assembly"],
540                                                                    new DirectoryInfo (dir).Name));
541                                         count++;
542                                 }
543                         }
544                         WriteLine ("Number of items = " + count);
545                 }
546
547                 private static bool InstallFromList (bool check_refs, string list_file, string package,
548                                 string gacdir, string link_gacdir, string libdir, string link_libdir)
549                 {
550                         StreamReader s = null;
551                         int processed, failed;
552                         string listdir = Path.GetDirectoryName (
553                                 Path.GetFullPath (list_file));
554
555                         processed = failed = 0;
556
557                         try {
558                                 s = new StreamReader (list_file);
559
560                                 string line;
561                                 while ((line = s.ReadLine ()) != null) {
562                                         string file = line.Trim ();
563                                         if (file.Length == 0)
564                                                 continue;
565
566                                         string assemblyPath = Path.Combine (listdir,
567                                                 file);
568
569                                         if (!Install (check_refs, assemblyPath, package, gacdir,
570                                                      link_gacdir, libdir, link_libdir))
571                                                 failed++;
572                                         processed++;
573                                 }
574
575                                 WriteLine ("Assemblies processed = {0}", processed);
576                                 WriteLine ("Assemblies installed = {0}", processed - failed);
577                                 WriteLine ("Failures = {0}", failed);
578
579                                 return (failed == 0);
580                         } catch (IOException) {
581                                 WriteLine ("Failed to open assemblies list file " + list_file + ".");
582                                 return false;
583                         } finally {
584                                 if (s != null)
585                                         s.Close ();
586                         }
587                 }
588
589                 private static bool UninstallFromList (string list_file, string package,
590                                 string gacdir, string libdir)
591                 {
592                         StreamReader s = null;
593                         int failed, uninstalled;
594
595                         failed = uninstalled = 0;
596
597                         try {
598                                 s = new StreamReader (list_file);
599
600                                 string line;
601                                 while ((line = s.ReadLine ()) != null) {
602                                         string name = line.Trim ();
603                                         if (name.Length == 0)
604                                                 continue;
605                                         Uninstall (line, package, gacdir, libdir,
606                                                 true, ref uninstalled, ref failed);
607                                 }
608
609                                 WriteLine ("Assemblies processed = {0}", uninstalled+failed);
610                                 WriteLine ("Assemblies uninstalled = {0}", uninstalled);
611                                 WriteLine ("Failures = {0}", failed);
612
613                                 return (failed == 0);
614                         } catch (IOException) {
615                                 WriteLine ("Failed to open assemblies list file " + list_file + ".");
616                                 return false;
617                         } finally {
618                                 if (s != null)
619                                         s.Close ();
620                         }
621                 }
622
623                 private static bool CheckReferencedAssemblies (AssemblyName an)
624                 {
625                         AppDomain d = null;
626                         try {
627                                 Assembly a = Assembly.LoadFrom (an.CodeBase);
628                                 AssemblyName corlib = typeof (object).Assembly.GetName ();
629
630                                 foreach (AssemblyName ref_an in a.GetReferencedAssemblies ()) {
631                                         if (ref_an.Name == corlib.Name) // Just do a string compare so we can install on diff versions
632                                                 continue;
633                                         byte [] pt = ref_an.GetPublicKeyToken ();
634                                         if (pt == null || pt.Length == 0) {
635                                                 WriteLine ("Assembly " + ref_an.Name + " is not strong named.");
636                                                 return false;
637                                         }
638                                 }
639                         } catch (Exception e) {
640                                 WriteLine (e.ToString ()); // This should be removed pre beta3
641                                 return false;
642                         } finally {
643                                 if (d != null) {
644                                         try {
645                                                 AppDomain.Unload (d);
646                                         } catch { }
647                                 }
648                         }
649
650                         return true;
651                 }
652
653                 private static string GetSearchString (Hashtable asm_info)
654                 {
655                         if (asm_info.Keys.Count == 1)
656                                 return "*";
657                         string version, culture, token;
658
659                         version = asm_info ["version"] as string;
660                         version = (version == null ? "*" : version + "*");
661                         culture = asm_info ["culture"] as string;
662                         culture = (culture == null ? "*" : (culture == "neutral") ? String.Empty : culture.ToLower (CultureInfo.InvariantCulture));
663                         token = asm_info ["publickeytoken"] as string;
664                         token = (token == null ? "*" : token.ToLower (CultureInfo.InvariantCulture));
665                         
666                         return String.Format ("{0}_{1}_{2}", version, culture, token);
667                 }
668
669                 private static string AsmbNameFromVersionString (string name, string str)
670                 {
671                         string [] pieces = str.Split ('_');
672                         return String.Format ("{0}, Version={1}, Culture={2}, PublicKeyToken={3}",
673                                         name, pieces [0], (pieces [1] == String.Empty ? "neutral" : pieces [1]),
674                                         pieces [2]);
675                 }
676
677                 static bool LoadConfig (bool quiet)
678                 {
679                         MethodInfo config = typeof (System.Environment).GetMethod ("GetMachineConfigPath",
680                                 BindingFlags.Static | BindingFlags.NonPublic);
681
682                         if (config != null) {
683                                 string path = (string) config.Invoke (null, null);
684
685                                 bool exist = File.Exists (path);
686                                 if (!quiet && !exist)
687                                         Console.WriteLine ("Couldn't find machine.config");
688
689                                 StrongNameManager.LoadConfig (path);
690                                 return exist;
691                         } else if (!quiet)
692                                 Console.WriteLine ("Couldn't resolve machine.config location (corlib issue)");
693
694                         // default CSP
695                         return false;
696                 }
697
698                 // modified copy from sn
699                 private static VerificationResult VerifyStrongName (AssemblyName an, string assemblyFile)
700                 {
701                         byte [] publicKey = StrongNameManager.GetMappedPublicKey (an.GetPublicKeyToken ());
702                         if ((publicKey == null) || (publicKey.Length < 12)) {
703                                 // no mapping
704                                 publicKey = an.GetPublicKey ();
705                                 if ((publicKey == null) || (publicKey.Length < 12))
706                                         return VerificationResult.WeakNamed;
707                         }
708
709                         // Note: MustVerify is based on the original token (by design). Public key
710                         // remapping won't affect if the assembly is verified or not.
711                         if (StrongNameManager.MustVerify (an)) {
712                                 RSA rsa = CryptoConvert.FromCapiPublicKeyBlob (publicKey, 12);
713                                 StrongName sn = new StrongName (rsa);
714                                 if (sn.Verify (assemblyFile)) {
715                                         return VerificationResult.StrongNamed;
716                                 } else {
717                                         return VerificationResult.DelaySigned;
718                                 }
719                         } else {
720                                 return VerificationResult.Skipped;
721                         }
722                 }
723
724                 private static bool IsSwitch (string arg)
725                 {
726                         return (arg [0] == '-' || (arg [0] == '/' && !arg.EndsWith(".dll") && arg.IndexOf('/', 1) < 0 ) );
727                 }
728
729                 private static Command GetCommand (string arg)
730                 {
731                         Command c = Command.Unknown;
732
733                         switch (arg) {
734                         case "-i":
735                         case "/i":
736                         case "--install":
737                                 c = Command.Install;
738                                 break;
739                         case "-il":
740                         case "/il":
741                         case "--install-from-list":
742                                 c = Command.InstallFromList;
743                                 break;
744                         case "-u":
745                         case "/u":
746                         case "/uf":
747                         case "--uninstall":
748                                 c = Command.Uninstall;
749                                 break;
750                         case "-ul":
751                         case "/ul":
752                         case "--uninstall-from-list":
753                                 c = Command.UninstallFromList;
754                                 break;
755                         case "-us":
756                         case "/us":
757                         case "--uninstall-specific":
758                                 c = Command.UninstallSpecific;
759                                 break;
760                         case "-l":
761                         case "/l":
762                         case "--list":
763                                 c = Command.List;
764                                 break;
765                         case "-?":
766                         case "/?":
767                         case "--help":
768                                 c = Command.Help;
769                                 break;
770                         }
771                         return c;        
772                 }
773
774                 [DllImport ("libc", SetLastError=true)]
775                 public static extern int symlink (string oldpath, string newpath);
776
777                 private static string GetGacDir () {
778                         PropertyInfo gac = typeof (System.Environment).GetProperty ("GacPath",
779                                         BindingFlags.Static|BindingFlags.NonPublic);
780                         if (gac == null) {
781                                 WriteLine ("ERROR: Mono runtime not detected, please use " +
782                                                 "the mono runtime for gacutil.exe");
783                                 Environment.Exit (1);
784                         }
785                         MethodInfo get_gac = gac.GetGetMethod (true);
786                         return (string) get_gac.Invoke (null, null);
787                 }
788
789                 private static string GetLibDir () {
790                         MethodInfo libdir = typeof (System.Environment).GetMethod ("internalGetGacPath",
791                                         BindingFlags.Static|BindingFlags.NonPublic);
792                         if (libdir == null) {
793                                 WriteLine ("ERROR: Mono runtime not detected, please use " +
794                                                 "the mono runtime for gacutil.exe");
795                                 Environment.Exit (1);
796                         }
797                         return Path.Combine ((string)libdir.Invoke (null, null), "mono");
798                 }
799
800                 private static string GetStringToken (byte[] tok)
801                 {
802                         StringBuilder sb = new StringBuilder ();
803                         for (int i = 0; i < tok.Length ; i++)
804                                 sb.Append (tok[i].ToString ("x2"));
805                         return sb.ToString ();
806                 }
807
808                 private static string EnsureLib (string dir)
809                 {
810                         DirectoryInfo d = new DirectoryInfo (dir);
811                         if (d.Name == "lib")
812                                 return dir;
813                         return Path.Combine (dir, "lib");
814                 }
815
816                 private static void WriteLine ()
817                 {
818                         if (silent)
819                                 return;
820                         Console.WriteLine ();
821                 }
822
823                 private static void WriteLine (string line)
824                 {
825                         if (silent)
826                                 return;
827                         Console.WriteLine (line);
828                 }
829
830                 private static void WriteLine (string line, params object [] p)
831                 {
832                         if (silent)
833                                 return; 
834                         Console.WriteLine (line, p);
835                 }
836
837                 private static void Usage ()
838                 {
839                         ShowHelp (false);
840                         Environment.Exit (1);
841                 }
842
843                 private static void ShowHelp (bool detailed)
844                 {
845                         WriteLine ("Usage: gacutil.exe <commands> [ <options> ]");
846                         WriteLine ("Commands:");
847
848                         WriteLine ("-i <assembly_path> [-check_refs] [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
849                         WriteLine ("\tInstalls an assembly into the global assembly cache.");
850                         if (detailed) {
851                                 WriteLine ("\t<assembly_path> is the name of the file that contains the " +
852                                                 "\tassembly manifest\n" +
853                                                 "\tExample: -i myDll.dll");
854                         }
855                         WriteLine ();
856
857                         WriteLine ("-il <assembly_list_file> [-check_refs] [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
858                         WriteLine ("\tInstalls one or more assemblies into the global assembly cache.");
859                         if (detailed) {
860                                 WriteLine ("\t<assembly_list_file> is the path to a test file containing a list of\n" +
861                                                 "\tassembly file paths on separate lines.\n" +
862                                                 "\tExample -il assembly_list.txt\n" +
863                                                 "\t\tassembly_list.txt contents:\n" +
864                                                 "\t\tassembly1.dll\n" +
865                                                 "\t\tassembly2.dll");
866                         }
867                         WriteLine ();
868                         
869                         WriteLine ("-u <assembly_display_name> [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
870                         WriteLine ("\tUninstalls an assembly from the global assembly cache.");
871                         if (detailed) {
872                                 WriteLine ("\t<assembly_display_name> is the name of the assembly (partial or\n" +
873                                                 "\tfully qualified) to remove from the global assembly cache. If a \n" +
874                                                 "\tpartial name is specified all matching assemblies will be uninstalled.\n" +
875                                                 "\tExample: -u myDll,Version=1.2.1.0");
876                         }
877                         WriteLine ();
878
879                         WriteLine ("-ul <assembly_list_file> [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
880                         WriteLine ("\tUninstalls one or more assemblies from the global assembly cache.");
881                         if (detailed) {
882                                 WriteLine ("\t<assembly_list_file> is the path to a test file containing a list of\n" +
883                                                 "\tassembly names on separate lines.\n" +
884                                                 "\tExample -ul assembly_list.txt\n" +
885                                                 "\t\tassembly_list.txt contents:\n" +
886                                                 "\t\tassembly1,Version=1.0.0.0,Culture=en,PublicKeyToken=0123456789abcdef\n" +
887                                                 "\t\tassembly2,Version=2.0.0.0,Culture=en,PublicKeyToken=0123456789abcdef");
888                         }
889                         WriteLine ();
890
891                         WriteLine ("-us <assembly_path> [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
892                         WriteLine ("\tUninstalls an assembly using the specifed assemblies full name.");
893                         if (detailed) {
894                                 WriteLine ("\t<assembly path> is the path to an assembly. The full assembly name\n" +
895                                                 "\tis retrieved from the specified assembly if there is an assembly in\n" +
896                                                 "\tthe GAC with a matching name, it is removed.\n" +
897                                                 "\tExample: -us myDll.dll");
898                         }
899                         WriteLine ();
900
901                         WriteLine ("-l [assembly_name] [-root ROOTDIR] [-gacdir GACDIR]");
902                         WriteLine ("\tLists the contents of the global assembly cache.");
903                         if (detailed) {
904                                 WriteLine ("\tWhen the <assembly_name> parameter is specified only matching\n" +
905                                                 "\tassemblies are listed.");
906                         }
907                         WriteLine ();
908
909                         WriteLine ("-?");
910                         WriteLine ("\tDisplays a detailed help screen");
911                         WriteLine ();
912
913                         if (!detailed)
914                                 return;
915
916                         WriteLine ("Options:");
917                         WriteLine ("-package <NAME>");
918                         WriteLine ("\tUsed to create a directory in prefix/lib/mono with the name NAME, and a\n" +
919                                         "\tsymlink is created from NAME/assembly_name to the assembly on the GAC.\n" +
920                                         "\tThis is used so developers can reference a set of libraries at once.");
921                         WriteLine ();
922
923                         WriteLine ("-gacdir <GACDIR>");
924                         WriteLine ("\tUsed to specify the GACs base directory. Once an assembly has been installed\n" +
925                                         "\tto a non standard gacdir the MONO_GAC_PREFIX environment variable must be used\n" +
926                                         "\tto access the assembly.");
927                         WriteLine ();
928
929                         WriteLine ("-root <ROOTDIR>");
930                         WriteLine ("\tUsed by developers integrating this with automake tools or packaging tools\n" +
931                                         "\tthat require a prefix directory to  be specified. The root represents the\n" +
932                                         "\t\"libdir\" component of a prefix (typically prefix/lib).");
933                         WriteLine ();
934
935                         WriteLine ("-check_refs");
936                         WriteLine ("\tUsed to ensure that the assembly being installed into the GAC does not\n" +
937                                         "\treference any non strong named assemblies. Assemblies being installed to\n" +
938                                         "\tthe GAC should not reference non strong named assemblies, however the is\n" +
939                                         "\tan optional check.");
940
941                         WriteLine ();
942                         WriteLine ("Ignored Options:");
943                         WriteLine ("-f");
944                         WriteLine ("\tThe Mono gacutil ignores the -f option to maintain commandline compatibility with");
945                         WriteLine ("\tother gacutils. gacutil will always force the installation of a new assembly.");
946
947                         WriteLine ();
948                         WriteLine ("-r <reference_scheme> <reference_id> <description>");
949                         WriteLine ("\tThe Mono gacutil has not implemented traced references and will emit a warning");
950                         WriteLine ("\twhen this option is used.");
951                 }
952         }
953 }
954