6bdd276d05
Former-commit-id: fd56571888259555122d8a0f58c68838229cea2b
1090 lines
31 KiB
C#
1090 lines
31 KiB
C#
//
|
|
// Mono.Tools.GacUtil
|
|
//
|
|
// Author(s):
|
|
// Todd Berman <tberman@sevenl.net>
|
|
// Jackson Harper <jackson@ximian.com>
|
|
//
|
|
// Copyright 2003, 2004 Todd Berman
|
|
// Copyright 2004 Novell, Inc (http://www.novell.com)
|
|
//
|
|
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Diagnostics;
|
|
using System.Text;
|
|
using System.Collections;
|
|
using System.Globalization;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security.Cryptography;
|
|
|
|
using Mono.Security;
|
|
using Mono.Security.Cryptography;
|
|
|
|
using IKVM.Reflection;
|
|
|
|
namespace Mono.Tools {
|
|
|
|
public class Driver {
|
|
|
|
private enum Command {
|
|
Unknown,
|
|
Install,
|
|
InstallFromList,
|
|
Uninstall,
|
|
UninstallFromList,
|
|
UninstallSpecific,
|
|
List,
|
|
Help
|
|
}
|
|
|
|
private enum VerificationResult
|
|
{
|
|
StrongNamed,
|
|
WeakNamed,
|
|
DelaySigned,
|
|
Skipped
|
|
}
|
|
|
|
private static bool silent;
|
|
static bool in_bootstrap;
|
|
private static Universe _universe;
|
|
|
|
public static int Main (string [] args)
|
|
{
|
|
if (args.Length == 0)
|
|
Usage ();
|
|
|
|
Command command = Command.Unknown;
|
|
string command_str = null;
|
|
|
|
string libdir;
|
|
string name, package, gacdir, root;
|
|
name = package = root = gacdir = null;
|
|
bool check_refs = false;
|
|
|
|
// Check for silent arg first so we can suppress
|
|
// warnings during command line parsing
|
|
if (Array.IndexOf (args, "/silent") > -1 || Array.IndexOf (args, "-silent") > -1)
|
|
silent = true;
|
|
|
|
for (int i=0; i<args.Length; i++) {
|
|
if (IsSwitch (args [i])) {
|
|
|
|
// for cmd line compatibility with other gacutils
|
|
// we always force it though
|
|
if (args [i] == "-f" || args [i] == "/f")
|
|
continue;
|
|
|
|
// Ignore this option for now, although we might implement it someday
|
|
if (args [i] == "/r") {
|
|
WriteLine ("WARNING: gacutil does not support traced references." +
|
|
"This option is being ignored.");
|
|
i += 3;
|
|
continue;
|
|
}
|
|
|
|
// This is already handled we just dont want to choke on it
|
|
if (args [i] == "-silent" || args [i] == "/silent")
|
|
continue;
|
|
|
|
if (args [i] == "-check_refs" || args [i] == "/check_refs") {
|
|
check_refs = true;
|
|
continue;
|
|
}
|
|
|
|
if (args [i] == "-bootstrap" || args [i] == "/bootstrap") {
|
|
in_bootstrap = true;
|
|
continue;
|
|
}
|
|
|
|
if (command == Command.Unknown) {
|
|
command = GetCommand (args [i]);
|
|
if (command != Command.Unknown) {
|
|
command_str = args [i];
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (i + 1 >= args.Length) {
|
|
Console.WriteLine ("Option " + args [i] + " takes 1 argument");
|
|
return 1;
|
|
}
|
|
|
|
switch (args [i]) {
|
|
case "-package":
|
|
case "/package":
|
|
package = args [++i];
|
|
continue;
|
|
case "-root":
|
|
case "/root":
|
|
root = args [++i];
|
|
continue;
|
|
case "-gacdir":
|
|
case "/gacdir":
|
|
gacdir = args [++i];
|
|
continue;
|
|
case "/nologo":
|
|
case "-nologo":
|
|
// we currently don't display a
|
|
// logo banner, so ignore it
|
|
// for command-line compatibility
|
|
// with MS gacutil
|
|
continue;
|
|
}
|
|
}
|
|
if (name == null)
|
|
name = args [i];
|
|
else
|
|
name += args [i];
|
|
}
|
|
|
|
if (command == Command.Unknown && IsSwitch (args [0])) {
|
|
Console.WriteLine ("Unknown command: " + args [0]);
|
|
return 1;
|
|
} else if (command == Command.Unknown) {
|
|
Usage ();
|
|
} else if (command == Command.Help) {
|
|
ShowHelp (true);
|
|
return 1;
|
|
}
|
|
|
|
if (gacdir == null) {
|
|
gacdir = GetGacDir ();
|
|
libdir = GetLibDir ();
|
|
} else {
|
|
gacdir = EnsureLib (gacdir);
|
|
libdir = Path.Combine (gacdir, "mono");
|
|
gacdir = Path.Combine (libdir, "gac");
|
|
}
|
|
|
|
string link_gacdir = gacdir;
|
|
string link_libdir = libdir;
|
|
if (root != null) {
|
|
libdir = Path.Combine (root, "mono");
|
|
gacdir = Path.Combine (libdir, "gac");
|
|
}
|
|
|
|
LoadConfig (silent);
|
|
|
|
switch (command) {
|
|
case Command.Install:
|
|
if (name == null) {
|
|
WriteLine ("Option " + command_str + " takes 1 argument");
|
|
return 1;
|
|
}
|
|
if (!Install (check_refs, name, package, gacdir, link_gacdir, libdir, link_libdir))
|
|
return 1;
|
|
break;
|
|
case Command.InstallFromList:
|
|
if (name == null) {
|
|
WriteLine ("Option " + command_str + " takes 1 argument");
|
|
return 1;
|
|
}
|
|
if (!InstallFromList (check_refs, name, package, gacdir, link_gacdir, libdir, link_libdir))
|
|
return 1;
|
|
break;
|
|
case Command.Uninstall:
|
|
if (name == null) {
|
|
WriteLine ("Option " + command_str + " takes 1 argument");
|
|
return 1;
|
|
}
|
|
int uninstallCount = 0;
|
|
int uninstallFailures = 0;
|
|
Uninstall (name, package, gacdir, libdir, false,
|
|
ref uninstallCount, ref uninstallFailures);
|
|
WriteLine ("Assemblies uninstalled = {0}", uninstallCount);
|
|
WriteLine ("Failures = {0}", uninstallFailures);
|
|
if (uninstallFailures > 0)
|
|
return 1;
|
|
break;
|
|
case Command.UninstallFromList:
|
|
if (name == null) {
|
|
WriteLine ("Option " + command_str + " takes 1 argument");
|
|
return 1;
|
|
}
|
|
if (!UninstallFromList (name, package, gacdir, libdir))
|
|
return 1;
|
|
break;
|
|
case Command.UninstallSpecific:
|
|
if (name == null) {
|
|
WriteLine ("Option " + command_str + " takes 1 argument");
|
|
return 1;
|
|
}
|
|
if (!UninstallSpecific (name, package, gacdir, libdir))
|
|
return 1;
|
|
break;
|
|
case Command.List:
|
|
List (name, gacdir);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void Copy (string source, string target, bool v)
|
|
{
|
|
try {
|
|
File.Delete (target);
|
|
} catch {}
|
|
File.Copy (source, target, v);
|
|
}
|
|
|
|
private static bool Install (bool check_refs, string name, string package,
|
|
string gacdir, string link_gacdir, string libdir, string link_libdir)
|
|
{
|
|
string failure_msg = "Failure adding assembly {0} to the cache: ";
|
|
ArrayList resources;
|
|
|
|
if (!File.Exists (name)) {
|
|
WriteLine (string.Format (failure_msg, name) + "The system cannot find the file specified.");
|
|
return false;
|
|
}
|
|
|
|
Assembly assembly = null;
|
|
AssemblyName an = null;
|
|
|
|
try {
|
|
assembly = ReflectionOnlyLoadFrom (name);
|
|
} catch {
|
|
WriteLine (string.Format (failure_msg, name) + "The file specified is not a valid assembly.");
|
|
return false;
|
|
}
|
|
|
|
an = assembly.GetName ();
|
|
|
|
switch (VerifyStrongName (an, name)) {
|
|
case VerificationResult.StrongNamed:
|
|
case VerificationResult.Skipped:
|
|
break;
|
|
case VerificationResult.WeakNamed:
|
|
WriteLine (string.Format (failure_msg, name) + "Attempt to install an assembly without a strong name"
|
|
+ (in_bootstrap ? "(continuing anyway)" : string.Empty));
|
|
if (!in_bootstrap)
|
|
return false;
|
|
break;
|
|
case VerificationResult.DelaySigned:
|
|
WriteLine (string.Format (failure_msg, name) + "Strong name cannot be verified for delay-signed assembly"
|
|
+ (in_bootstrap ? "(continuing anyway)" : string.Empty));
|
|
if (!in_bootstrap)
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
resources = new ArrayList ();
|
|
foreach (string res_name in assembly.GetManifestResourceNames ()) {
|
|
ManifestResourceInfo res_info = assembly.GetManifestResourceInfo (res_name);
|
|
|
|
if ((res_info.ResourceLocation & ResourceLocation.Embedded) == 0) {
|
|
if (!File.Exists (res_info.FileName)) {
|
|
WriteLine (string.Format (failure_msg, name) + "The system cannot find resource " + res_info.FileName);
|
|
return false;
|
|
}
|
|
|
|
resources.Add (res_info);
|
|
}
|
|
}
|
|
|
|
if (check_refs && !CheckReferencedAssemblies (an)) {
|
|
WriteLine (string.Format (failure_msg, name) +
|
|
"Attempt to install an assembly that " +
|
|
"references non strong named assemblies " +
|
|
"with -check_refs enabled.");
|
|
return false;
|
|
}
|
|
|
|
string [] siblings = { ".config", ".mdb" };
|
|
string version_token = an.Version + "_" +
|
|
an.CultureInfo.Name.ToLower (CultureInfo.InvariantCulture) + "_" +
|
|
GetStringToken (an.GetPublicKeyToken ());
|
|
string full_path = Path.Combine (Path.Combine (gacdir, an.Name), version_token);
|
|
string asmb_file = Path.GetFileName (name);
|
|
string asmb_path = Path.Combine (full_path, asmb_file);
|
|
string asmb_name = assembly.GetName ().Name;
|
|
|
|
if (Path.GetFileNameWithoutExtension (asmb_file) != asmb_name) {
|
|
WriteLine (string.Format (failure_msg, name) +
|
|
string.Format ("the filename \"{0}\" doesn't match the assembly name \"{1}\"",
|
|
asmb_file, asmb_name));
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
if (Directory.Exists (full_path)) {
|
|
// Wipe out the directory. This way we ensure old assemblies
|
|
// config files, and AOTd files are removed.
|
|
Directory.Delete (full_path, true);
|
|
}
|
|
Directory.CreateDirectory (full_path);
|
|
} catch {
|
|
WriteLine (string.Format (failure_msg, name) +
|
|
"gac directories could not be created, " +
|
|
"possibly permission issues.");
|
|
return false;
|
|
}
|
|
|
|
Copy (name, asmb_path, true);
|
|
|
|
var name_pdb = Path.ChangeExtension (name, ".pdb");
|
|
if (File.Exists (name_pdb)) {
|
|
Copy (name_pdb, Path.ChangeExtension (asmb_path, ".pdb"), true);
|
|
}
|
|
|
|
foreach (string ext in siblings) {
|
|
string sibling = String.Concat (name, ext);
|
|
if (File.Exists (sibling))
|
|
Copy (sibling, String.Concat (asmb_path, ext), true);
|
|
}
|
|
|
|
foreach (ManifestResourceInfo resource_info in resources) {
|
|
try {
|
|
Copy (resource_info.FileName, Path.Combine (full_path, Path.GetFileName (resource_info.FileName)), true);
|
|
} catch {
|
|
WriteLine ("ERROR: Could not install resource file " + resource_info.FileName);
|
|
Environment.Exit (1);
|
|
}
|
|
}
|
|
|
|
if (package != null) {
|
|
string ref_dir = Path.Combine (libdir, package);
|
|
string ref_path = Path.Combine (ref_dir, asmb_file);
|
|
|
|
if (File.Exists (ref_path))
|
|
File.Delete (ref_path);
|
|
try {
|
|
Directory.CreateDirectory (ref_dir);
|
|
} catch {
|
|
WriteLine ("ERROR: Could not create package dir file.");
|
|
Environment.Exit (1);
|
|
}
|
|
if (Path.DirectorySeparatorChar == '/') {
|
|
string pkg_path_abs = Path.Combine (gacdir, Path.Combine (an.Name, Path.Combine (version_token, asmb_file)));
|
|
string pkg_path = AbsoluteToRelativePath (ref_dir, pkg_path_abs);
|
|
symlink (pkg_path, ref_path);
|
|
|
|
var pdb_pkg_path = Path.ChangeExtension (pkg_path, ".pdb");
|
|
var pdb_ref_path = Path.ChangeExtension (ref_path, ".pdb");
|
|
|
|
if (File.Exists (pdb_pkg_path)) {
|
|
symlink (pdb_pkg_path, pdb_ref_path);
|
|
} else {
|
|
try {
|
|
File.Delete (pdb_ref_path);
|
|
} catch {
|
|
// Ignore error, just delete files that should not be there.
|
|
}
|
|
}
|
|
|
|
foreach (string ext in siblings) {
|
|
string sibling = String.Concat (pkg_path, ext);
|
|
string sref = String.Concat (ref_path, ext);
|
|
|
|
if (File.Exists (sibling))
|
|
symlink (sibling, sref);
|
|
else {
|
|
try {
|
|
File.Delete (sref);
|
|
} catch {
|
|
// Ignore error, just delete files that should not be there.
|
|
}
|
|
}
|
|
}
|
|
WriteLine ("Package exported to: {0} -> {1}", ref_path, pkg_path);
|
|
} else {
|
|
// string link_path = Path.Combine (Path.Combine (link_gacdir, an.Name), version_token);
|
|
//
|
|
// We can't use 'link_path' here, since it need not be a valid path at the time 'gacutil'
|
|
// is run, esp. when invoked in a DESTDIR install.
|
|
Copy (name, ref_path, true);
|
|
WriteLine ("Package exported to: " + ref_path);
|
|
}
|
|
}
|
|
|
|
WriteLine ("Installed {0} into the gac ({1})", name,
|
|
gacdir);
|
|
|
|
return true;
|
|
}
|
|
|
|
//from MonoDevelop.Core.FileService
|
|
unsafe static string AbsoluteToRelativePath (string baseDirectoryPath, string absPath)
|
|
{
|
|
if (!Path.IsPathRooted (absPath) || string.IsNullOrEmpty (baseDirectoryPath))
|
|
return absPath;
|
|
|
|
absPath = Path.GetFullPath (absPath);
|
|
baseDirectoryPath = Path.GetFullPath (baseDirectoryPath).TrimEnd (Path.DirectorySeparatorChar);
|
|
|
|
fixed (char* bPtr = baseDirectoryPath, aPtr = absPath) {
|
|
var bEnd = bPtr + baseDirectoryPath.Length;
|
|
var aEnd = aPtr + absPath.Length;
|
|
char* lastStartA = aEnd;
|
|
char* lastStartB = bEnd;
|
|
|
|
int indx = 0;
|
|
// search common base path
|
|
var a = aPtr;
|
|
var b = bPtr;
|
|
while (a < aEnd) {
|
|
if (*a != *b)
|
|
break;
|
|
if (IsSeparator (*a)) {
|
|
indx++;
|
|
lastStartA = a + 1;
|
|
lastStartB = b;
|
|
}
|
|
a++;
|
|
b++;
|
|
if (b >= bEnd) {
|
|
if (a >= aEnd || IsSeparator (*a)) {
|
|
indx++;
|
|
lastStartA = a + 1;
|
|
lastStartB = b;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (indx == 0)
|
|
return absPath;
|
|
|
|
if (lastStartA >= aEnd)
|
|
return ".";
|
|
|
|
// handle case a: some/path b: some/path/deeper...
|
|
if (a >= aEnd) {
|
|
if (IsSeparator (*b)) {
|
|
lastStartA = a + 1;
|
|
lastStartB = b;
|
|
}
|
|
}
|
|
|
|
// look how many levels to go up into the base path
|
|
int goUpCount = 0;
|
|
while (lastStartB < bEnd) {
|
|
if (IsSeparator (*lastStartB))
|
|
goUpCount++;
|
|
lastStartB++;
|
|
}
|
|
var size = goUpCount * 2 + goUpCount + aEnd - lastStartA;
|
|
var result = new char [size];
|
|
fixed (char* rPtr = result) {
|
|
// go paths up
|
|
var r = rPtr;
|
|
for (int i = 0; i < goUpCount; i++) {
|
|
*(r++) = '.';
|
|
*(r++) = '.';
|
|
*(r++) = Path.DirectorySeparatorChar;
|
|
}
|
|
// copy the remaining absulute path
|
|
while (lastStartA < aEnd)
|
|
*(r++) = *(lastStartA++);
|
|
}
|
|
return new string (result);
|
|
}
|
|
}
|
|
|
|
static bool IsSeparator (char ch)
|
|
{
|
|
return ch == Path.DirectorySeparatorChar || ch == Path.AltDirectorySeparatorChar || ch == Path.VolumeSeparatorChar;
|
|
}
|
|
|
|
private static void Uninstall (string name, string package, string gacdir, string libdir, bool listMode, ref int uninstalled, ref int failures)
|
|
{
|
|
string [] assembly_pieces = name.Split (new char[] { ',' });
|
|
Hashtable asm_info = new Hashtable ();
|
|
|
|
foreach (string item in assembly_pieces) {
|
|
if (item == String.Empty) continue;
|
|
string[] pieces = item.Trim ().Split (new char[] { '=' }, 2);
|
|
if(pieces.Length == 1)
|
|
asm_info ["assembly"] = pieces [0];
|
|
else
|
|
asm_info [pieces[0].Trim ().ToLower (CultureInfo.InvariantCulture)] = pieces [1];
|
|
}
|
|
|
|
string assembly_name = (string) asm_info ["assembly"];
|
|
string asmdir = Path.Combine (gacdir, assembly_name);
|
|
if (!Directory.Exists (asmdir)) {
|
|
if (listMode) {
|
|
failures++;
|
|
WriteLine ("Assembly: " + name);
|
|
}
|
|
WriteLine ("No assemblies found that match: " + name);
|
|
return;
|
|
}
|
|
|
|
string searchString = GetSearchString (asm_info);
|
|
string [] directories = Directory.GetDirectories (asmdir, searchString);
|
|
|
|
if (directories.Length == 0) {
|
|
if (listMode) {
|
|
failures++;
|
|
WriteLine ("Assembly: " + name);
|
|
WriteLine ("No assemblies found that match: " + name);
|
|
}
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < directories.Length; i++) {
|
|
if (listMode && i > 0)
|
|
break;
|
|
|
|
string dir = directories [i];
|
|
string extension = null;
|
|
|
|
if (File.Exists (Path.Combine (dir, assembly_name + ".dll"))) {
|
|
extension = ".dll";
|
|
} else if (File.Exists (Path.Combine (dir, assembly_name + ".exe"))) {
|
|
extension = ".exe";
|
|
} else {
|
|
failures++;
|
|
WriteLine("Cannot find the assembly: " + assembly_name);
|
|
continue;
|
|
}
|
|
|
|
string assembly_filename = assembly_name + extension;
|
|
|
|
AssemblyName an = AssemblyName.GetAssemblyName (
|
|
Path.Combine (dir, assembly_filename));
|
|
WriteLine ("Assembly: " + an.FullName);
|
|
|
|
Directory.Delete (dir, true);
|
|
if (package != null) {
|
|
string link_dir = Path.Combine (libdir, package);
|
|
string link = Path.Combine (link_dir, assembly_filename);
|
|
|
|
try {
|
|
File.Delete (link);
|
|
} catch {
|
|
// The file might not exist, happens with
|
|
// the debugger on make uninstall
|
|
}
|
|
|
|
if (Directory.GetFiles (link_dir).Length == 0) {
|
|
WriteLine ("Cleaning package directory, it is empty.");
|
|
try {
|
|
Directory.Delete (link_dir);
|
|
} catch {
|
|
// Workaround: GetFiles does not list Symlinks
|
|
}
|
|
}
|
|
}
|
|
|
|
uninstalled++;
|
|
WriteLine ("Uninstalled: " + an.FullName);
|
|
}
|
|
|
|
if (Directory.GetDirectories (asmdir).Length == 0) {
|
|
WriteLine ("Cleaning assembly dir, it is empty");
|
|
try {
|
|
Directory.Delete (asmdir);
|
|
} catch {
|
|
// Workaround: GetFiles does not list Symlinks
|
|
}
|
|
}
|
|
}
|
|
|
|
private static bool UninstallSpecific (string name, string package,
|
|
string gacdir, string libdir)
|
|
{
|
|
string failure_msg = "Failure to remove assembly from the cache: ";
|
|
|
|
if (!File.Exists (name)) {
|
|
WriteLine (failure_msg + "The system cannot find the file specified.");
|
|
return false;
|
|
}
|
|
|
|
AssemblyName an = null;
|
|
|
|
try {
|
|
an = AssemblyName.GetAssemblyName (name);
|
|
} catch {
|
|
WriteLine (failure_msg + "The file specified is not a valid assembly.");
|
|
return false;
|
|
}
|
|
|
|
int uninstallCount = 0;
|
|
int uninstallFailures = 0;
|
|
Uninstall (an.FullName.Replace (" ", String.Empty),
|
|
package, gacdir, libdir, true, ref uninstallCount,
|
|
ref uninstallFailures);
|
|
WriteLine ("Assemblies uninstalled = {0}", uninstallCount);
|
|
WriteLine ("Failures = {0}", uninstallFailures);
|
|
return (uninstallFailures == 0);
|
|
}
|
|
|
|
private static void List (string name, string gacdir)
|
|
{
|
|
WriteLine ("The following assemblies are installed into the GAC:");
|
|
|
|
if (name != null) {
|
|
FilteredList (name, gacdir);
|
|
return;
|
|
}
|
|
|
|
int count = 0;
|
|
DirectoryInfo gacinfo = new DirectoryInfo (gacdir);
|
|
foreach (DirectoryInfo parent in gacinfo.GetDirectories ()) {
|
|
foreach (DirectoryInfo dir in parent.GetDirectories ()) {
|
|
string asmb = Path.Combine (Path.Combine (parent.FullName, dir.Name), parent.Name) + ".dll";
|
|
if (File.Exists (asmb)) {
|
|
WriteLine (AsmbNameFromVersionString (parent.Name, dir.Name));
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
WriteLine ("Number of items = " + count);
|
|
}
|
|
|
|
private static void FilteredList (string name, string gacdir)
|
|
{
|
|
string [] assembly_pieces = name.Split (new char[] { ',' });
|
|
Hashtable asm_info = new Hashtable ();
|
|
|
|
foreach (string item in assembly_pieces) {
|
|
string[] pieces = item.Trim ().Split (new char[] { '=' }, 2);
|
|
if(pieces.Length == 1)
|
|
asm_info ["assembly"] = pieces [0];
|
|
else
|
|
asm_info [pieces[0].Trim ().ToLower (CultureInfo.InvariantCulture)] = pieces [1];
|
|
}
|
|
|
|
string asmdir = Path.Combine (gacdir, (string) asm_info ["assembly"]);
|
|
if (!Directory.Exists (asmdir)) {
|
|
WriteLine ("Number of items = 0");
|
|
return;
|
|
}
|
|
string search = GetSearchString (asm_info);
|
|
string [] dir_list = Directory.GetDirectories (asmdir, search);
|
|
|
|
int count = 0;
|
|
foreach (string dir in dir_list) {
|
|
string asmb = Path.Combine (dir, (string) asm_info ["assembly"]) + ".dll";
|
|
if (File.Exists (asmb)) {
|
|
WriteLine (AsmbNameFromVersionString ((string) asm_info ["assembly"],
|
|
new DirectoryInfo (dir).Name));
|
|
count++;
|
|
}
|
|
}
|
|
WriteLine ("Number of items = " + count);
|
|
}
|
|
|
|
private static bool InstallFromList (bool check_refs, string list_file, string package,
|
|
string gacdir, string link_gacdir, string libdir, string link_libdir)
|
|
{
|
|
StreamReader s = null;
|
|
int processed, failed;
|
|
string listdir = Path.GetDirectoryName (
|
|
Path.GetFullPath (list_file));
|
|
|
|
processed = failed = 0;
|
|
|
|
try {
|
|
s = new StreamReader (list_file);
|
|
|
|
string line;
|
|
while ((line = s.ReadLine ()) != null) {
|
|
string file = line.Trim ();
|
|
if (file.Length == 0)
|
|
continue;
|
|
|
|
string assemblyPath = Path.Combine (listdir,
|
|
file);
|
|
|
|
if (!Install (check_refs, assemblyPath, package, gacdir,
|
|
link_gacdir, libdir, link_libdir))
|
|
failed++;
|
|
processed++;
|
|
}
|
|
|
|
WriteLine ("Assemblies processed = {0}", processed);
|
|
WriteLine ("Assemblies installed = {0}", processed - failed);
|
|
WriteLine ("Failures = {0}", failed);
|
|
|
|
return (failed == 0);
|
|
} catch (IOException) {
|
|
WriteLine ("Failed to open assemblies list file " + list_file + ".");
|
|
return false;
|
|
} finally {
|
|
if (s != null)
|
|
s.Close ();
|
|
}
|
|
}
|
|
|
|
private static bool UninstallFromList (string list_file, string package,
|
|
string gacdir, string libdir)
|
|
{
|
|
StreamReader s = null;
|
|
int failed, uninstalled;
|
|
|
|
failed = uninstalled = 0;
|
|
|
|
try {
|
|
s = new StreamReader (list_file);
|
|
|
|
string line;
|
|
while ((line = s.ReadLine ()) != null) {
|
|
string name = line.Trim ();
|
|
if (name.Length == 0)
|
|
continue;
|
|
Uninstall (line, package, gacdir, libdir,
|
|
true, ref uninstalled, ref failed);
|
|
}
|
|
|
|
WriteLine ("Assemblies processed = {0}", uninstalled+failed);
|
|
WriteLine ("Assemblies uninstalled = {0}", uninstalled);
|
|
WriteLine ("Failures = {0}", failed);
|
|
|
|
return (failed == 0);
|
|
} catch (IOException) {
|
|
WriteLine ("Failed to open assemblies list file " + list_file + ".");
|
|
return false;
|
|
} finally {
|
|
if (s != null)
|
|
s.Close ();
|
|
}
|
|
}
|
|
|
|
private static Universe GetUniverse () {
|
|
if (_universe == null) {
|
|
_universe = new Universe (UniverseOptions.MetadataOnly);
|
|
}
|
|
return _universe;
|
|
}
|
|
|
|
private static Assembly ReflectionOnlyLoadFrom (string fileName)
|
|
{
|
|
return GetUniverse ().LoadFile (fileName);
|
|
}
|
|
private static AssemblyName GetCorlibName ()
|
|
{
|
|
return GetUniverse ().Mscorlib.GetName ();
|
|
}
|
|
|
|
private static bool CheckReferencedAssemblies (AssemblyName an)
|
|
{
|
|
try {
|
|
Assembly a = ReflectionOnlyLoadFrom (an.CodeBase);
|
|
AssemblyName corlib = GetCorlibName ();
|
|
|
|
foreach (AssemblyName ref_an in a.GetReferencedAssemblies ()) {
|
|
if (ref_an.Name == corlib.Name) // Just do a string compare so we can install on diff versions
|
|
continue;
|
|
byte [] pt = ref_an.GetPublicKeyToken ();
|
|
if (pt == null || pt.Length == 0) {
|
|
WriteLine ("Assembly " + ref_an.Name + " is not strong named.");
|
|
return false;
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
WriteLine (e.ToString ()); // This should be removed pre beta3
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static string GetSearchString (Hashtable asm_info)
|
|
{
|
|
if (asm_info.Keys.Count == 1)
|
|
return "*";
|
|
string version, culture, token;
|
|
|
|
version = asm_info ["version"] as string;
|
|
version = (version == null ? "*" : version + "*");
|
|
culture = asm_info ["culture"] as string;
|
|
culture = (culture == null ? "*" : (culture == "neutral") ? String.Empty : culture.ToLower (CultureInfo.InvariantCulture));
|
|
token = asm_info ["publickeytoken"] as string;
|
|
token = (token == null ? "*" : token.ToLower (CultureInfo.InvariantCulture));
|
|
|
|
return String.Format ("{0}_{1}_{2}", version, culture, token);
|
|
}
|
|
|
|
private static string AsmbNameFromVersionString (string name, string str)
|
|
{
|
|
string [] pieces = str.Split ('_');
|
|
return String.Format ("{0}, Version={1}, Culture={2}, PublicKeyToken={3}",
|
|
name, pieces [0], (pieces [1] == String.Empty ? "neutral" : pieces [1]),
|
|
pieces [2]);
|
|
}
|
|
|
|
static bool LoadConfig (bool quiet)
|
|
{
|
|
System.Reflection.MethodInfo config = typeof (System.Environment).GetMethod ("GetMachineConfigPath",
|
|
System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
|
|
|
|
if (config != null) {
|
|
string path = (string) config.Invoke (null, null);
|
|
|
|
bool exist = File.Exists (path);
|
|
if (!quiet && !exist)
|
|
Console.WriteLine ("Couldn't find machine.config");
|
|
|
|
StrongNameManager.LoadConfig (path);
|
|
return exist;
|
|
} else if (!quiet)
|
|
Console.WriteLine ("Couldn't resolve machine.config location (corlib issue)");
|
|
|
|
// default CSP
|
|
return false;
|
|
}
|
|
|
|
// modified copy from sn
|
|
private static VerificationResult VerifyStrongName (AssemblyName an, string assemblyFile)
|
|
{
|
|
byte [] publicKey = StrongNameManager.GetMappedPublicKey (an.GetPublicKeyToken ());
|
|
if ((publicKey == null) || (publicKey.Length < 12)) {
|
|
// no mapping
|
|
publicKey = an.GetPublicKey ();
|
|
if ((publicKey == null) || (publicKey.Length < 12))
|
|
return VerificationResult.WeakNamed;
|
|
}
|
|
|
|
// Note: MustVerify is based on the original token (by design). Public key
|
|
// remapping won't affect if the assembly is verified or not.
|
|
if (StrongNameManager.MustVerify (new System.Reflection.AssemblyName (an.FullName))) {
|
|
RSA rsa = CryptoConvert.FromCapiPublicKeyBlob (publicKey, 12);
|
|
StrongName sn = new StrongName (rsa);
|
|
if (sn.Verify (assemblyFile)) {
|
|
return VerificationResult.StrongNamed;
|
|
} else {
|
|
return VerificationResult.DelaySigned;
|
|
}
|
|
} else {
|
|
return VerificationResult.Skipped;
|
|
}
|
|
}
|
|
|
|
private static bool IsSwitch (string arg)
|
|
{
|
|
return (arg [0] == '-' || (arg [0] == '/' && !arg.EndsWith (".dll") && !arg.EndsWith (".exe") && arg.IndexOf ('/', 1) < 0 ) );
|
|
}
|
|
|
|
private static Command GetCommand (string arg)
|
|
{
|
|
Command c = Command.Unknown;
|
|
|
|
switch (arg) {
|
|
case "-i":
|
|
case "/i":
|
|
case "--install":
|
|
c = Command.Install;
|
|
break;
|
|
case "-il":
|
|
case "/il":
|
|
case "--install-from-list":
|
|
c = Command.InstallFromList;
|
|
break;
|
|
case "-u":
|
|
case "/u":
|
|
case "/uf":
|
|
case "--uninstall":
|
|
c = Command.Uninstall;
|
|
break;
|
|
case "-ul":
|
|
case "/ul":
|
|
case "--uninstall-from-list":
|
|
c = Command.UninstallFromList;
|
|
break;
|
|
case "-us":
|
|
case "/us":
|
|
case "--uninstall-specific":
|
|
c = Command.UninstallSpecific;
|
|
break;
|
|
case "-l":
|
|
case "/l":
|
|
case "--list":
|
|
c = Command.List;
|
|
break;
|
|
case "-?":
|
|
case "/?":
|
|
case "--help":
|
|
c = Command.Help;
|
|
break;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
[DllImport ("libc", SetLastError=true)]
|
|
public static extern int symlink (string oldpath, string newpath);
|
|
|
|
private static string GetGacDir () {
|
|
System.Reflection.PropertyInfo gac = typeof (System.Environment).GetProperty ("GacPath",
|
|
System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.NonPublic);
|
|
if (gac == null) {
|
|
WriteLine ("ERROR: Mono runtime not detected, please use " +
|
|
"the mono runtime for gacutil.exe");
|
|
Environment.Exit (1);
|
|
}
|
|
System.Reflection.MethodInfo get_gac = gac.GetGetMethod (true);
|
|
return (string) get_gac.Invoke (null, null);
|
|
}
|
|
|
|
private static string GetLibDir () {
|
|
System.Reflection.MethodInfo libdir = typeof (System.Environment).GetMethod ("internalGetGacPath",
|
|
System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.NonPublic);
|
|
if (libdir == null) {
|
|
WriteLine ("ERROR: Mono runtime not detected, please use " +
|
|
"the mono runtime for gacutil.exe");
|
|
Environment.Exit (1);
|
|
}
|
|
return Path.Combine ((string)libdir.Invoke (null, null), "mono");
|
|
}
|
|
|
|
private static string GetStringToken (byte[] tok)
|
|
{
|
|
StringBuilder sb = new StringBuilder ();
|
|
for (int i = 0; i < tok.Length ; i++)
|
|
sb.Append (tok[i].ToString ("x2"));
|
|
return sb.ToString ();
|
|
}
|
|
|
|
private static string EnsureLib (string dir)
|
|
{
|
|
DirectoryInfo d = new DirectoryInfo (dir);
|
|
if (d.Name == "lib")
|
|
return dir;
|
|
return Path.Combine (dir, "lib");
|
|
}
|
|
|
|
private static void WriteLine ()
|
|
{
|
|
if (silent)
|
|
return;
|
|
Console.WriteLine ();
|
|
}
|
|
|
|
private static void WriteLine (string line)
|
|
{
|
|
if (silent)
|
|
return;
|
|
Console.WriteLine (line);
|
|
}
|
|
|
|
private static void WriteLine (string line, params object [] p)
|
|
{
|
|
if (silent)
|
|
return;
|
|
Console.WriteLine (line, p);
|
|
}
|
|
|
|
private static void Usage ()
|
|
{
|
|
ShowHelp (false);
|
|
Environment.Exit (1);
|
|
}
|
|
|
|
private static void ShowHelp (bool detailed)
|
|
{
|
|
WriteLine ("Usage: gacutil.exe <commands> [ <options> ]");
|
|
WriteLine ("Commands:");
|
|
|
|
WriteLine ("-i <assembly_path> [-check_refs] [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
|
|
WriteLine ("\tInstalls an assembly into the global assembly cache.");
|
|
if (detailed) {
|
|
WriteLine ("\t<assembly_path> is the name of the file that contains the " +
|
|
"\tassembly manifest\n" +
|
|
"\tExample: -i myDll.dll");
|
|
}
|
|
WriteLine ();
|
|
|
|
WriteLine ("-il <assembly_list_file> [-check_refs] [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
|
|
WriteLine ("\tInstalls one or more assemblies into the global assembly cache.");
|
|
if (detailed) {
|
|
WriteLine ("\t<assembly_list_file> is the path to a test file containing a list of\n" +
|
|
"\tassembly file paths on separate lines.\n" +
|
|
"\tExample -il assembly_list.txt\n" +
|
|
"\t\tassembly_list.txt contents:\n" +
|
|
"\t\tassembly1.dll\n" +
|
|
"\t\tassembly2.dll");
|
|
}
|
|
WriteLine ();
|
|
|
|
WriteLine ("-u <assembly_display_name> [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
|
|
WriteLine ("\tUninstalls an assembly from the global assembly cache.");
|
|
if (detailed) {
|
|
WriteLine ("\t<assembly_display_name> is the name of the assembly (partial or\n" +
|
|
"\tfully qualified) to remove from the global assembly cache. If a \n" +
|
|
"\tpartial name is specified all matching assemblies will be uninstalled.\n" +
|
|
"\tExample: -u myDll,Version=1.2.1.0");
|
|
}
|
|
WriteLine ();
|
|
|
|
WriteLine ("-ul <assembly_list_file> [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
|
|
WriteLine ("\tUninstalls one or more assemblies from the global assembly cache.");
|
|
if (detailed) {
|
|
WriteLine ("\t<assembly_list_file> is the path to a test file containing a list of\n" +
|
|
"\tassembly names on separate lines.\n" +
|
|
"\tExample -ul assembly_list.txt\n" +
|
|
"\t\tassembly_list.txt contents:\n" +
|
|
"\t\tassembly1,Version=1.0.0.0,Culture=en,PublicKeyToken=0123456789abcdef\n" +
|
|
"\t\tassembly2,Version=2.0.0.0,Culture=en,PublicKeyToken=0123456789abcdef");
|
|
}
|
|
WriteLine ();
|
|
|
|
WriteLine ("-us <assembly_path> [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
|
|
WriteLine ("\tUninstalls an assembly using the specifed assemblies full name.");
|
|
if (detailed) {
|
|
WriteLine ("\t<assembly path> is the path to an assembly. The full assembly name\n" +
|
|
"\tis retrieved from the specified assembly if there is an assembly in\n" +
|
|
"\tthe GAC with a matching name, it is removed.\n" +
|
|
"\tExample: -us myDll.dll");
|
|
}
|
|
WriteLine ();
|
|
|
|
WriteLine ("-l [assembly_name] [-root ROOTDIR] [-gacdir GACDIR]");
|
|
WriteLine ("\tLists the contents of the global assembly cache.");
|
|
if (detailed) {
|
|
WriteLine ("\tWhen the <assembly_name> parameter is specified only matching\n" +
|
|
"\tassemblies are listed.");
|
|
}
|
|
WriteLine ();
|
|
|
|
WriteLine ("-?");
|
|
WriteLine ("\tDisplays a detailed help screen");
|
|
WriteLine ();
|
|
|
|
if (!detailed)
|
|
return;
|
|
|
|
WriteLine ("Options:");
|
|
WriteLine ("-package <NAME>");
|
|
WriteLine ("\tUsed to create a directory in prefix/lib/mono with the name NAME, and a\n" +
|
|
"\tsymlink is created from NAME/assembly_name to the assembly on the GAC.\n" +
|
|
"\tThis is used so developers can reference a set of libraries at once.");
|
|
WriteLine ();
|
|
|
|
WriteLine ("-gacdir <GACDIR>");
|
|
WriteLine ("\tUsed to specify the GACs base directory. Once an assembly has been installed\n" +
|
|
"\tto a non standard gacdir the MONO_GAC_PREFIX environment variable must be used\n" +
|
|
"\tto access the assembly.");
|
|
WriteLine ();
|
|
|
|
WriteLine ("-root <ROOTDIR>");
|
|
WriteLine ("\tUsed by developers integrating this with automake tools or packaging tools\n" +
|
|
"\tthat require a prefix directory to be specified. The root represents the\n" +
|
|
"\t\"libdir\" component of a prefix (typically prefix/lib).");
|
|
WriteLine ();
|
|
|
|
WriteLine ("-check_refs");
|
|
WriteLine ("\tUsed to ensure that the assembly being installed into the GAC does not\n" +
|
|
"\treference any non strong named assemblies. Assemblies being installed to\n" +
|
|
"\tthe GAC should not reference non strong named assemblies, however the is\n" +
|
|
"\tan optional check.");
|
|
|
|
WriteLine ();
|
|
WriteLine ("Ignored Options:");
|
|
WriteLine ("-f");
|
|
WriteLine ("\tThe Mono gacutil ignores the -f option to maintain commandline compatibility with");
|
|
WriteLine ("\tother gacutils. gacutil will always force the installation of a new assembly.");
|
|
|
|
WriteLine ();
|
|
WriteLine ("-r <reference_scheme> <reference_id> <description>");
|
|
WriteLine ("\tThe Mono gacutil has not implemented traced references and will emit a warning");
|
|
WriteLine ("\twhen this option is used.");
|
|
}
|
|
}
|
|
}
|
|
|