c1b580195035b98d8a8c6550e96e258ba37ac291
[teamcity/dotNetPackagesSupport.git] / nuget-extensions / nuget-runner / src / NuGetRunner.cs
1 using System;\r
2 using System.Collections.Generic;\r
3 using System.Diagnostics;\r
4 using System.IO;\r
5 using System.Reflection;\r
6 using System.Linq;\r
7 using System.Text;\r
8 using System.Threading;\r
9 \r
10 namespace JetBrains.TeamCity.NuGetRunner\r
11 {\r
12   public class NuGetRunner\r
13   {\r
14     private readonly string myNuGetExe;\r
15     private readonly Assembly myNuGetAssembly;\r
16     private readonly List<EventHandler> myStartEvents = new List<EventHandler>();\r
17     private readonly List<EventHandler> myFinishEvents = new List<EventHandler>();\r
18 \r
19     public NuGetRunner(string NuGetExe)\r
20     {\r
21       myNuGetExe = NuGetExe;\r
22       if (!File.Exists(NuGetExe))\r
23         throw new NuGetLoadException("Failed to find NuGet.exe at " + myNuGetExe);\r
24 \r
25       try\r
26       {\r
27         myNuGetAssembly = Assembly.LoadFrom(myNuGetExe);\r
28       }\r
29       catch (Exception e)\r
30       {\r
31         throw new NuGetLoadException("Failed to load NuGet assembly into AppDomain. " + e.Message, e);\r
32       }\r
33 \r
34       NuGetExtensionsPath = new Lazy<string>(LocateNuGetExtensionsPath);\r
35     }\r
36 \r
37     private string LocateNuGetExtensionsPath()\r
38     {\r
39       var mi = myNuGetAssembly.EntryPoint.DeclaringType;\r
40       foreach (\r
41         var type in\r
42           new Func<Type>[] {() => mi.DeclaringType, () => myNuGetAssembly.GetType("NuGet.Program")}.Select(Compute).\r
43             Where(x => x != null))\r
44       {\r
45         var field = type.GetField("ExtensionsDirectoryRoot",\r
46                                   BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);\r
47         if (field != null && field.FieldType == typeof (string))\r
48         {\r
49           var extensionsPath = field.GetValue(null) as string;\r
50           if (extensionsPath != null)\r
51             return extensionsPath;\r
52         }\r
53       }\r
54       //This is explicit path value taken from NuGet source code\r
55       return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "NuGet", "Commands");\r
56     }\r
57 \r
58     public Lazy<string> NuGetExtensionsPath { get; private set; }\r
59 \r
60     public event EventHandler BeforeNuGetStarted { add { myStartEvents.Add(value); } remove { myStartEvents.Remove(value);  } }\r
61     public event EventHandler AfterNuGetFinished { add { myFinishEvents.Add(value); } remove { myFinishEvents.Remove(value); } }\r
62 \r
63     private void CallEvents(IEnumerable<EventHandler> handler)\r
64     {\r
65       foreach (var h in handler)\r
66       {\r
67         try\r
68         {\r
69           h(this, EventArgs.Empty);\r
70         } catch (Exception e)\r
71         {\r
72           Console.Error.WriteLine("Failed to execute event: " + e);\r
73         }\r
74       }\r
75     }\r
76 \r
77     public int Run(IEnumerable<string> argz)\r
78     {\r
79       CallEvents(myStartEvents);\r
80 \r
81       try\r
82       {\r
83         var process = Process.Start(new ProcessStartInfo\r
84                                       {\r
85                                         FileName = myNuGetExe,\r
86                                         //TODO use escapring safe escaping here.\r
87                                         Arguments = string.Join(" ", argz.Select(x=>x.IndexOfAny(" \t\n\r".ToCharArray()) >=0 ? "\"" + x + "\"" : x)),\r
88                                         UseShellExecute = false,\r
89                                         RedirectStandardInput = true, \r
90                                         RedirectStandardError = true, \r
91                                         RedirectStandardOutput = true, \r
92                                         CreateNoWindow = true,\r
93                                       });\r
94 \r
95         process.StandardInput.Close();\r
96         Func<StreamReader, TextWriter, Thread> readOutput = (si, so) =>\r
97         {\r
98           var th = new Thread(delegate()\r
99           {\r
100             int i;\r
101             while ((i = si.Read()) >= 0) so.Write((char)i);            \r
102           }) { Name = "Process output reader " + process.Id };\r
103           th.Start();\r
104           return th;\r
105         };\r
106 \r
107         var t1 = readOutput(process.StandardOutput, Console.Out);\r
108         var t2 = readOutput(process.StandardError, Console.Error);\r
109         \r
110         process.WaitForExit();\r
111 \r
112         t1.Join(TimeSpan.FromMinutes(5));\r
113         t2.Join(TimeSpan.FromMinutes(5));\r
114 \r
115         return process.ExitCode;\r
116         /*AppDomain dom = AppDomain.CreateDomain("NuGet Launcher Domain");\r
117         var result = dom.ExecuteAssembly(myNuGetExe, argz);\r
118         return result is int ? (int) result : 0;*/\r
119       }\r
120       finally\r
121       {\r
122         CallEvents(myFinishEvents);\r
123       }\r
124     }\r
125 \r
126     private static T Compute<T>(Func<T> func) where T : class\r
127     {\r
128       try\r
129       {\r
130         return func();\r
131       }\r
132       catch\r
133       {\r
134         return null;\r
135       }\r
136     }\r
137   }\r
138 }