[sgen] Fix logging of major heap size with concurrent sweep
[mono.git] / mcs / class / Microsoft.Build.Tasks / Microsoft.Build.Tasks / CodeTaskFactory.cs
1 //
2 // CodeTaskFactory.cs
3 //
4 // Author:
5 //   Atsushi Enomoto <atsushi@xamarin.com>
6 //
7 // Copyright (C) 2014 Xamarin Inc.
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using Microsoft.Build.Framework;
30 using System.Collections.Generic;
31 using System.Xml;
32 using System.IO;
33 using Microsoft.Build.BuildEngine;
34 using System.CodeDom;
35 using Microsoft.CSharp;
36 using System.CodeDom.Compiler;
37 using System.Linq;
38 using System.Reflection;
39 using System.Collections.Specialized;
40
41 namespace Microsoft.Build.Tasks
42 {
43         public class CodeTaskFactory : ITaskFactory
44         {
45                 public CodeTaskFactory ()
46                 {
47                 }
48
49                 #region ITaskFactory implementation
50                 public void CleanupTask (ITask task)
51                 {
52                 }
53                 public ITask CreateTask (IBuildEngine loggingHost)
54                 {
55                         return CreateTask (loggingHost, null);
56                 }
57                 
58                 ITask CreateTask (IBuildEngine taskFactoryLoggingHost, IDictionary<string, string> taskIdentityParameters)
59                 {
60                         if (assembly == null)
61                                 return null;
62                         return (ITask) Activator.CreateInstance (assembly.GetType (task_name));
63                 }
64
65                 public TaskPropertyInfo [] GetTaskParameters ()
66                 {
67                         return parameter_group != null ? parameter_group.Values.ToArray () : new TaskPropertyInfo [0];
68                 }
69                 public bool Initialize (string taskName, IDictionary<string, TaskPropertyInfo> taskParameters, string taskElementContents, IBuildEngine taskFactoryLoggingHost)
70                 {
71                         return Initialize (taskName, null, taskParameters, taskElementContents, taskFactoryLoggingHost);
72                 }
73                 bool Initialize (string taskName, IDictionary<string, string> factoryIdentityParameters, IDictionary<string, TaskPropertyInfo> parameterGroup, string taskBody, IBuildEngine taskFactoryLoggingHost)
74                 {
75                         task_name = taskName;
76                         if (parameterGroup != null)
77                                 parameter_group = new Dictionary<string, TaskPropertyInfo> (parameterGroup);
78                         
79                         List<string> references = new List<string> ();
80                         List<string> namespace_uses = new List<string> ();
81                         namespace_uses.Add ("Microsoft.Build.Framework");
82                         string type = null, language = null, code = null;
83
84                         var xml = XmlReader.Create (new StringReader (taskBody), new XmlReaderSettings () { ConformanceLevel = ConformanceLevel.Fragment });
85                         for (xml.MoveToContent (); !xml.EOF; xml.MoveToContent ()) {
86                                 switch (xml.NodeType) {
87                                 case XmlNodeType.Element:
88                                         switch (xml.LocalName) {
89                                         case "Reference":
90                                                 references.Add (xml.GetAttribute ("Include"));
91                                                 xml.Skip ();
92                                                 break;
93                                         case "Using":
94                                                 namespace_uses.Add (xml.GetAttribute ("Namespace"));
95                                                 xml.Skip ();
96                                                 break;
97                                         case "Code":
98                                                 // MSB3757: Multiple Code elements have been found, this is not allowed.
99                                                 if (code != null)
100                                                         throw new InvalidProjectFileException (null, "Multiple Code elements are not allowed", "MSB", "3757", null);
101                                                 type = xml.GetAttribute ("Type");
102                                                 language = xml.GetAttribute ("Language");
103                                                 code = xml.ReadElementContentAsString ();
104                                                 break;
105                                         }
106                                         break;
107                                 default:
108                                         xml.Skip ();
109                                         break;
110                                 }
111                         }
112
113                         if (language != "cs" && language != "vb")
114                                 throw new InvalidProjectFileException (null, string.Format ("{0} is not supported language for inline task", language), "MSB", "4175", null);
115
116                         CodeCompileUnit ccu;
117
118                         if (type == "Class") {  // 'code' contains the whole class that implements the task
119                                 ccu = new CodeSnippetCompileUnit (code);
120                         }
121                         else {  // 'code' contains parts of the class that implements the task
122                                 ccu = new CodeCompileUnit ();
123                                 var nsp = new CodeNamespace ();
124                                 nsp.Imports.AddRange (namespace_uses.Select (x => new CodeNamespaceImport (x)).ToArray ());
125                                 ccu.Namespaces.Add (nsp);
126
127                                 var taskClass = new CodeTypeDeclaration {
128                                         IsClass = true,
129                                         Name = taskName,
130                                         TypeAttributes = TypeAttributes.Public
131                                 };
132
133                                 var parameters = new List<CodeMemberProperty> ();
134                                 var parametersBackingFields = new List<CodeMemberField> ();
135
136                                 // add a public property + backing field for each parameter
137                                 foreach (var param in parameter_group) {
138                                         var prop = new CodeMemberProperty {
139                                                 Attributes = MemberAttributes.Public | MemberAttributes.Final,
140                                                 Name = param.Value.Name,
141                                                 Type = new CodeTypeReference (param.Value.PropertyType)
142                                         };
143
144                                         var propBf = new CodeMemberField {
145                                                 Attributes = MemberAttributes.Private,
146                                                 Name = "_" + prop.Name,
147                                                 Type = prop.Type
148                                         };
149
150                                         // add getter and setter to the property
151                                         prop.GetStatements.Add (new CodeMethodReturnStatement (new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), propBf.Name)));
152                                         prop.SetStatements.Add (new CodeAssignStatement (new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), propBf.Name), new CodePropertySetValueReferenceExpression ()));
153
154                                         parameters.Add (prop);
155                                         parametersBackingFields.Add (propBf);
156                                 }
157
158                                 taskClass.Members.AddRange (parameters.ToArray ());
159                                 taskClass.Members.AddRange (parametersBackingFields.ToArray ());
160                                 taskClass.BaseTypes.Add ("Microsoft.Build.Utilities.Task");  // The documentation says "ITask", but the very first example shows "Log" which is not in ITask! It is likely that the generated code uses Task or TaskExtension.
161
162                                 if (type == "Method") {  // 'code' contains the 'Execute' method directly
163                                         taskClass.Members.Add (new CodeSnippetTypeMember (code));
164                                 }
165                                 else if (type == "Fragment") {  // 'code' contains the body of the 'Execute' method
166                                         var method = new CodeMemberMethod {
167                                                 Attributes = MemberAttributes.Public | MemberAttributes.Override,
168                                                 Name = "Execute",
169                                                 ReturnType = new CodeTypeReference (typeof (bool))
170                                         };
171
172                                         // add the code and a 'return true' at the end of the method
173                                         method.Statements.Add (new CodeSnippetStatement (code));
174                                         method.Statements.Add (new CodeMethodReturnStatement (new CodePrimitiveExpression (true)));
175
176                                         taskClass.Members.Add (method);
177                                 }
178                                 else {
179                                         throw new ArgumentException ("Invalid type: " + type);
180                                 }
181
182                                 nsp.Types.Add (taskClass);
183                         }
184
185                         var cscParams = new CompilerParameters ();
186                         cscParams.ReferencedAssemblies.Add ("Microsoft.Build.Framework.dll");
187                         cscParams.ReferencedAssemblies.Add ("Microsoft.Build.Utilities.v4.0.dll"); // since we use Task, it depends on this dll.
188                         cscParams.ReferencedAssemblies.AddRange (references.ToArray ());
189                         cscParams.GenerateInMemory = true;
190                         var results = CodeDomProvider.CreateProvider (language).CompileAssemblyFromDom (cscParams, ccu);
191                         var errors = new CompilerError [results.Errors.Count];
192                         results.Errors.CopyTo (errors, 0);
193                         if (errors.Any (e => !e.IsWarning)) {
194                                 string msg = string.Format ("Invalid '{0}' source code of '{1}' type: {2}", language, type, string.Join (" ", errors.Where (e => !e.IsWarning).Select (e => e.ToString ())));
195                                 throw new InvalidProjectFileException (null, msg, "MSB", "3758", null);
196                         }
197                         assembly = results.CompiledAssembly;
198                         return true;
199                 }
200                 public string FactoryName {
201                         get { return "Code Task Factory"; }
202                 }
203                 public Type TaskType {
204                         get { return null; }
205                 }
206
207                 string task_name;
208                 Assembly assembly;
209                 Dictionary<string, TaskPropertyInfo> parameter_group;
210
211                 #endregion
212         }
213 }
214