5 // Atsushi Enomoto <atsushi@xamarin.com>
7 // Copyright (C) 2014 Xamarin Inc.
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
30 using Microsoft.Build.Framework;
31 using System.Collections.Generic;
34 using Microsoft.Build.BuildEngine;
36 using Microsoft.CSharp;
37 using System.CodeDom.Compiler;
39 using System.Reflection;
40 using System.Collections.Specialized;
42 namespace Microsoft.Build.Tasks
44 public class CodeTaskFactory : ITaskFactory2
46 public CodeTaskFactory ()
50 #region ITaskFactory implementation
51 public void CleanupTask (ITask task)
54 public ITask CreateTask (IBuildEngine taskFactoryLoggingHost)
56 return CreateTask (taskFactoryLoggingHost, null);
58 public ITask CreateTask (IBuildEngine taskFactoryLoggingHost, IDictionary<string, string> taskIdentityParameters)
62 return (ITask) Activator.CreateInstance (assembly.GetType (task_name));
65 public TaskPropertyInfo [] GetTaskParameters ()
67 return parameter_group != null ? parameter_group.Values.ToArray () : new TaskPropertyInfo [0];
69 public bool Initialize (string taskName, IDictionary<string, TaskPropertyInfo> parameterGroup, string taskBody, IBuildEngine taskFactoryLoggingHost)
71 return Initialize (taskName, null, parameterGroup, taskBody, taskFactoryLoggingHost);
73 public bool Initialize (string taskName, IDictionary<string, string> factoryIdentityParameters, IDictionary<string, TaskPropertyInfo> parameterGroup, string taskBody, IBuildEngine taskFactoryLoggingHost)
76 if (parameterGroup != null)
77 parameter_group = new Dictionary<string, TaskPropertyInfo> (parameterGroup);
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;
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) {
90 references.Add (xml.GetAttribute ("Include"));
94 namespace_uses.Add (xml.GetAttribute ("Namespace"));
98 // MSB3757: Multiple Code elements have been found, this is not allowed.
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 ();
112 if (language == "vb")
113 throw new NotImplementedException (string.Format ("{0} is not supported language for inline task", language));
114 if (language != "cs")
115 throw new InvalidProjectFileException (null, string.Format ("{0} is not supported language for inline task", language), "MSB", "4175", null);
117 // 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.
118 string classTemplate = @"public class " + taskName + " : Microsoft.Build.Utilities.Task { @@EXECUTE@@ }";
119 foreach (var ns in namespace_uses)
120 gen += "using " + ns + ";\n";
126 gen += classTemplate.Replace ("@@EXECUTE@@", code);
129 gen += classTemplate.Replace ("@@EXECUTE@@", "public override bool Execute () { " + code + " return true; }");
133 var cscParams = new CompilerParameters ();
134 cscParams.ReferencedAssemblies.Add ("Microsoft.Build.Framework.dll");
135 cscParams.ReferencedAssemblies.Add ("Microsoft.Build.Utilities.v4.0.dll"); // since we use Task, it depends on this dll.
136 var results = new CSharpCodeProvider ().CompileAssemblyFromSource (cscParams, gen);
137 var errors = new CompilerError [results.Errors.Count];
138 results.Errors.CopyTo (errors, 0);
139 if (errors.Any (e => !e.IsWarning)) {
140 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 ())));
141 throw new InvalidProjectFileException (null, msg, "MSB", "3758", null);
143 assembly = results.CompiledAssembly;
146 public string FactoryName {
147 get { return "Code Task Factory"; }
149 public Type TaskType {
155 Dictionary<string, TaskPropertyInfo> parameter_group;