public static System.Reflection.Assembly GenerateCode(ITextTemplatingEngineHost host, ParsedTemplate pt, 
		                                                       TemplateSettings settings, CodeCompileUnit ccu)
        {
            CompilerParameters pars = new CompilerParameters ();
            pars.GenerateExecutable = false;

            if (settings.Debug) {
                pars.GenerateInMemory = false;
                pars.IncludeDebugInformation = true;
                pars.TempFiles.KeepFiles = true;
            } else {
                pars.GenerateInMemory = true;
                pars.IncludeDebugInformation = false;
            }

            //resolve and add assembly references
            HashSet<string> assemblies = new HashSet<string> ();
            assemblies.UnionWith (settings.Assemblies);
            assemblies.UnionWith (host.StandardAssemblyReferences);
            foreach (string assem in assemblies) {
                string resolvedAssem = host.ResolveAssemblyReference (assem);
                if (!String.IsNullOrEmpty (resolvedAssem)) {
                    pars.ReferencedAssemblies.Add (resolvedAssem);
                } else {
                    pt.LogError ("Could not resolve assembly reference '" + assem + "'");
                    return null;
                }
            }
            CompilerResults results = settings.Provider.CompileAssemblyFromDom (pars, ccu);
            pt.Errors.AddRange (results.Errors);
            if (pt.Errors.HasErrors)
                return null;
            return results.CompiledAssembly;
        }
		public CompiledTemplate (ParsedTemplate parsedTemplate, ITextTemplatingEngineHost host, Assembly assembly, TemplateSettings settings)
		{
			this.host = host;
			this.assembly = assembly;
			this.settings = settings;
			this.parsedTemplate = parsedTemplate;
		}
        public static CodeCompileUnit GenerateCompileUnit(ITextTemplatingEngineHost host, ParsedTemplate pt,
		                                                   TemplateSettings settings)
        {
            //prep the compile unit
            var ccu = new CodeCompileUnit ();
            var namespac = new CodeNamespace (settings.Namespace);
            ccu.Namespaces.Add (namespac);

            var imports = new HashSet<string> ();
            imports.UnionWith (settings.Imports);
            imports.UnionWith (host.StandardImports);
            foreach (string ns in imports)
                namespac.Imports.Add (new CodeNamespaceImport (ns));

            //prep the type
            var type = new CodeTypeDeclaration (settings.Name);
            type.IsPartial = true;
            if (!String.IsNullOrEmpty (settings.Inherits))
                type.BaseTypes.Add (new CodeTypeReference (settings.Inherits));
            else
                type.BaseTypes.Add (new CodeTypeReference (typeof (TextTransformation)));
            namespac.Types.Add (type);

            //prep the transform method
            var transformMeth = new CodeMemberMethod () {
                Name = "TransformText",
                ReturnType = new CodeTypeReference (typeof (String)),
                Attributes = MemberAttributes.Public | MemberAttributes.Override
            };

            //method references that will need to be used multiple times
            var writeMeth = new CodeMethodReferenceExpression (new CodeThisReferenceExpression (), "Write");
            var toStringMeth = new CodeMethodReferenceExpression (new CodeTypeReferenceExpression (typeof (ToStringHelper)), "ToStringWithCulture");
            bool helperMode = false;

            //build the code from the segments
            foreach (TemplateSegment seg in pt.Content) {
                CodeStatement st = null;
                var location = new CodeLinePragma (seg.StartLocation.FileName ?? host.TemplateFile, seg.StartLocation.Line);
                switch (seg.Type) {
                case SegmentType.Block:
                    if (helperMode)
                        //TODO: are blocks permitted after helpers?
                        throw new ParserException ("Blocks are not permitted after helpers", seg.StartLocation);
                    st = new CodeSnippetStatement (seg.Text);
                    break;
                case SegmentType.Expression:
                    st = new CodeExpressionStatement (
                        new CodeMethodInvokeExpression (writeMeth,
                            new CodeMethodInvokeExpression (toStringMeth, new CodeSnippetExpression (seg.Text))));
                    break;
                case SegmentType.Content:
                    st = new CodeExpressionStatement (new CodeMethodInvokeExpression (writeMeth, new CodePrimitiveExpression (seg.Text)));
                    break;
                case SegmentType.Helper:
                    type.Members.Add (new CodeSnippetTypeMember (seg.Text) { LinePragma = location });
                    helperMode = true;
                    break;
                default:
                    throw new InvalidOperationException ();
                }
                if (st != null) {
                    if (helperMode) {
                        //convert the statement into a snippet member and attach it to the top level type
                        //TODO: is there a way to do this for languages that use indentation for blocks, e.g. python?
                        using (var writer = new StringWriter ()) {
                            settings.Provider.GenerateCodeFromStatement (st, writer, null);
                            type.Members.Add (new CodeSnippetTypeMember (writer.ToString ()) { LinePragma = location });
                        }
                    } else {
                        st.LinePragma = location;
                        transformMeth.Statements.Add (st);
                        continue;
                    }
                }
            }

            //complete the transform method
            transformMeth.Statements.Add (new CodeMethodReturnStatement (
                new CodeMethodInvokeExpression (
                    new CodePropertyReferenceExpression (
                        new CodeThisReferenceExpression (),
                        "GenerationEnvironment"),
                        "ToString")));
            type.Members.Add (transformMeth);

            //generate the Host property if needed
            if (settings.HostSpecific) {
                var hostField = new CodeMemberField (new CodeTypeReference (typeof (ITextTemplatingEngineHost)), "hostValue");
                hostField.Attributes = (hostField.Attributes & ~MemberAttributes.AccessMask) | MemberAttributes.Private;
                type.Members.Add (hostField);

                var hostProp = new CodeMemberProperty () {
                    Name = "Host",
                    Attributes = MemberAttributes.Public,
                    HasGet = true,
                    HasSet = true,
                    Type = hostField.Type
                };
                var hostFieldRef = new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), "hostValue");
                hostProp.SetStatements.Add (new CodeAssignStatement (hostFieldRef, new CodePropertySetValueReferenceExpression ()));
                hostProp.GetStatements.Add (new CodeMethodReturnStatement (hostFieldRef));
                type.Members.Add (hostProp);
            }

            return ccu;
        }
        public static TemplateSettings GetSettings(ITextTemplatingEngineHost host, ParsedTemplate pt)
        {
            string language = null;

            TemplateSettings settings = new TemplateSettings ();
            foreach (Directive dt in pt.Directives) {
                switch (dt.Name) {
                case "template":
                    string val = dt.Extract ("language");
                    if (val != null)
                        language = val;
                    val = dt.Extract ("debug");
                    if (val != null)
                        settings.Debug = string.Compare (val, "true", StringComparison.OrdinalIgnoreCase) == 0;
                    val = dt.Extract ("inherits");
                    if (val != null)
                        settings.Inherits = val;
                    val = dt.Extract ("culture");
                    if (val != null) {
                        System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.GetCultureInfo (val);
                        if (culture == null)
                            pt.LogWarning ("Could not find culture '" + val + "'", dt.StartLocation);
                        else
                            settings.Culture = culture;
                    }
                    val = dt.Extract ("hostspecific");
                    if (val != null) {
                        settings.HostSpecific = string.Compare (val, "true", StringComparison.OrdinalIgnoreCase) == 0;
                    }
                    break;

                case "assembly":
                    string name = dt.Extract ("name");
                    if (name == null)
                        pt.LogError ("Missing name attribute in assembly directive", dt.StartLocation);
                    else
                        settings.Assemblies.Add (name);
                    break;

                case "import":
                    string namespac = dt.Extract ("namespace");
                    if (namespac == null)
                        pt.LogError ("Missing namespace attribute in import directive", dt.StartLocation);
                    else
                        settings.Imports.Add (namespac);
                    break;

                case "output":
                    settings.Extension = dt.Extract ("extension");
                    string encoding = dt.Extract ("encoding");
                    if (encoding != null)
                        settings.Encoding = Encoding.GetEncoding ("encoding");
                    break;

                case "include":
                    throw new InvalidOperationException ("Include is handled in the parser");

                default:
                    throw new NotImplementedException ("Custom directives are not supported yet");
                }
                ComplainExcessAttributes (dt, pt);
            }

            if (settings.Name == null)
                settings.Name = string.Format ("GeneratedTextTransformation{0:x}", new System.Random ().Next ());
            if (settings.Namespace == null)
                settings.Namespace = typeof (TextTransformation).Namespace;

            //resolve the CodeDOM provider
            if (String.IsNullOrEmpty (language)) {
                pt.LogError ("No language was specified for the template");
                return settings;
            }

            if (language == "C#v3.5") {
                Dictionary<string, string> providerOptions = new Dictionary<string, string> ();
                providerOptions.Add ("CompilerVersion", "v3.5");
                settings.Provider = new CSharpCodeProvider (providerOptions);
            }
            else {
                settings.Provider = CodeDomProvider.CreateProvider (language);
            }

            if (settings.Provider == null) {
                pt.LogError ("A provider could not be found for the language '" + language + "'");
                return settings;
            }

            return settings;
        }
			return false;
		}
		
		public static CodeCompileUnit GenerateCompileUnit (ITextTemplatingEngineHost host, ParsedTemplate pt,
		                                                   TemplateSettings settings)
		{
			//prep the compile unit
			CodeCompileUnit ccu = new CodeCompileUnit ();
			CodeNamespace namespac = new CodeNamespace (settings.Namespace);
			ccu.Namespaces.Add (namespac);
			
			HashSet<string> imports = new HashSet<string> ();
			imports.UnionWith (settings.Imports);
			imports.UnionWith (host.StandardImports);
			foreach (string ns in imports)
				namespac.Imports.Add (new CodeNamespaceImport (ns));
			
			//prep the type
			CodeTypeDeclaration type = new CodeTypeDeclaration (settings.Name);
			if (!String.IsNullOrEmpty (settings.Inherits))
				type.BaseTypes.Add (new CodeTypeReference (settings.Inherits));
			else
				type.BaseTypes.Add (new CodeTypeReference (typeof (TextTransformation)));
			namespac.Types.Add (type);
			
			//prep the transform method
			CodeMemberMethod transformMeth = new CodeMemberMethod ();
			transformMeth.Name = "TransformText";
			transformMeth.ReturnType = new CodeTypeReference (typeof (String));
			transformMeth.Attributes = MemberAttributes.Public | MemberAttributes.Override;
			
			//method references that will need to be used multiple times
			CodeMethodReferenceExpression writeMeth =
				new CodeMethodReferenceExpression (new CodeThisReferenceExpression (), "Write");
			CodeMethodReferenceExpression toStringMeth =
				new CodeMethodReferenceExpression (new CodeTypeReferenceExpression (typeof (ToStringHelper)), "ToStringWithCulture");
			
			//build the code from the segments
			foreach (TemplateSegment seg in pt.Content) {
				CodeStatement st = null;
				switch (seg.Type) {
				case SegmentType.Block:
					st = new CodeSnippetStatement (seg.Text);
					break;
				case SegmentType.Expression:
					st = new CodeExpressionStatement (
						new CodeMethodInvokeExpression (writeMeth,
							new CodeMethodInvokeExpression (toStringMeth, new CodeSnippetExpression (seg.Text))));
					break;
				case SegmentType.Content:
					st = new CodeExpressionStatement (new CodeMethodInvokeExpression (writeMeth, new CodePrimitiveExpression (seg.Text)));
					break;
				case SegmentType.Helper:
					CodeTypeMember mem = new CodeSnippetTypeMember (seg.Text);
					mem.LinePragma = new CodeLinePragma (host.TemplateFile, seg.StartLocation.Line);
					type.Members.Add (mem);
					break;
				default:
					throw new InvalidOperationException ();
				}
				if (st != null) {
					st.LinePragma = new CodeLinePragma (host.TemplateFile, seg.StartLocation.Line);
					transformMeth.Statements.Add (st);
				}
			}
			
			//complete the transform method
			transformMeth.Statements.Add (new CodeMethodReturnStatement (
				new CodeMethodInvokeExpression (
					new CodePropertyReferenceExpression (
						new CodeThisReferenceExpression (),
						"GenerationEnvironment"),
						"ToString")));
			type.Members.Add (transformMeth);
			
			//generate the Host property if needed
			if (settings.HostSpecific) {
				CodeMemberField hostField = new CodeMemberField (new CodeTypeReference (typeof (ITextTemplatingEngineHost)), "hostValue");
				hostField.Attributes = (hostField.Attributes & ~MemberAttributes.AccessMask) | MemberAttributes.Private;
				type.Members.Add (hostField);
				CodeMemberProperty hostProp = new CodeMemberProperty ();
				hostProp.Name = "Host";
				hostProp.Attributes = MemberAttributes.Public;
				hostProp.HasGet = hostProp.HasGet = true;
				hostProp.Type = hostField.Type;
				CodeFieldReferenceExpression hostFieldRef = new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), "hostValue");
				hostProp.SetStatements.Add (new CodeAssignStatement (hostFieldRef, new CodePropertySetValueReferenceExpression ()));
				hostProp.GetStatements.Add (new CodeMethodReturnStatement (hostFieldRef));
				type.Members.Add (hostProp);
			}
Beispiel #6
0
        static int MainInternal(string [] args)
        {
            if (args.Length == 0 && !Console.IsInputRedirected)
            {
                ShowHelp(true);
            }

            var    generator = new ToolTemplateGenerator();
            string outputFile = null, inputFile = null;
            var    directives          = new List <string> ();
            var    parameters          = new List <string> ();
            var    properties          = new Dictionary <string, string> ();
            string preprocessClassName = null;
            bool   debug   = false;
            bool   verbose = false;

            optionSet = new OptionSet {
                {
                    "o=|out=",
                    "Name or path of the output {<file>}. Defaults to the input filename with its " +
                    "extension changed to `.txt'. Use `-' to output to stdout.",
                    s => outputFile = s
                },
                {
                    "r=",
                    "Name or path of an {<assembly>} reference. Assemblies will be resolved from the " +
                    "framework and the include folders",
                    s => generator.Refs.Add(s)
                },
                {
                    "u=|using=",
                    "Import a {<namespace>}' statement with a `using",
                    s => generator.Imports.Add(s)
                },
                {
                    "I=",
                    "Search {<directory>} when resolving file includes",
                    s => generator.IncludePaths.Add(s)
                },
                {
                    "P=",
                    "Search {<directory>} when resolving assembly references",
                    s => generator.ReferencePaths.Add(s)
                },
                {
                    "c=|class=",
                    "Preprocess the template into class {<name>}",
                    (s) => preprocessClassName = s
                },
                {
                    "p:=",
                    "Add a {<name>}={<value>} key-value pair to the template's `Session' " +
                    "dictionary. These can also be accessed using strongly typed " +
                    "properties declared with `<#@ parameter name=\"<name>\" type=\"<type>\" #> " +
                    "directives.",
                    (k, v) => properties[k] = v
                },
                {
                    "debug",
                    "Generate debug symbols and keep temp files",
                    s => debug = true
                },
                {
                    "v|verbose",
                    "Generate debug symbols and keep temp files",
                    s => verbose = true
                },
                {
                    "h|?|help",
                    "Show help",
                    s => ShowHelp(false)
                }
            };

            compatOptionSet = new OptionSet {
                {
                    "dp=",
                    "Directive processor (name!class!assembly)",
                    s => directives.Add(s)
                },
                {
                    "a=",
                    "Parameters (name=value) or ([processorName!][directiveName!]name!value)",
                    s => parameters.Add(s)
                },
            };

            var remainingArgs = optionSet.Parse(args);

            remainingArgs = compatOptionSet.Parse(remainingArgs);

            string inputContent     = null;
            bool   inputIsFromStdin = false;

            if (remainingArgs.Count != 1)
            {
                if (Console.IsInputRedirected)
                {
                    inputContent     = Console.In.ReadToEnd();
                    inputIsFromStdin = true;
                }
                else
                {
                    Console.Error.WriteLine("No input file specified.");
                    return(1);
                }
            }
            else
            {
                inputFile = remainingArgs [0];
                if (!File.Exists(inputFile))
                {
                    Console.Error.WriteLine("Input file '{0}' does not exist.", inputFile);
                    return(1);
                }
            }

            bool writeToStdout = outputFile == "-" || (inputIsFromStdin && string.IsNullOrEmpty(outputFile));

            if (!writeToStdout && string.IsNullOrEmpty(outputFile))
            {
                outputFile = inputFile;
                if (Path.HasExtension(outputFile))
                {
                    var dir = Path.GetDirectoryName(outputFile);
                    var fn  = Path.GetFileNameWithoutExtension(outputFile);
                    outputFile = Path.Combine(dir, fn + ".txt");
                }
                else
                {
                    outputFile = outputFile + ".txt";
                }
            }

            if (inputFile != null)
            {
                try {
                    inputContent = File.ReadAllText(inputFile);
                }
                catch (IOException ex) {
                    Console.Error.WriteLine("Could not read input file '" + inputFile + "':\n" + ex);
                    return(1);
                }
            }

            if (inputContent.Length == 0)
            {
                Console.Error.WriteLine("Input is empty");
                return(1);
            }

            foreach (var par in parameters)
            {
                if (!generator.TryAddParameter(par))
                {
                    Console.Error.WriteLine("Parameter has incorrect format: {0}", par);
                    return(1);
                }
            }

            if (!AddDirectiveProcessors(generator, directives))
            {
                return(1);
            }

            var pt = ParsedTemplate.FromText(inputContent, generator);

            TemplateSettings settings = TemplatingEngine.GetSettings(generator, pt);

            if (debug)
            {
                settings.Debug = true;
            }
            if (verbose)
            {
                settings.Log = Console.Out;
            }

            if (pt.Errors.Count > 0)
            {
                generator.Errors.AddRange(pt.Errors);
            }

            string outputContent = null;

            if (!generator.Errors.HasErrors)
            {
                AddCoercedSessionParameters(generator, pt, properties);
            }

            if (!generator.Errors.HasErrors)
            {
                if (preprocessClassName == null)
                {
                    outputContent = generator.ProcessTemplate(pt, inputFile, inputContent, ref outputFile, settings);
                }
                else
                {
                    outputContent = generator.PreprocessTemplate(pt, inputFile, inputContent, preprocessClassName, settings);
                }
            }

            if (generator.Errors.HasErrors)
            {
                Console.Error.WriteLine(inputFile == null ? "Processing failed." : $"Processing '{inputFile}' failed.");
            }

            try {
                if (!generator.Errors.HasErrors)
                {
                    if (writeToStdout)
                    {
                        Console.WriteLine(outputContent);
                    }
                    else
                    {
                        File.WriteAllText(outputFile, outputContent, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
                    }
                }
            }
            catch (IOException ex) {
                Console.Error.WriteLine("Could not write output file '" + outputFile + "':\n" + ex);
                return(1);
            }

            LogErrors(generator);

            return(generator.Errors.HasErrors ? 1 : 0);
        }