3 // Author: Todd Berman <tberman@gentoo.org>
5 // (C) 2003 Todd Berman
10 using System.Reflection;
11 using System.Collections;
12 using System.Globalization;
13 using System.Runtime.InteropServices;
23 private string libdir = InternalLibdir () + Path.DirectorySeparatorChar;
24 private string gac_path = GetGacPath ();
25 private string package_name = String.Empty;
28 public static int Main (string[] args)
30 Driver d = new Driver ();
34 public int Run (string[] args)
36 if (args.Length == 0) {
41 if (args[0] == "/user" || args[0] == "--user") {
42 //FIXME: Need to check machine.config to make sure this is legal (potential security hole)
43 gac_path = Path.Combine (Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal), ".mono"), "gac");
44 gac_path += Path.DirectorySeparatorChar;
45 libdir = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal), ".mono");
46 libdir += Path.DirectorySeparatorChar;
48 string[] stripped = new string[args.Length - 1];
49 Array.Copy (args, 1, stripped, 0, args.Length - 1);
53 installed_gac = gac_path;
54 if (args.Length >= 2 && (args[args.Length - 2] == "/root" || args[args.Length - 2] == "-root" || args[args.Length - 2] == "--root")) {
55 gac_path = Path.Combine (Path.Combine (args[args.Length - 1], "mono"), "gac");
56 gac_path += Path.DirectorySeparatorChar;
57 libdir = Path.Combine (args[args.Length - 1], "mono");
58 libdir += Path.DirectorySeparatorChar;
60 string[] stripped = new string[args.Length - 2]; Array.Copy (args, 0, stripped, 0, args.Length - 2);
64 if (args.Length >= 2 && (args[args.Length - 2] == "/package" || args[args.Length - 2] == "--package" || args[args.Length - 2] == "-package")) {
65 package_name = args[args.Length - 1];
66 string[] stripped = new string[args.Length - 2];
67 Array.Copy (args, 0, stripped, 0, args.Length - 2);
72 string[] remainder_args = new string[args.Length - 1];
74 if (args.Length >= 2) {
75 Array.Copy (args, 1, remainder_args, 0, args.Length - 1);
87 return InstallAssembly (remainder_args);
91 return ListAssemblies (remainder_args);
95 return UninstallAssemblies (remainder_args);
98 case "--install-from-list":
99 return InstallAssembliesFromList (remainder_args);
102 case "--uninstall-from-list":
103 return UninstallAssembliesFromList (remainder_args);
112 public int InstallAssembliesFromList (string[] args)
114 if (args.Length == 0) {
115 Console.WriteLine ("ERROR: need a file passed");
119 if (!File.Exists (args[0])) {
120 Console.WriteLine ("ERROR: file '" + args[0] + "' does not exist");
124 string[] perFile = args;
127 using (StreamReader s = File.OpenText (args[0])) {
130 while((line = s.ReadLine()) != null) {
133 if (InstallAssembly (perFile) != 0)
135 } catch (Exception e) {
136 Console.WriteLine ("Failed for {0}. Reason: {1}", line, e.Message);
145 public int UninstallAssembliesFromList (string[] args)
147 if (args.Length == 0) {
148 Console.WriteLine ("ERROR: file must be passed.");
152 if (!File.Exists (args[0])) {
153 Console.WriteLine ("ERROR: file '" + args[0] + "' does not exist");
158 using (StreamReader s = File.OpenText (args[0])) {
161 while ((line = s.ReadLine ()) != null) {
162 if (UninstallAssemblies (new string[] { line } ) != 0)
170 public int UninstallAssemblies (string[] args)
172 if(args.Length == 0) {
173 Console.WriteLine ("ERROR: need an argument to uninstall");
177 string joinedArgs = String.Join ("", args);
179 string[] assemblyPieces = joinedArgs.Split(new char[] { ',' });
181 Hashtable paramInfo = new Hashtable ();
183 foreach (string item in assemblyPieces) {
184 string[] pieces = item.Trim ().Split (new char[] { '=' }, 2);
185 if(pieces.Length == 1)
186 paramInfo["assembly"] = pieces[0];
188 paramInfo[pieces[0].Trim ().ToLower (CultureInfo.InvariantCulture)] = pieces[1];
191 if (!Directory.Exists (Path.Combine (gac_path, (string) paramInfo["assembly"]))) {
192 Console.WriteLine ("ERROR: Assembly not in gac.");
196 string searchString = (string) paramInfo["assembly"] + Path.DirectorySeparatorChar;
198 if (paramInfo.Keys.Count != 1) {
199 if (paramInfo["version"] != null) {
200 searchString += (string) paramInfo["version"] + "*";
206 string[] directories = Directory.GetDirectories (gac_path, searchString);
208 foreach (string dir in directories) {
209 Hashtable info = GetAssemblyInfo (Path.Combine (dir, "__AssemblyInfo__"));
210 if(Convert.ToInt32 (info["RefCount"]) == 1) {
211 Directory.Delete (dir, true);
212 if (package_name != String.Empty) {
213 File.Delete (libdir + package_name + Path.DirectorySeparatorChar + (string)paramInfo["assembly"] + ".dll");
215 Console.WriteLine ("Assembly removed from the gac.");
217 info["RefCount"] = ((int) Convert.ToInt32 (info["RefCount"]) - 1).ToString ();
218 WriteAssemblyInfo (Path.Combine (dir, "__AssemblyInfo__"), info);
219 Console.WriteLine ("Assembly was not deleted because its still needed by other applications");
222 if(Directory.GetDirectories (Path.Combine (gac_path, (string) paramInfo["assembly"])).Length == 0) {
223 Console.WriteLine ("Cleaning assembly dir, its empty");
224 Directory.Delete (Path.Combine (gac_path, (string) paramInfo["assembly"]));
230 public int ListAssemblies (string[] args)
232 Console.WriteLine ("The following assemblies are installed into the GAC:");
233 DirectoryInfo d = new DirectoryInfo (gac_path);
234 foreach (DirectoryInfo namedDir in d.GetDirectories ()) {
235 foreach (DirectoryInfo assemblyDir in namedDir.GetDirectories ()) {
236 Hashtable assemblyInfo = GetAssemblyInfo (Path.Combine (assemblyDir.FullName, "__AssemblyInfo__"));
237 if (assemblyInfo != null){
238 Console.WriteLine ("\t" + assemblyInfo["DisplayName"]);
246 private Hashtable GetAssemblyInfo (string filename)
249 Hashtable infoHash = new Hashtable ();
250 using (StreamReader s = new StreamReader (filename)) {
253 while ((line = s.ReadLine ()) != null) {
254 string[] splitStr = line.Split (new char[] { '=' }, 2);
255 infoHash[splitStr[0]] = splitStr[1];
264 private void WriteAssemblyInfo (string filename, Hashtable info)
266 using (StreamWriter s = File.CreateText (filename)) {
267 foreach (string key in info.Keys) {
268 s.WriteLine (key + "=" + (string) info[key]);
273 public int InstallAssembly (string[] args)
275 if(args.Length == 0) {
276 Console.WriteLine ("ERROR: You must specify a valid assembly name after the install switch");
280 if(!File.Exists (args[0])) {
281 Console.WriteLine ("ERROR: The assembly: '" + args[0] + "' does not exist");
285 AssemblyName an = AssemblyName.GetAssemblyName (args[0]);
286 string config_path = null;
287 byte[] pub_tok = an.GetPublicKeyToken ();
289 if (pub_tok == null || pub_tok.Length == 0) {
290 Console.WriteLine ("ERROR: assembly has no valid public key token");
294 config_path = args [0] + ".config";
295 // strong name verification temp. disabled
297 byte[] akey = an.GetPublicKey ();
298 if (akey == null || akey.Length < 12) {
299 Console.WriteLine ("ERROR: assembly has no valid public key token");
302 StrongName sn = new StrongName (akey);
303 if (!sn.Verify (args[0])) {
304 Console.WriteLine ("ERROR: invalid strongname signature in assembly");
309 //FIXME: force=true per mig's request.
312 if (Array.IndexOf (args, "/f") != -1 || Array.IndexOf (args, "-f") != -1 ||
313 Array.IndexOf (args, "--force") != -1) {
317 string version_token = an.Version + "_" +
318 an.CultureInfo.Name.ToLower (CultureInfo.InvariantCulture) +
319 "_" + GetStringToken (an.GetPublicKeyToken ());
321 string fullPath = String.Format ("{0}{3}{1}{3}{2}{3}", gac_path, an.Name, version_token, Path.DirectorySeparatorChar);
322 string linkPath = String.Format ("{0}{3}{1}{3}{2}{3}", installed_gac, an.Name, version_token, Path.DirectorySeparatorChar);
324 if (File.Exists (fullPath + an.Name + ".dll") && force == false) {
325 Hashtable assemInfo = GetAssemblyInfo (fullPath + "__AssemblyInfo__");
327 if (assemInfo != null){
328 assemInfo["RefCount"] = ((int) Convert.ToInt32 (assemInfo["RefCount"]) + 1).ToString ();
329 WriteAssemblyInfo (fullPath + "__AssemblyInfo__", assemInfo);
330 Console.WriteLine ("RefCount of assembly '" + an.Name + "' increased by one.");
331 if (File.Exists (config_path))
332 File.Copy (config_path, fullPath + an.Name + ".dll" + ".config", force);
333 InstallPackage (libdir, linkPath, an, args [0], Path.GetFileName (args [0]));
338 if(!EnsureDirectories (an.Name, version_token)) {
339 Console.WriteLine ("ERROR: gac directories could not be created, possibly permission issues");
343 File.Copy (args[0], fullPath + an.Name + ".dll", force);
344 InstallPackage (libdir, linkPath, an, args [0], Path.GetFileName (args [0]));
345 if (File.Exists (config_path)){
346 File.Copy (config_path, fullPath + an.Name + ".dll" + ".config", force);
349 Hashtable info = new Hashtable ();
351 info["DisplayName"] = an.FullName;
352 info["RefCount"] = 1.ToString ();
354 WriteAssemblyInfo (fullPath + "__AssemblyInfo__", info);
356 Console.WriteLine ("{0} installed into the gac ({1})", an.Name, gac_path);
360 private void InstallPackage (string libdir, string linkPath,
361 AssemblyName an, string path, string filename)
363 if (package_name != String.Empty) {
364 string ref_file = libdir + package_name +
365 Path.DirectorySeparatorChar + filename;
366 if (File.Exists (ref_file)) {
367 File.Delete (ref_file);
369 if (Path.DirectorySeparatorChar == '/') {
371 Directory.CreateDirectory (libdir + package_name);
374 symlink (linkPath + an.Name + ".dll", ref_file);
377 File.Copy (path, ref_file);
379 Console.WriteLine ("Package exported to: " + libdir + package_name);
383 private bool EnsureDirectories (string name, string tok)
385 //FIXME: Workaround for broken DirectoryInfo.CreateSubdirectory
387 DirectoryInfo d = new DirectoryInfo (gac_path);
389 d.CreateSubdirectory (name);
390 d = new DirectoryInfo (Path.Combine (gac_path, name));
391 d.CreateSubdirectory (tok);
392 if (package_name != String.Empty) {
393 d = new DirectoryInfo (libdir);
394 d.CreateSubdirectory (package_name);
402 private string GetStringToken (byte[] tok)
404 StringBuilder sb = new StringBuilder ();
405 for (int i = 0; i < tok.Length ; i++) {
406 sb.Append (tok[i].ToString ("x2"));
408 return sb.ToString ();
411 public void ShowHelp (bool detailed)
413 StringBuilder sb = new StringBuilder ();
415 sb.Append ("Usage: gacutil.exe <commands> [ <options> ]\n");
416 sb.Append ("Commands:\n");
417 sb.Append (" -i <assembly_path> [ -f ] [-package NAME] [-root ROOTDIR]\n");
418 if (detailed == false) {
419 sb.Append (" Installs an assembly into the global assembly cache\n");
421 sb.Append (" Installs an assembly to the global assembly cache. <assembly_path> is the\n name of the file that contains the assembly manifest. \n Example: -i myDll.dll\n");
426 sb.Append (" -il <assembly_path_list_file> [ -f ]\n");
427 if (detailed == false) {
428 sb.Append (" Installs one or more assemblies into the global assembly cache\n");
430 sb.Append (" Installs on or more assemblies to the global assembly cache. \n <assembly_list_file is the path to a text file that contains a list of \n assembly manifest file paths. Individual paths in the text file must be \n separated by a newline.\n");
431 sb.Append (" Example: -il MyAssemblyList\n");
432 sb.Append (" MyAssemblyList content:\n");
433 sb.Append (" Mydll.dll\n");
434 sb.Append (" Mydll2.dll\n");
435 sb.Append (" path/to/myDll3.dll\n");
440 sb.Append (" -u <assembly_display_name> [-package NAME] [-root ROOTDIR]\n");
441 if (detailed == false) {
442 sb.Append (" Uninstalls an assembly from the global assembly cache\n");
444 sb.Append (" Uninstalls an assembly. <assembly_display_name> is the name of the assembly\n");
445 sb.Append (" (partial or fully qualified) to remove from the global assembly cache. \n If a partial name is specified all matching assemblies will be uninstalled.\n");
446 sb.Append (" Example: /u myDll,Version=1.2.1.0\n");
451 sb.Append (" -ul <assembly_display_name_list_file>\n");
452 if (detailed == false) {
453 sb.Append (" Uninstalls one or more assemblies from the global assembly cache\n");
455 sb.Append (" Uninstalls one or more assemblies from the global assembly cache. \n <assembly_display_name_list_file> is the path to a text file that contains\n a list of assembly names. Individual names in the text file must be \n separated by a newline.\n");
456 sb.Append (" Example: /ul MyAssemblyList\n");
457 sb.Append (" MyAssemblyList content:\n");
458 sb.Append (" MyDll1.dll,Version=1.0.0.0\n");
459 sb.Append (" MyDll2.dll,Version=1.2.0.0\n");
465 if (detailed == false) {
466 sb.Append (" List the global assembly cache\n");
468 sb.Append (" Lists the contents of the global assembly cache.\n");
474 if (detailed == false) {
475 sb.Append (" Displays a detailed help screen\n");
477 sb.Append (" Displays a detailed help screen\n");
482 if (detailed == true) {
483 sb.Append ("Options:\n");
485 sb.Append (" Forces reinstall of assembly, resets reference count\n");
488 sb.Append ("Note, mono's gacutil also supports these unix like aliases for its commands:\n");
489 sb.Append (" -i -> --install\n");
490 sb.Append (" -il -> --install-from-list\n");
491 sb.Append (" -u -> --uninstall\n");
492 sb.Append (" -ul -> --uninstall-from-list\n");
493 sb.Append (" -l -> --ls\n");
494 sb.Append (" -f -> --force\n");
496 sb.Append ("Mono also allows a User Assembly Cache, this cache can be accessed by passing\n/user as the first argument to gacutil.exe\n");
499 Console.WriteLine (sb.ToString ());
502 private static string GetGacPath () {
503 PropertyInfo gac = typeof (System.Environment).GetProperty ("GacPath", BindingFlags.Static|BindingFlags.NonPublic);
505 Console.WriteLine ("ERROR: MS.Net runtime detected, please use the mono runtime for gacutil.exe");
506 Environment.Exit (1);
508 MethodInfo getGac = gac.GetGetMethod (true);
509 return Path.Combine ((string) getGac.Invoke (null, null), "");
512 private static string InternalLibdir () {
513 MethodInfo libdir = typeof (System.Environment).GetMethod ("internalGetGacPath", BindingFlags.Static|BindingFlags.NonPublic);
514 if (libdir == null) {
515 Console.WriteLine ("ERROR: MS.Net runtime detected, please use the mono runtime for gacutil.exe");
516 Environment.Exit (1);
518 return Path.Combine (Path.Combine ((string)libdir.Invoke (null, null), "mono"), "");
521 [DllImport ("libc", SetLastError=true)]
522 public static extern int symlink (string oldpath, string newpath);