Skip to content

Instantly share code, notes, and snippets.

@maxmcguire
Last active July 13, 2016 08:33
Show Gist options
  • Save maxmcguire/911e445324ffb97a61f8 to your computer and use it in GitHub Desktop.
Save maxmcguire/911e445324ffb97a61f8 to your computer and use it in GitHub Desktop.
// This is a small Unity utility that will add a new menu item which can be used to force
// shaders to be rebuilt when an included file is updated (Unity will only automatically do
// this if the #including file is in the same directory).
// To use it, simply select the Assets/Shaders/Rebuild Shaders menu option. Any shader file
// which #includes a file that has a newer timestamp will be trivially modified to force it
// to be rebuilt.
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections;
using System.Text.RegularExpressions;
using System.Collections.Generic;
public class RebuildShaders
{
[MenuItem("Assets/Shaders/Rebuild Shaders")]
private static void RebuildShadersAction()
{
var projectFolder = System.IO.Directory.GetCurrentDirectory();
var shaderGuids = AssetDatabase.FindAssets("t:shader");
// Build a list of dependenices starting from the shaders.
Hashtable lastChangeTimes = new Hashtable();
foreach (var guid in shaderGuids)
{
string fileName = projectFolder + "/" + AssetDatabase.GUIDToAssetPath(guid);
ExtractDependencies(projectFolder, fileName, lastChangeTimes);
}
// Check if a shader is older than any of its dependencies.
foreach (var guid in shaderGuids)
{
string fileName = projectFolder + "/" + AssetDatabase.GUIDToAssetPath(guid);
long lastChangeTime = File.GetLastWriteTime(fileName).ToFileTime();
if (lastChangeTime < (long)lastChangeTimes[fileName])
{
Debug.Log("Need to rebuild" + fileName);
ForceRebuild(fileName);
}
}
AssetDatabase.Refresh();
}
private static void ForceRebuild(string fileName)
{
// Updating the file modification time doesn't seem to be enough to get Unity to rebuild it,
// so we need to actually modify the file.
using (FileStream file = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite))
{
bool hasNewLineAtEnd = false;
long length = file.Length;
if (length > 0)
{
file.Seek(-1, SeekOrigin.End);
int c = file.ReadByte();
hasNewLineAtEnd = (c == '\n');
}
if (hasNewLineAtEnd)
{
// Remove the new line at the end.
file.SetLength(length - 1);
}
else
{
// Add a new line at the end.
file.Seek(0, SeekOrigin.End);
file.WriteByte((byte)'\n');
}
}
}
private static long ExtractDependencies(string projectFolder, string fileName, Hashtable lastChangeTimes)
{
if (!File.Exists(fileName))
{
return 0;
}
if (lastChangeTimes.Contains(fileName))
{
// We've already parsed the dependenices for this file.
return (long)lastChangeTimes[fileName];
}
long lastChangeTime = File.GetLastWriteTime(fileName).ToFileTime();
// Store right away, just in case we have circular dependencies. This will prevent an infinite loop.
lastChangeTimes[fileName] = lastChangeTime;
string fileFolder = Path.GetDirectoryName(fileName);
using (StreamReader reader = File.OpenText(fileName))
{
string line;
while ((line = reader.ReadLine()) != null)
{
// This doesn't properly handly #include being inside a string or inside a comment, but it won't have
// any negative effects.
Match match = Regex.Match(line, "#include\\s\"(.+)\"");
if (match.Success)
{
string includeFileName = match.Groups[1].Value;
// The include file name could be relative to the current file's path, or it could be "absolute" in
// the project, so try both possibilities.
string absoluteFileName = Path.Combine(projectFolder, includeFileName);
string relativeFileName = Path.Combine(fileFolder, includeFileName);
long dependencyLastChangeTime = ExtractDependencies(projectFolder, absoluteFileName, lastChangeTimes);
if (dependencyLastChangeTime > lastChangeTime)
{
lastChangeTime = dependencyLastChangeTime;
}
dependencyLastChangeTime = ExtractDependencies(projectFolder, relativeFileName, lastChangeTimes);
if (dependencyLastChangeTime > lastChangeTime)
{
lastChangeTime = dependencyLastChangeTime;
}
}
}
}
lastChangeTimes[fileName] = lastChangeTime;
return lastChangeTime;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment