5 // Tood Berman <tberman@sevenl.net>
6 // Jackson Harper <jackson@ximian.com>
8 // Copyright 2003, 2004 Todd Berman
9 // Copyright 2004 Novell, Inc (http://www.novell.com)
16 using System.Reflection;
17 using System.Collections;
18 using System.Globalization;
19 using System.Runtime.InteropServices;
23 namespace Mono.Tools {
27 private enum Command {
36 public static int Main (string [] args)
41 Command command = GetCommand (args [0]);
43 if (command == Command.Unknown && IsSwitch (args [0])) {
44 Console.WriteLine ("Unknown command: " + args [0]);
46 } else if (command == Command.Unknown) {
48 } else if (command == Command.Help) {
54 string name, package, gacdir, root;
55 name = package = root = gacdir = null;
56 bool check_refs = false;
58 for (int i=1; i<args.Length; i++) {
59 if (IsSwitch (args [i])) {
61 // for cmd line compatibility with other gacutils
62 // we always force it though
63 if (args [i] == "-f" || args [i] == "/f")
66 if (args [i] == "-check_refs" || args [i] == "/check_refs") {
71 if (i + 1 >= args.Length) {
72 Console.WriteLine ("Option " + args [i] + " takes 1 argument");
99 gacdir = GetGacDir ();
100 libdir = GetLibDir ();
102 libdir = Path.Combine (gacdir, "mono");
103 gacdir = Path.Combine (libdir, "gac");
106 string link_gacdir = gacdir;
107 string link_libdir = libdir;
109 libdir = CombinePaths (root, libdir);
110 gacdir = CombinePaths (root, gacdir);
114 case Command.Install:
116 Console.WriteLine ("Option " + args [0] + " takes 1 argument");
117 Environment.Exit (1);
119 Install (check_refs, name, package, gacdir, link_gacdir, libdir, link_libdir);
121 case Command.Uninstall:
123 Console.WriteLine ("Option " + args [0] + " takes 1 argument");
124 Environment.Exit (1);
126 Uninstall (name, package, gacdir, libdir);
128 case Command.UninstallSpecific:
130 Console.WriteLine ("Opetion " + args [0] + " takes 1 argument");
131 Environment.Exit (1);
133 UninstallSpecific (name, package, gacdir, libdir);
143 private static void Install (bool check_refs, string name, string package,
144 string gacdir, string link_gacdir, string libdir, string link_libdir)
146 string failure_msg = "Failure adding assembly to the cache: ";
148 if (!File.Exists (name)) {
149 Console.WriteLine (failure_msg + "The system cannot find the file specified.");
150 Environment.Exit (1);
153 AssemblyName an = null;
157 an = AssemblyName.GetAssemblyName (name);
159 Console.WriteLine (failure_msg + "The file specified is not a valid assembly.");
160 Environment.Exit (1);
163 pub_tok = an.GetPublicKeyToken ();
164 if (pub_tok == null || pub_tok.Length == 0) {
165 Console.WriteLine (failure_msg + "Attempt to install an assembly without a strong name.");
166 Environment.Exit (1);
169 if (check_refs && !CheckReferencedAssemblies (an)) {
170 Console.WriteLine (failure_msg + "Attempt to install an assembly that references non " +
171 "strong named assemblies with -check_refs enabled.");
172 Environment.Exit (1);
175 string conf_name = name + ".config";
176 string version_token = an.Version + "_" +
177 an.CultureInfo.Name.ToLower (CultureInfo.InvariantCulture) + "_" +
178 GetStringToken (pub_tok);
179 string full_path = Path.Combine (Path.Combine (gacdir, an.Name), version_token);
180 string asmb_file = Path.GetFileName (name);
181 string asmb_path = Path.Combine (full_path, asmb_file);
184 if (Directory.Exists (full_path)) {
185 // Wipe out the directory. This way we ensure old assemblies
186 // config files, and AOTd files are removed.
187 Directory.Delete (full_path, true);
189 Directory.CreateDirectory (full_path);
191 Console.WriteLine (failure_msg + "gac directories could not be created, " +
192 "possibly permission issues.");
193 Environment.Exit (1);
196 File.Copy (name, asmb_path, true);
197 if (File.Exists (conf_name))
198 File.Copy (conf_name, asmb_path + ".config", true);
200 if (package != null) {
201 string link_path = Path.Combine (Path.Combine (link_gacdir, an.Name), version_token);
202 string ref_dir = Path.Combine (libdir, package);
203 string ref_path = Path.Combine (ref_dir, asmb_file);
205 if (File.Exists (ref_path))
206 File.Delete (ref_path);
208 Directory.CreateDirectory (ref_dir);
210 Console.WriteLine ("ERROR: Could not create package dir file.");
211 Environment.Exit (1);
213 Symlink (Path.Combine (link_path, asmb_file), ref_path);
215 Console.WriteLine ("Package exported to: " + ref_path + " -> " +
216 Path.Combine (link_path, asmb_file));
220 Console.WriteLine ("{0} installed into the gac ({1})", an.Name, gacdir);
223 private static void Uninstall (string name, string package, string gacdir, string libdir)
225 string [] assembly_pieces = name.Split (new char[] { ',' });
226 Hashtable asm_info = new Hashtable ();
228 foreach (string item in assembly_pieces) {
229 string[] pieces = item.Trim ().Split (new char[] { '=' }, 2);
230 if(pieces.Length == 1)
231 asm_info ["assembly"] = pieces [0];
233 asm_info [pieces[0].Trim ().ToLower (CultureInfo.InvariantCulture)] = pieces [1];
236 string asmdir = Path.Combine (gacdir, (string) asm_info ["assembly"]);
237 if (!Directory.Exists (asmdir)) {
238 Console.WriteLine ("No assemblies found that match: " + name);
239 Environment.Exit (1);
242 string searchString = GetSearchString (asm_info);
243 string [] directories = Directory.GetDirectories (asmdir, searchString);
245 foreach (string dir in directories) {
246 Directory.Delete (dir, true);
247 if (package != null) {
248 string link_dir = Path.Combine (libdir, package);
249 string link = Path.Combine (link_dir, (string) asm_info ["assembly"] + ".dll");
251 if (Directory.GetFiles (link_dir).Length == 0) {
252 Console.WriteLine ("Cleaning package directory, it is empty.");
253 Directory.Delete (link_dir);
256 Console.WriteLine ("Assembly removed from the gac.");
259 if(Directory.GetDirectories (asmdir).Length == 0) {
260 Console.WriteLine ("Cleaning assembly dir, its empty");
261 Directory.Delete (asmdir);
265 private static void UninstallSpecific (string name, string package,
266 string gacdir, string libdir)
268 string failure_msg = "Failure to remove assembly from the cache: ";
270 if (!File.Exists (name)) {
271 Console.WriteLine (failure_msg + "The system cannot find the file specified.");
272 Environment.Exit (1);
275 AssemblyName an = null;
278 an = AssemblyName.GetAssemblyName (name);
280 Console.WriteLine (failure_msg + "The file specified is not a valid assembly.");
281 Environment.Exit (1);
284 Uninstall (an.FullName.Replace (" ", String.Empty),
285 package, gacdir, libdir);
288 private static void List (string name, string gacdir)
290 Console.WriteLine ("The following assemblies are installed into the GAC:");
293 FilteredList (name, gacdir);
298 DirectoryInfo gacinfo = new DirectoryInfo (gacdir);
299 foreach (DirectoryInfo parent in gacinfo.GetDirectories ()) {
300 foreach (DirectoryInfo dir in parent.GetDirectories ()) {
301 Console.WriteLine (AsmbNameFromVersionString (parent.Name, dir.Name));
305 Console.WriteLine ("Number of items = " + count);
308 private static void FilteredList (string name, string gacdir)
310 string [] assembly_pieces = name.Split (new char[] { ',' });
311 Hashtable asm_info = new Hashtable ();
313 foreach (string item in assembly_pieces) {
314 string[] pieces = item.Trim ().Split (new char[] { '=' }, 2);
315 if(pieces.Length == 1)
316 asm_info ["assembly"] = pieces [0];
318 asm_info [pieces[0].Trim ().ToLower (CultureInfo.InvariantCulture)] = pieces [1];
321 string asmdir = Path.Combine (gacdir, (string) asm_info ["assembly"]);
322 if (!Directory.Exists (asmdir)) {
323 Console.WriteLine ("Number of items = 0");
326 string search = GetSearchString (asm_info);
327 string [] dir_list = Directory.GetDirectories (asmdir, search);
330 foreach (string dir in dir_list) {
331 Console.WriteLine (AsmbNameFromVersionString ((string) asm_info ["assembly"],
332 new DirectoryInfo (dir).Name));
335 Console.WriteLine ("Number of items = " + count);
338 private static bool CheckReferencedAssemblies (AssemblyName an)
343 AssemblyName corlib = typeof (object).Assembly.GetName ();
344 d = AppDomain.CreateDomain (an.Name);
346 foreach (AssemblyName ref_an in a.GetReferencedAssemblies ()) {
347 if (ref_an.Name == corlib.Name) // Just do a string compare so we can install on diff versions
349 byte [] pt = ref_an.GetPublicKeyToken ();
350 if (pt == null || pt.Length == 0) {
351 Console.WriteLine (ref_an);
355 } catch (Exception e) {
356 Console.WriteLine (e); // This should be removed pre beta3
361 AppDomain.Unload (d);
369 private static string GetSearchString (Hashtable asm_info)
371 if (asm_info.Keys.Count == 1)
373 string version, culture, token;
375 version = asm_info ["version"] as string;
376 version = (version == null ? "*" : version);
377 culture = asm_info ["culture"] as string;
378 culture = (culture == null ? "*" : culture.ToLower (CultureInfo.InvariantCulture));
379 token = asm_info ["publickeytoken"] as string;
380 token = (token == null ? "*" : token.ToLower (CultureInfo.InvariantCulture));
382 return String.Format ("{0}_{1}_{2}", version, culture, token);
385 private static string AsmbNameFromVersionString (string name, string str)
387 string [] pieces = str.Split ('_');
388 return String.Format ("{0}, Version={1}, Culture={2}, PublicKeyToken={3}",
389 name, pieces [0], (pieces [1] == String.Empty ? "neutral" : pieces [1]),
393 private static bool IsSwitch (string arg)
395 return (arg [0] == '-' || (arg [0] == '/' && !File.Exists (arg)));
398 private static Command GetCommand (string arg)
400 Command c = Command.Unknown;
412 c = Command.Uninstall;
416 case "--uninstall-specific":
417 c = Command.UninstallSpecific;
433 private static void Symlink (string oldpath, string newpath) {
434 if (Path.DirectorySeparatorChar == '/') {
435 symlink (oldpath, newpath);
437 File.Copy (oldpath, newpath);
441 [DllImport ("libc", SetLastError=true)]
442 public static extern int symlink (string oldpath, string newpath);
444 private static string GetGacDir () {
445 PropertyInfo gac = typeof (System.Environment).GetProperty ("GacPath",
446 BindingFlags.Static|BindingFlags.NonPublic);
448 Console.WriteLine ("ERROR: Mono runtime not detected, please use " +
449 "the mono runtime for gacutil.exe");
450 Environment.Exit (1);
452 MethodInfo get_gac = gac.GetGetMethod (true);
453 return (string) get_gac.Invoke (null, null);
456 private static string GetLibDir () {
457 MethodInfo libdir = typeof (System.Environment).GetMethod ("internalGetGacPath",
458 BindingFlags.Static|BindingFlags.NonPublic);
459 if (libdir == null) {
460 Console.WriteLine ("ERROR: Mono runtime not detected, please use " +
461 "the mono runtime for gacutil.exe");
462 Environment.Exit (1);
464 return Path.Combine ((string)libdir.Invoke (null, null), "mono");
467 private static string GetStringToken (byte[] tok)
469 StringBuilder sb = new StringBuilder ();
470 for (int i = 0; i < tok.Length ; i++)
471 sb.Append (tok[i].ToString ("x2"));
472 return sb.ToString ();
475 private static string CombinePaths (string a, string b)
477 string dsc = Path.DirectorySeparatorChar.ToString ();
478 string sep = (a.EndsWith (dsc) ? String.Empty : dsc);
479 string end = (b.StartsWith (dsc) ? b.Substring (1) : b);
480 return String.Concat (a, sep, end);
483 private static void Usage ()
486 Environment.Exit (1);
489 private static void ShowHelp (bool detailed)
491 Console.WriteLine ("Usage: gacutil.exe <commands> [ <options> ]");
492 Console.WriteLine ("Commands:");
494 Console.WriteLine ("-i <assembly_path> [-check_refs] [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
495 Console.WriteLine ("\tInstalls an assembly into the global assembly cache.");
497 Console.WriteLine ("\t<assembly_path> is the name of the file that contains the " +
498 "\tassembly manifest\n" +
499 "\tExample: -i myDll.dll");
501 Console.WriteLine ();
503 Console.WriteLine ("-u <assembly_display_name> [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
504 Console.WriteLine ("\tUninstalls an assembly from the global assembly cache.");
506 Console.WriteLine ("\t<assembly_display_name> is the name of the assembly (partial or\n" +
507 "\tfully qualified) to remove from the global assembly cache. If a \n" +
508 "\tpartial name is specified all matching assemblies will be uninstalled.\n" +
509 "\tExample: -u myDll,Version=1.2.1.0");
511 Console.WriteLine ();
513 Console.WriteLine ("-us <assembly_path> [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
514 Console.WriteLine ("\tUninstalls an assembly using the specifed assemblies full name.");
516 Console.WriteLine ("\t<assembly path> is the path to an assembly. The full assembly name\n" +
517 "\tis retrieved from the specified assembly if there is an assembly in\n" +
518 "\tthe GAC with a matching name, it is removed.\n" +
519 "\tExample: -us myDll.dll");
521 Console.WriteLine ();
523 Console.WriteLine ("-l [assembly_name] [-root ROOTDIR] [-gacdir GACDIR]");
524 Console.WriteLine ("\tLists the contents of the global assembly cache.");
526 Console.WriteLine ("\tWhen the <assembly_name> parameter is specified only matching\n" +
527 "\tassemblies are listed.");
529 Console.WriteLine ();
531 Console.WriteLine ("-?");
532 Console.WriteLine ("\tDisplays a detailed help screen\n");
533 Console.WriteLine ();
538 Console.WriteLine ("Options:");
539 Console.WriteLine ("-package <NAME>");
540 Console.WriteLine ("Used to create a directory in prefix/lib/mono with the name NAME, and a\n" +
541 "\tsymlink is created from NAME/assembly_name to the assembly on the GAC.\n" +
542 "\tThis is used so developers can reference a set of libraries at once.");
543 Console.WriteLine ();
545 Console.WriteLine ("-gacdir <GACDIR>");
546 Console.WriteLine ("Used to specify the GACs base directory. Once an assembly has been installed\n" +
547 "\tto a non standard gacdir the MONO_GAC_PATH environment variable must be used\n" +
548 "\tto access the assembly.");
549 Console.WriteLine ();
551 Console.WriteLine ("-root <ROOTDIR>");
552 Console.WriteLine ("\tUsed by developers integrating this with automake tools or packaging tools\n" +
553 "\tthat require a prefix directory to be specified. The root represents the\n" +
554 "\t\"libdir\" component of a prefix (typically prefix/lib).");
555 Console.WriteLine ();
557 Console.WriteLine ("-check_refs");
558 Console.WriteLine ("\tUsed to ensure that the assembly being installed into the GAC does not\n" +
559 "\treference any non strong named assemblies. Assemblies being installed to\n" +
560 "\tthe GAC should not reference non strong named assemblies, however the is\n" +
561 "\tan optional check.\n");