From 1964e884e19be1e7ab1eabbf7f47fb8bd09ccad4 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sat, 1 Feb 2014 11:55:10 -0800 Subject: [PATCH] Initial version of watch target. - Added FileWatcher helper class - Added ability to disable fetching k from nuget every time it's called - Added watch target to standard goals - Added ability to skip project generation for net45. This is useful for watch so that changes made to the project but not saved to disk won't be discarded when the watcher runs. --- build/FileWatcher.shade | 122 +++++++++++++++++++++++++++++++ build/_k-generate-projects.shade | 24 +++++- build/_k-standard-goals.shade | 47 ++++++++++++ build/_k.shade | 7 +- 4 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 build/FileWatcher.shade diff --git a/build/FileWatcher.shade b/build/FileWatcher.shade new file mode 100644 index 0000000000..f2d93fa010 --- /dev/null +++ b/build/FileWatcher.shade @@ -0,0 +1,122 @@ +use namespace="System.Collections.Concurrent" + +functions + @{ + public class FileWatcher + { + private readonly HashSet _files = new HashSet(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary> _directories = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); + + private readonly FileSystemWatcher _watcher; + + public FileWatcher(string path) + { + _watcher = new FileSystemWatcher(path); + _watcher.IncludeSubdirectories = true; + _watcher.EnableRaisingEvents = true; + + _watcher.Changed += OnWatcherChanged; + _watcher.Renamed += OnRenamed; + _watcher.Deleted += OnWatcherChanged; + _watcher.Created += OnWatcherChanged; + } + + public event Action OnChanged; + + public void WatchDirectory(string path, string extension) + { + var extensions = _directories.GetOrAdd(path, _ => new HashSet(StringComparer.OrdinalIgnoreCase)); + + extensions.Add(extension); + } + + public bool WatchFile(string path) + { + return _files.Add(path); + } + public void Dispose() + { + _watcher.Dispose(); + } + + public bool ReportChange(string newPath, WatcherChangeTypes changeType) + { + return ReportChange(oldPath: null, newPath: newPath, changeType: changeType); + } + + public bool ReportChange(string oldPath, string newPath, WatcherChangeTypes changeType) + { + if (HasChanged(oldPath, newPath, changeType)) + { + if (oldPath != null) + { + Trace.TraceInformation("{0} -> {1}", oldPath, newPath); + } + else + { + Trace.TraceInformation("{0} -> {1}", changeType, newPath); + } + + if (OnChanged != null) + { + OnChanged(newPath); + } + + return true; + } + + return false; + } + + private void OnRenamed(object sender, RenamedEventArgs e) + { + ReportChange(e.OldFullPath, e.FullPath, e.ChangeType); + } + + private void OnWatcherChanged(object sender, FileSystemEventArgs e) + { + ReportChange(e.FullPath, e.ChangeType); + } + + private bool HasChanged(string oldPath, string newPath, WatcherChangeTypes changeType) + { + // File changes + if (_files.Contains(newPath) || + (oldPath != null && _files.Contains(oldPath))) + { + return true; + } + + HashSet extensions; + if (_directories.TryGetValue(newPath, out extensions) || + _directories.TryGetValue(Path.GetDirectoryName(newPath), out extensions)) + { + string extension = Path.GetExtension(newPath); + + if (String.IsNullOrEmpty(extension)) + { + // Assume it's a directory + if (changeType == WatcherChangeTypes.Created || + changeType == WatcherChangeTypes.Renamed) + { + foreach (var e in extensions) + { + WatchDirectory(newPath, e); + } + } + else if(changeType == WatcherChangeTypes.Deleted) + { + return true; + } + + // Ignore anything else + return false; + } + + return extensions.Contains(extension); + } + + return false; + } + } + } \ No newline at end of file diff --git a/build/_k-generate-projects.shade b/build/_k-generate-projects.shade index 8a50d94abb..7dfa97f9d7 100644 --- a/build/_k-generate-projects.shade +++ b/build/_k-generate-projects.shade @@ -17,11 +17,16 @@ k-generate-projects solutionPath='' Required. Path to the solution directory +skipNet45='false' + Optional. Skip net45 project regeneration + */} content var='net45' include href='net45.txt' content var='k10' include href='k10.txt' +default skipNet45='${false}' + @{ ProjectGenerator.Logger = Log; @@ -30,6 +35,11 @@ content var='k10' include href='k10.txt' { "k10", k10 } }; + if(skipNet45) + { + templates.Remove("net45"); + } + ProjectGenerator.MakeProjects(solutionPath, templates); } @@ -245,9 +255,17 @@ functions foreach (var pair in configs) { + var targetFramework = pair.Key; + + string projectTemplate; + if(!templates.TryGetValue(targetFramework, out projectTemplate)) + { + Warn("Skipping project generation for " + projectName + " - " + targetFramework); + continue; + } + var allPackageReferences = new Dictionary(packageReferences); - var targetFramework = pair.Key; var props = (IDictionary)pair.Value; var specificCompilationOptions = GetObject(props, "compilationOptions") ?? compilationOptions ?? new Dictionary(); @@ -294,7 +312,7 @@ functions } } - var template = templates[targetFramework] + var template = projectTemplate .Replace("{ProjectGuid}", id) .Replace("{Name}", projectName) .Replace("{Defines}", String.Join(";", defines)) @@ -359,7 +377,7 @@ functions "Microsoft.Net.ApplicationHost.dll"); var klrSwitches = configType == "k10" ? "--core45" : "--net45"; - var args = klrSwitches + " " + appHostPath + " " + projectRoot + " --nobin"; + var args = klrSwitches + " " + appHostPath + " --nobin " + projectRoot; return String.Format(@"Program {0} diff --git a/build/_k-standard-goals.shade b/build/_k-standard-goals.shade index af57415b1a..399d0535bd 100644 --- a/build/_k-standard-goals.shade +++ b/build/_k-standard-goals.shade @@ -2,6 +2,7 @@ use namespace="System" use namespace="System.IO" use import="Files" use import="BuildEnv" +use import="FileWatcher" default BASE_DIR='${Directory.GetCurrentDirectory()}' default TARGET_DIR='${Path.Combine(BASE_DIR, "artifacts")}' @@ -34,7 +35,53 @@ default TEST_DIR='${Path.Combine(TARGET_DIR, "test")}' #nuget-install target='install' description='Copy NuGet packages to local repo' nuget-local-publish sourcePackagesDir='${BUILD_DIR}' + +#watch + @{ + var watcher = new FileWatcher(BASE_DIR); + + foreach (var file in Directory.EnumerateFiles(BASE_DIR, "*.json", SearchOption.AllDirectories)) + { + watcher.WatchFile(file); + } + + foreach (var file in Directory.EnumerateFiles(BASE_DIR, "*.cs", SearchOption.AllDirectories)) + { + watcher.WatchFile(file); + } + + foreach (var dir in Directory.EnumerateDirectories(BASE_DIR, "*.*", SearchOption.AllDirectories)) + { + watcher.WatchDirectory(dir, ".cs"); + watcher.WatchDirectory(dir, ".json"); + } + + watcher.OnChanged += path => + { + Log.Info("Change detected in " + path); + bool restore = path.EndsWith("project.json"); + + try + { + UpdateProjects(BASE_DIR, restore); + } + catch (Exception ex) + { + Log.Warn(ex.Message); + } + + Log.Info("Waiting for changes..."); + }; + + Log.Info("Waiting for changes..."); + Console.ReadLine(); + } + functions @{ string E(string key) { return Environment.GetEnvironmentVariable(key); } void E(string key, string value) { Environment.SetEnvironmentVariable(key, value); } } + +macro name='UpdateProjects' basePath='string' restore='bool' + k command='restore' prefetch='${false}' if='restore' + k-generate-projects solutionPath='${basePath}' skipNet45='${!restore}' \ No newline at end of file diff --git a/build/_k.shade b/build/_k.shade index 292f0bc9a1..94e0c75f30 100644 --- a/build/_k.shade +++ b/build/_k.shade @@ -11,9 +11,14 @@ kProgram='...' command='' +prefetch='true' + May be passed if fetching k from nuget is required before running + */} -nuget-install package='ProjectK' outputDir='packages' extra='-pre' once='ProjectK-NuGet' +default prefetch='${true}' + +nuget-install package='ProjectK' outputDir='packages' extra='-pre' once='ProjectK-NuGet' if='prefetch' @{ Func getVersion = version => {