static partial void Wain(IEnumerable <string> args) { const string csx = "csx"; const string exe = "exe"; var verbose = false; var help = false; var recurse = false; var force = false; var watching = false; var incremental = false; var extraPackageList = new List <PackageReference>(); var extraImportList = new List <string>(); var cscPath = (string)null; var target = (string)null; var targetFramework = NuGetFramework.Parse(AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName); var options = new OptionSet { { "?|help|h", "prints out the options", _ => help = true }, { "verbose|v", "enable additional output", _ => verbose = true }, { "d|debug", "debug break", _ => Debugger.Launch() }, { "r|recurse", "include sub-directories", _ => recurse = true }, { "f|force", "force continue on errors", _ => force = true }, { "w|watch", "watch for changes and re-compile outdated", _ => watching = true }, { "i|incremental", "compile outdated scripts only", _ => incremental = true }, { "ref|reference=", "extra NuGet reference", v => { if (!string.IsNullOrEmpty(v)) { extraPackageList.Add(ParseExtraPackageReference(v)); } } }, { "imp|import=", "extra import", v => { extraImportList.Add(v); } }, { "csc=", "C# compiler path", v => cscPath = v }, { "t|target=", csx + " = C# script (default); " + exe + " = executable (experimental)", v => target = v }, { "fx=", $"target framework; default: {targetFramework}", v => targetFramework = NuGetFramework.Parse(v) }, }; var tail = options.Parse(args.TakeWhile(arg => arg != "--")); if (verbose) { Trace.Listeners.Add(new ConsoleTraceListener(useErrorStream: true)); } if (help || tail.Count == 0) { Help(options); return; } if (target == null) { target = !string.IsNullOrEmpty(cscPath) ? exe : csx; } var generator = csx.Equals(target, StringComparison.OrdinalIgnoreCase) ? GenerateCsx : exe.Equals(target, StringComparison.OrdinalIgnoreCase) ? GenerateExecutable(cscPath) : throw new Exception("Target is invalid or missing. Supported targets are: " + string.Join(", ", csx, exe)); extraImportList.RemoveAll(string.IsNullOrEmpty); // TODO Allow package source to be specified via args var queries = GetQueries(tail, recurse); // TODO Allow packages directory to be specified via args const string packagesDirName = "packages"; var compiler = Compiler(NuGetClient.CreateDefaultFactory(), packagesDirName, extraPackageList, extraImportList, targetFramework, generator, watching || incremental, force, verbose); if (watching) { if (tail.Count > 1) { // TODO Support multiple watch roots throw new NotSupportedException( "Watch mode does not support multiple file specifications. " + "Use a single wildcard specification instead instead to watch and re-compile several queries."); } var tokens = SplitDirFileSpec(tail.First()).Fold((dp, fs) => ( dirPath: dp ?? Environment.CurrentDirectory, fileSpec: fs )); using (var cts = new CancellationTokenSource()) { Console.CancelKeyPress += (_, e) => { // TODO Re-consider proper cancellation Console.WriteLine("Aborting..."); // ReSharper disable once AccessToDisposedClosure cts.Cancel(); e.Cancel = true; }; var changes = FileMonitor.GetFolderChanges( tokens.dirPath, tokens.fileSpec, recurse, NotifyFilters.FileName | NotifyFilters.LastWrite, WatcherChangeTypes.Created | WatcherChangeTypes.Changed | WatcherChangeTypes.Renamed, cts.Token); foreach (var e in from cs in changes.Throttle(TimeSpan.FromSeconds(2)) select cs.Length) { Console.WriteLine($"{e} change(s) detected. Re-compiling..."); var count = 0; var compiledCount = 0; // ReSharper disable once LoopCanBeConvertedToQuery // ReSharper disable once LoopCanBePartlyConvertedToQuery // ReSharper disable once PossibleMultipleEnumeration foreach (var query in queries) { // TODO Re-try on potential file locking issues var compiled = compiler(query); count++; compiledCount += compiled ? 1 : 0; } if (count > 1) { Console.WriteLine($"Re-compiled {compiledCount:N0} of {count:N0} queries."); } } } } else { foreach (var query in queries) { compiler(query); } } }