Saving dynamically created code.

A word of warning. This is loosely Unity related.

I’m trying to create an editor system where I can create code from a string and save it to .cs file. (It sounds strange, but I have this idea…) I have an editor script, and it doesn’t log any errors right now, but when I run it, I don’t have any code. I tried several things.

  1. Using a binary formatter to serialize my CompilerResults into binary then putting that into a FileStream. This created a .cs file in the right spot, but it was empty.

  2. Here’s what I’m currently doing. I’m open to any changes.

using UnityEngine;
using System.CodeDom.Compiler;
using System.IO;
using UnityEditor;
using Microsoft.CSharp;

public class TestWriteFiles : ScriptableWizard { 
    private int i;    
    public string[] st = new string[1];
    
    [MenuItem ("GameObject/Write File")]
    static void CreateScript () 
	{
		
		string code = ""; 
		code += "using UnityEngine;"; 
		code += "public class CSTest : MonoBehaviour {"; 
      code += "    //Use this for initialization.";
		code += "    public void Start () {";  
		code += "    }"; 
		code += "}"; 
		
		string assetPath = "Script.cs"; //What should this be?
                //Is it relative, absolute?

		if (File.Exists(assetPath))
		{
			File.Delete(assetPath); //Delete anything at that location
		}
		
		
		CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
		//I think that's right
		
		
		// Set up parameters for the compilation: 
		CompilerParameters parameters = new CompilerParameters(); 
		parameters.GenerateExecutable = false;
		parameters.GenerateInMemory = false;
		parameters.OutputAssembly = assetPath;
			
		parameters.TempFiles = new TempFileCollection(".", true);
		//This creats a default named file in the project folder that contains
		//what I want with some arbitrary numbers and letters for a name, I just don't have any more control than that over it.
		//right folder.
		
		CompilerResults cR = provider.CompileAssemblyFromSource(parameters, code);
		Debug.Log("Compiled");
	}

}

Any help is greatly appreciated. :smile:

From what I recall, you can not compile from within unity. thats what you see there likely, the formatter works, yet it gets no data to write.

you can use eval to do it on the fly but if you want to it to be used in the project otherwise, just use File to create the cs in the assets folder and let unity compile it (will happen automatically)

Thanks :smile:

For anyone who searches this tread at a later date. I used Dreamora’s answer and changed the formatting. I cannot say this is the best way to do it, just that it worked without any errors and generated the proper code at the proper directory.

What I found that worked was a CodeDomProvider and a CodeCompileUnit. I used CodeDomProvider.GenerateCodeFromCompileUnit(CodeCompileUnit, TextWriter, CodeCompilerOptions); The TextWriter wrote the code to a cs file that Unity compiled. So my code looked something like:

using UnityEngine;
using System.//lots of them


public class TestWriteFiles : ScriptableWizard { 
    
     [MenuItem ("GameObject/Write File")]
     static void CreateScript () 
     {
			
			CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
			CodeGeneratorOptions options = new CodeGeneratorOptions();
			
			// Create a new CodeCompileUnit to contain the program graph
       CodeCompileUnit compileUnit = new CodeCompileUnit();

            //Added to the CodeCompileUnit.

    		string fileName = EditorUtility.SaveFilePanel("Save Generated Script", "", "New Script", "cs");
    		if (fileName.Length > 0) {
				IndentedTextWriter tw = new IndentedTextWriter(new StreamWriter(fileName, false), "    ");
				
				provider.GenerateCodeFromCompileUnit(compileUnit, tw, options);

				tw.Close();
    		}
	}

}

Does this enable a way to for example download scripts and execute them on the fly during game play?

www → www.text → eval

but there are restrictions and overheads to this. for example you can not setup prefabs

After more experimenting:

File.WriteAllText is easier than using CodeDoms in my opinion, so that is what I would recommend to anyone looking at this thread.

(I forgot about this thread then it popped up again.)