Merge pull request #2819 from BrzVlad/fix-major-log
[mono.git] / mcs / class / Microsoft.Build.Tasks / Microsoft.Build.Tasks / CodeTaskFactory.cs
index 5e853036989822e8dbe31e7b2a504a0e7fb5d273..9d759e09d9d5be9fd8a72b9326d19cfcee53253c 100644 (file)
@@ -25,7 +25,6 @@
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-#if NET_4_0
 using System;
 using Microsoft.Build.Framework;
 using System.Collections.Generic;
@@ -41,7 +40,7 @@ using System.Collections.Specialized;
 
 namespace Microsoft.Build.Tasks
 {
-       public class CodeTaskFactory : ITaskFactory2
+       public class CodeTaskFactory : ITaskFactory
        {
                public CodeTaskFactory ()
                {
@@ -51,11 +50,12 @@ namespace Microsoft.Build.Tasks
                public void CleanupTask (ITask task)
                {
                }
-               public ITask CreateTask (IBuildEngine taskFactoryLoggingHost)
+               public ITask CreateTask (IBuildEngine loggingHost)
                {
-                       return CreateTask (taskFactoryLoggingHost, null);
+                       return CreateTask (loggingHost, null);
                }
-               public ITask CreateTask (IBuildEngine taskFactoryLoggingHost, IDictionary<string, string> taskIdentityParameters)
+               
+               ITask CreateTask (IBuildEngine taskFactoryLoggingHost, IDictionary<string, string> taskIdentityParameters)
                {
                        if (assembly == null)
                                return null;
@@ -66,11 +66,11 @@ namespace Microsoft.Build.Tasks
                {
                        return parameter_group != null ? parameter_group.Values.ToArray () : new TaskPropertyInfo [0];
                }
-               public bool Initialize (string taskName, IDictionary<string, TaskPropertyInfo> parameterGroup, string taskBody, IBuildEngine taskFactoryLoggingHost)
+               public bool Initialize (string taskName, IDictionary<string, TaskPropertyInfo> taskParameters, string taskElementContents, IBuildEngine taskFactoryLoggingHost)
                {
-                       return Initialize (taskName, null, parameterGroup, taskBody, taskFactoryLoggingHost);
+                       return Initialize (taskName, null, taskParameters, taskElementContents, taskFactoryLoggingHost);
                }
-               public bool Initialize (string taskName, IDictionary<string, string> factoryIdentityParameters, IDictionary<string, TaskPropertyInfo> parameterGroup, string taskBody, IBuildEngine taskFactoryLoggingHost)
+               bool Initialize (string taskName, IDictionary<string, string> factoryIdentityParameters, IDictionary<string, TaskPropertyInfo> parameterGroup, string taskBody, IBuildEngine taskFactoryLoggingHost)
                {
                        task_name = taskName;
                        if (parameterGroup != null)
@@ -109,31 +109,85 @@ namespace Microsoft.Build.Tasks
                                        break;
                                }
                        }
-                       if (language == "vb")
-                               throw new NotImplementedException (string.Format ("{0} is not supported language for inline task", language));
-                       if (language != "cs")
+
+                       if (language != "cs" && language != "vb")
                                throw new InvalidProjectFileException (null, string.Format ("{0} is not supported language for inline task", language), "MSB", "4175", null);
-                       string gen = null;
-                       // 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.
-                       string classTemplate = @"public class " + taskName + " : Microsoft.Build.Utilities.Task { @@EXECUTE@@ }";
-                       foreach (var ns in namespace_uses)
-                               gen += "using " + ns + ";\n";
-                       switch (type) {
-                       case "Class":
-                               gen += code;
-                               break;
-                       case "Method":
-                               gen += classTemplate.Replace ("@@EXECUTE@@", code);
-                               break;
-                       case "Fragment":
-                               gen += classTemplate.Replace ("@@EXECUTE@@", "public override bool Execute () { " + code + " return true; }");
-                               break;
+
+                       CodeCompileUnit ccu;
+
+                       if (type == "Class") {  // 'code' contains the whole class that implements the task
+                               ccu = new CodeSnippetCompileUnit (code);
+                       }
+                       else {  // 'code' contains parts of the class that implements the task
+                               ccu = new CodeCompileUnit ();
+                               var nsp = new CodeNamespace ();
+                               nsp.Imports.AddRange (namespace_uses.Select (x => new CodeNamespaceImport (x)).ToArray ());
+                               ccu.Namespaces.Add (nsp);
+
+                               var taskClass = new CodeTypeDeclaration {
+                                       IsClass = true,
+                                       Name = taskName,
+                                       TypeAttributes = TypeAttributes.Public
+                               };
+
+                               var parameters = new List<CodeMemberProperty> ();
+                               var parametersBackingFields = new List<CodeMemberField> ();
+
+                               // add a public property + backing field for each parameter
+                               foreach (var param in parameter_group) {
+                                       var prop = new CodeMemberProperty {
+                                               Attributes = MemberAttributes.Public | MemberAttributes.Final,
+                                               Name = param.Value.Name,
+                                               Type = new CodeTypeReference (param.Value.PropertyType)
+                                       };
+
+                                       var propBf = new CodeMemberField {
+                                               Attributes = MemberAttributes.Private,
+                                               Name = "_" + prop.Name,
+                                               Type = prop.Type
+                                       };
+
+                                       // add getter and setter to the property
+                                       prop.GetStatements.Add (new CodeMethodReturnStatement (new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), propBf.Name)));
+                                       prop.SetStatements.Add (new CodeAssignStatement (new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), propBf.Name), new CodePropertySetValueReferenceExpression ()));
+
+                                       parameters.Add (prop);
+                                       parametersBackingFields.Add (propBf);
+                               }
+
+                               taskClass.Members.AddRange (parameters.ToArray ());
+                               taskClass.Members.AddRange (parametersBackingFields.ToArray ());
+                               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.
+
+                               if (type == "Method") {  // 'code' contains the 'Execute' method directly
+                                       taskClass.Members.Add (new CodeSnippetTypeMember (code));
+                               }
+                               else if (type == "Fragment") {  // 'code' contains the body of the 'Execute' method
+                                       var method = new CodeMemberMethod {
+                                               Attributes = MemberAttributes.Public | MemberAttributes.Override,
+                                               Name = "Execute",
+                                               ReturnType = new CodeTypeReference (typeof (bool))
+                                       };
+
+                                       // add the code and a 'return true' at the end of the method
+                                       method.Statements.Add (new CodeSnippetStatement (code));
+                                       method.Statements.Add (new CodeMethodReturnStatement (new CodePrimitiveExpression (true)));
+
+                                       taskClass.Members.Add (method);
+                               }
+                               else {
+                                       throw new ArgumentException ("Invalid type: " + type);
+                               }
+
+                               nsp.Types.Add (taskClass);
                        }
 
                        var cscParams = new CompilerParameters ();
                        cscParams.ReferencedAssemblies.Add ("Microsoft.Build.Framework.dll");
                        cscParams.ReferencedAssemblies.Add ("Microsoft.Build.Utilities.v4.0.dll"); // since we use Task, it depends on this dll.
-                       var results = new CSharpCodeProvider ().CompileAssemblyFromSource (cscParams, gen);
+                       cscParams.ReferencedAssemblies.AddRange (GetReferences (references, taskFactoryLoggingHost));
+                       cscParams.GenerateInMemory = true;
+                       var results = CodeDomProvider.CreateProvider (language).CompileAssemblyFromDom (cscParams, ccu);
                        var errors = new CompilerError [results.Errors.Count];
                        results.Errors.CopyTo (errors, 0);
                        if (errors.Any (e => !e.IsWarning)) {
@@ -143,6 +197,36 @@ namespace Microsoft.Build.Tasks
                        assembly = results.CompiledAssembly;
                        return true;
                }
+
+               static string[] GetReferences (List<string> references, IBuildEngine log)
+               {
+                       var res = new List<string> ();
+                       foreach (var r in references) {
+                               if (File.Exists (r)) {
+                                       res.Add (r);
+                                       continue;
+                               }
+
+                               Assembly assembly = null;
+
+                               try {
+                                       if (!r.EndsWith (".dll", StringComparison.OrdinalIgnoreCase) || !r.EndsWith (".exe", StringComparison.OrdinalIgnoreCase)) {
+                                               assembly = Assembly.LoadWithPartialName (r);
+                                       }
+
+                                       if (assembly != null) {
+                                               res.Add (assembly.Location);
+                                               continue;
+                                       }
+                               } catch {
+                               }
+
+                               log.LogErrorEvent (new BuildErrorEventArgs ("", "", "", 0, 0, 0, 0, "Assembly reference {r} could not be resolved", "", ""));
+                       }
+
+                       return res.ToArray ();
+               }
+
                public string FactoryName {
                        get { return "Code Task Factory"; }
                }
@@ -158,4 +242,3 @@ namespace Microsoft.Build.Tasks
        }
 }
 
-#endif