Skip to content

Commit

Permalink
Wildcard arguments for dotless.Compiler
Browse files Browse the repository at this point in the history
Added support for wildcard arguments and ability to compile & watch multiple files. Imports doesn't work properly because Parser clears imports (bug? fix in the next commit).
  • Loading branch information
gthx authored and Tigraine committed Aug 29, 2010
1 parent 8448dd7 commit e8a1104
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 94 deletions.
102 changes: 65 additions & 37 deletions src/dotless.Compiler/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ namespace dotless.Compiler
using System.Reflection;
using Core;
using Core.configuration;
using System.Linq;
using Core.Input;
using Core.Parameters;

public class Program
Expand All @@ -29,62 +27,82 @@ public static void Main(string[] args)
return;
}

var inputFile = new FileInfo(arguments[0]);
var inputDirectoryPath = Path.GetDirectoryName(arguments[0]);
var inputFilePattern = Path.GetFileName(arguments[0]);
var outputDirectoryPath = string.Empty;
var outputFilename = string.Empty;

if (!inputFile.Exists && inputFile.Extension != ".less" && !inputFile.FullName.EndsWith(".less.css"))
inputFile = new FileInfo(inputFile.FullName + ".less");
if (string.IsNullOrEmpty(inputFilePattern)) inputFilePattern = "*.less";
if (!Path.HasExtension(inputFilePattern)) inputFilePattern = Path.ChangeExtension(inputFilePattern, "less");

string outputFilePath;
if (arguments.Count > 1)
{
outputFilePath = arguments[1] + (Path.HasExtension(arguments[1]) ? "" : ".css");
outputFilePath = Path.GetFullPath(outputFilePath);
outputDirectoryPath = Path.GetDirectoryName(arguments[1]);
outputFilename = Path.GetFileName(arguments[1]);
outputFilename = Path.ChangeExtension(outputFilename, "css");
}
else if (inputFile.FullName.EndsWith(".less.css"))
outputFilePath = inputFile.Name.Substring(0, inputFile.Name.Length - 9) + ".css";
else
outputFilePath = Path.ChangeExtension(inputFile.Name, ".css");

var currentDir = Directory.GetCurrentDirectory();
if (inputFile.Directory != null)
Directory.SetCurrentDirectory(inputFile.Directory.FullName);
else outputDirectoryPath = inputDirectoryPath;
if (HasWildcards(inputFilePattern)) outputFilename = string.Empty;

var filenames = Directory.GetFiles(inputDirectoryPath, inputFilePattern);
var engine = new EngineFactory(configuration).GetEngine();
Func<IEnumerable<string>> compilationDelegate = () => Compile(engine, inputFile.Name, outputFilePath);

var files = compilationDelegate();

if (configuration.Watch)
using (var watcher = new Watcher() { Watch = configuration.Watch })
{
WriteAbortInstructions();

var watcher = new Watcher(files, compilationDelegate);

while (Console.ReadLine() != "")
if (watcher.Watch && HasWildcards(inputFilePattern))
{
WriteAbortInstructions();
CompilationFactoryDelegate factoryDelegate = (input) => CreationImpl(engine, input, Path.GetFullPath(outputDirectoryPath));
watcher.SetupDirectoryWatcher(Path.GetFullPath(inputDirectoryPath), inputFilePattern, factoryDelegate);
}

watcher.RemoveWatchers();
foreach (var filename in filenames)
{
var inputFile = new FileInfo(filename);
var pathbuilder = new System.Text.StringBuilder(outputDirectoryPath + "\\");
if (string.IsNullOrEmpty(outputFilename)) pathbuilder.Append(Path.ChangeExtension(inputFile.Name, "css"));
else pathbuilder.Append(outputFilename);
var outputFilePath = Path.GetFullPath(pathbuilder.ToString());

CompilationDelegate compilationDelegate = () => CompileImpl(engine, inputFile.FullName, outputFilePath);
Console.WriteLine("[Compile]");
var files = compilationDelegate();
if (watcher.Watch) watcher.SetupWatchers(files, compilationDelegate);
}
if (configuration.Watch) WriteAbortInstructions();
while (watcher.Watch && Console.ReadKey(true).Key != ConsoleKey.Enter)
{
System.Threading.Thread.Sleep(200);
}
}

Directory.SetCurrentDirectory(currentDir);
}
private static CompilationDelegate CreationImpl(ILessEngine engine, string inputFilePath, string outputDirectoryPath)
{
var pathbuilder = new System.Text.StringBuilder(outputDirectoryPath + Path.DirectorySeparatorChar);
pathbuilder.Append(Path.ChangeExtension(Path.GetFileName(inputFilePath), "css"));
var outputFilePath = Path.GetFullPath(pathbuilder.ToString());
return () => CompileImpl(engine, inputFilePath, outputFilePath);
}

private static IEnumerable<string> Compile(ILessEngine engine, string inputFilePath, string outputFilePath)
private static IEnumerable<string> CompileImpl(ILessEngine engine, string inputFilePath, string outputFilePath)
{
Console.Write("Compiling {0} -> {1} ", inputFilePath, outputFilePath);
var currentDir = Directory.GetCurrentDirectory();
try
{
var source = new FileReader().GetFileContents(inputFilePath);
Console.WriteLine("{0} -> {1}", inputFilePath, outputFilePath);
var directoryPath = Path.GetDirectoryName(inputFilePath);
var source = new dotless.Core.Input.FileReader().GetFileContents(inputFilePath);
Directory.SetCurrentDirectory(directoryPath);
var css = engine.TransformToCss(source, inputFilePath);

File.WriteAllText(outputFilePath, css);
Console.WriteLine("[Done]");

return new[] { inputFilePath }.Concat(engine.GetImports());

var files = new List<string>();
files.Add(inputFilePath);
foreach (var file in engine.GetImports())
files.Add(Path.Combine(directoryPath, Path.ChangeExtension(file, "less")));
return files;
}
catch(IOException)
catch (IOException)
{
throw;
}
Expand All @@ -95,6 +113,16 @@ private static IEnumerable<string> Compile(ILessEngine engine, string inputFileP
Console.WriteLine(ex.StackTrace);
return null;
}
finally
{
Directory.SetCurrentDirectory(currentDir);
}
}


private static bool HasWildcards(string inputFilePattern)
{
return System.Text.RegularExpressions.Regex.Match(inputFilePattern, @"[\*\?]").Success;
}

private static void WriteAbortInstructions()
Expand All @@ -105,7 +133,7 @@ private static void WriteAbortInstructions()
private static string GetAssemblyVersion()
{
Assembly assembly = typeof(EngineFactory).Assembly;
var attributes = assembly.GetCustomAttributes(typeof (AssemblyFileVersionAttribute), true) as
var attributes = assembly.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), true) as
AssemblyFileVersionAttribute[];
if (attributes != null && attributes.Length == 1)
return attributes[0].Version;
Expand Down
160 changes: 103 additions & 57 deletions src/dotless.Compiler/Watcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,89 +3,117 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;

public class Watcher
public delegate CompilationDelegate CompilationFactoryDelegate(string input);
public delegate IEnumerable<string> CompilationDelegate();

class Watcher : IDisposable
{
private Func<IEnumerable<string>> CompilationDelegate { get; set; }
private Dictionary<string, FileSystemWatcher> FileSystemWatchers { get; set; }
private static Dictionary<string, CompilationDelegate> CompilationDelegates = new Dictionary<string, CompilationDelegate>();
private static Dictionary<string, CompilationFactoryDelegate> CreationDelegates = new Dictionary<string, CompilationFactoryDelegate>();
private static List<FileSystemWatcher> FileSystemWatchers = new List<FileSystemWatcher>();
public bool Watch { get; set; }

public Watcher(IEnumerable<string> files, Func<IEnumerable<string>> compilationDelegate)
{
CompilationDelegate = compilationDelegate;
FileSystemWatchers = new Dictionary<string, FileSystemWatcher>();
private object _eventLock = new object();

SetupWatchers(files);
public void SetupDirectoryWatcher(string directoryPath, string pattern, CompilationFactoryDelegate del)
{
var fsWatcher = new FileSystemWatcher(directoryPath, pattern);
fsWatcher.Created += FileCreatedHandler;
fsWatcher.EnableRaisingEvents = true;
Console.WriteLine("Started watching '{0}' for changes", directoryPath + "\\" + pattern);
CreationDelegates.Add(directoryPath, del);
FileSystemWatchers.Add(fsWatcher);
}

public void SetupWatchers(IEnumerable<string> files)
public void SetupWatchers(IEnumerable<string> files, CompilationDelegate del)
{
if (files == null)
return;

foreach (var file in files)
foreach (var path in files)
{
var fileInfo = new FileInfo(file);
if (fileInfo.Directory == null)
throw new IOException("File Path has no directory to watch");

if(FileSystemWatchers.ContainsKey(file))
continue;

Console.WriteLine("Started watching '{0}' for changes", file);

var directoryFullName = fileInfo.Directory.FullName;
var fsWatcher = new FileSystemWatcher(directoryFullName, fileInfo.Name);
fsWatcher.Changed += FsWatcherChanged;
fsWatcher.EnableRaisingEvents = true;

FileSystemWatchers[file] = fsWatcher;
if (!CompilationDelegates.ContainsKey(path))
{
var fsWatcher = new FileSystemWatcher(Path.GetDirectoryName(path), Path.GetFileName(path));
fsWatcher.Changed += FileChangedHandler;
fsWatcher.Deleted += FileDeletedHandler;
fsWatcher.EnableRaisingEvents = true;
Console.WriteLine("Started watching '{0}' for changes", path);
CompilationDelegates.Add(path, del);
FileSystemWatchers.Add(fsWatcher);
}
else
{
lock (_eventLock)
{
bool add = true;
foreach (var d in CompilationDelegates[path].GetInvocationList())
{
if (d.Target.Equals(del.Target)) add = false;
}
if (add) CompilationDelegates[path] += del;
}
}
}
}

var missing = FileSystemWatchers.Keys.Where(f => !files.Contains(f)).ToList();

foreach (var file in missing)
void FileCreatedHandler(object sender, FileSystemEventArgs e)
{
var directoryPath = Path.GetDirectoryName(e.FullPath);
if (CreationDelegates.ContainsKey(directoryPath))
{
var fsWatcher = FileSystemWatchers[file];

fsWatcher.Changed -= FsWatcherChanged;

fsWatcher.Dispose();

FileSystemWatchers.Remove(file);

Console.WriteLine("Stopped watching '{0}'", file);
var compilationDelegate = CreationDelegates[directoryPath](e.FullPath);
Console.WriteLine("[Compile]");
var files = compilationDelegate();
SetupWatchers(files, compilationDelegate);
}
}

public void RemoveWatchers()
void FileDeletedHandler(object sender, FileSystemEventArgs e)
{
SetupWatchers(new string[0]);
var fsWatcher = sender as FileSystemWatcher;
fsWatcher.EnableRaisingEvents = false;
Console.WriteLine("Stopped watching '{0}'", e.FullPath);
FileSystemWatchers.Remove(fsWatcher);
fsWatcher.Changed -= null;
fsWatcher.Dispose();

var path = e.FullPath;
if (CompilationDelegates.ContainsKey(path))
{
lock (_eventLock)
{
var del = CompilationDelegates[path];
CompilationDelegates.Remove(path);
List<string> toberemoved = new List<string>();
foreach (var key in CompilationDelegates.Keys)
foreach (var d in CompilationDelegates[key].GetInvocationList())
if (d.Target.Equals(del.Target))
toberemoved.Add(key);
foreach (var key in toberemoved)
CompilationDelegates[key] -= del;
}
}
}

void FsWatcherChanged(object sender, FileSystemEventArgs e)
void FileChangedHandler(object sender, FileSystemEventArgs e)
{
if (IsDuplicateEvent()) return;

var fsWatcher = (FileSystemWatcher) sender;
var file = FileSystemWatchers.First(d => d.Value == fsWatcher).Key;

var compilationDelegate = CompilationDelegates[e.FullPath];
var completed = false;
Console.WriteLine("Found change in '{0}'. Recompiling...", file);
while(!completed)
Console.WriteLine("[Change in {0}]", e.Name);
Console.WriteLine("[Recompile]");
while (!completed)
{
try
{
var files = CompilationDelegate();
SetupWatchers(files);
var files = compilationDelegate();
SetupWatchers(files, compilationDelegate);
completed = true;
}
catch(IOException)
catch (IOException)
{
Thread.Sleep(100);
Console.WriteLine("[Waiting]");
Console.WriteLine("File still locked, waiting 100ms");
Console.WriteLine("[Waiting(File locked)]");
Thread.Sleep(300);
}
}
}
Expand All @@ -104,5 +132,23 @@ private bool IsDuplicateEvent()
lastEventOccured = fileTimeUtc;
return false;
}

#region IDisposable Members

void IDisposable.Dispose()
{
foreach (var fsWatcher in FileSystemWatchers)
{
fsWatcher.EnableRaisingEvents = false;
fsWatcher.Changed -= null;
fsWatcher.Dispose();
Console.WriteLine("Stopped watching '{0}'", fsWatcher.Filter);
}
FileSystemWatchers.Clear();
CompilationDelegates.Clear();
CreationDelegates.Clear();
}

#endregion
}
}
}

0 comments on commit e8a1104

Please sign in to comment.