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 => {