Skip to content
This repository has been archived by the owner on Jan 22, 2022. It is now read-only.

Added pre and post compiler callbacks for compiling UdonSharpProgramAsset #64

Merged
merged 1 commit into from
Nov 29, 2020
Merged

Conversation

NGenesis
Copy link

@NGenesis NGenesis commented Nov 28, 2020

This pull request adds support for beforeProgramAssetCompile and afterProgramAssetCompile callbacks to UdonSharpCompiler which allows for additional pre/post pipelining steps such as method stub generation, source file overrides, cache updates, etc when an UdonSharpProgramAsset is to be compiled by UdonSharp.

An example use case:

using System;
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

public class MyUdonSharpBehaviour : UdonSharpBehaviour {
	public void OnCustomEvent(string eventName) {
		Debug.Log(eventName);
	}

	// Custom event stubs in this section are automatically generated before compile time and will be overwritten.
	/*<#UDONSHARP_CUSTOMEVENTS#>*/
	// These methods would be listed in the source file after compilation has completed.  Alternative implementations could temporarily override the original source file during compilation.
	/*public void MyFirstCustomEvent() {
		OnCustomEvent("MyFirstCustomEvent");
	}
	public void MySecondCustomEvent() {
		OnCustomEvent("MySecondCustomEvent");
	}
	public void MyThirdCustomEvent() {
		OnCustomEvent("MyThirdCustomEvent");
	}*/
	/*<#!UDONSHARP_CUSTOMEVENTS!#>*/
}

#if !COMPILER_UDONSHARP && UNITY_EDITOR
using UdonSharpEditor;
using UnityEditor;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Linq;

[CustomEditor(typeof(MyUdonSharpBehaviour))]
public class MyUdonSharpBehaviourInspectorEditor : Editor {
	public override void OnInspectorGUI() {
		UdonSharpCustomEventUtil.CustomEventNames.Add("MyFirstCustomEvent");
		UdonSharpCustomEventUtil.CustomEventNames.Add("MySecondCustomEvent");
		UdonSharpCustomEventUtil.CustomEventNames.Add("MyThirdCustomEvent");
	}
}

[InitializeOnLoad]
public static class UdonSharpCustomEventUtil {
	public static readonly string CustomEventStubMarker = "/*<#UDONSHARP_CUSTOMEVENTS#>*/";
	public static readonly string CustomEventStubMarkerEnd = "/*<#!UDONSHARP_CUSTOMEVENTS!#>*/";
	public static readonly string CustomEventStubMethodName = "OnCustomEvent";

	private static HashSet<string> CustomEventNames = new HashSet<string>();

	static UdonSharpCustomEventUtil() {
		UdonSharpCompiler.beforeProgramAssetCompile += OnUdonSharpCompile;
	}

	private static void OnUdonSharpCompile(UdonSharpProgramAsset programAsset) {
		var program = UdonSharpEditorUtility.GetUdonSharpProgramAsset(typeof(MyUdonSharpBehaviour));
		if(programAsset != null && programAsset.sourceCsScript != null && programAsset == program) BuildCustomEvents();
	}

	private static void BuildCustomEvents() {
		var orderedEventNames = CustomEventNames.OrderBy(x => x).ToList();

		// Generate stub code
		var customEventStubs = CustomEventStubMarker;
		foreach(var eventName in orderedEventNames) customEventStubs += BuildCustomEventMethodStub(eventName, CustomEventStubMethodName);
		customEventStubs += "\n\t" + CustomEventStubMarkerEnd;

		var program = UdonSharpEditorUtility.GetUdonSharpProgramAsset(typeof(MyUdonSharpBehaviour));
		var sourceFileName = AssetDatabase.GetAssetPath(program.sourceCsScript);
		var sourceCode = File.ReadAllText(sourceFileName);

		var expression = String.Format("{0}(.*){1}", Regex.Escape(CustomEventStubMarker), Regex.Escape(CustomEventStubMarkerEnd));
		var newSourceCode = Regex.Replace(sourceCode, expression, customEventStubs, RegexOptions.Singleline);
		if(newSourceCode != sourceCode) File.WriteAllText(sourceFileName, newSourceCode);
	}

	private static string BuildCustomEventMethodStub(string eventName, string methodName) {
		return "\n\tpublic void " + eventName + "() {\n\t\t" + methodName + "(\"" + eventName + "\");\n\t}";
	}
}
#endif

@MerlinVR
Copy link
Owner

The callbacks currently aren't doing quite what they're advertised to do. The callback will run on all program assets regardless of what's actually being compiled. It'd be more accurate to use the programs in the compilation modules since that is what is actually getting compiled at a given moment. I can merge and fix this if you'd like.

@MerlinVR MerlinVR merged commit 73dee96 into MerlinVR:master Nov 29, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants