├── .gitignore ├── NuGet.Config ├── appveyor.yml ├── build ├── .vscode │ └── tasks.json ├── build.csx ├── common.csx ├── csx.nuspec ├── project.json └── tmp │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── anotherscript.csx │ ├── csx.csproj │ ├── csx.sln │ ├── csx.sln.DotSettings.user │ └── samplescript.csx ├── readme.md └── src ├── CommandRunner.cs ├── ICommandRunner.cs ├── IScriptParser.cs ├── NuGetMetadataReferenceResolver.cs ├── PackageReference.cs ├── ParseResult.cs ├── Program.cs ├── ProjectFile.cs ├── Properties └── launchSettings.json ├── ResourceLoader.cs ├── RuntimeDependencyResolver.cs ├── ScriptExecutor.cs ├── ScriptParser.cs ├── ScriptProjectProvider.cs ├── Skaffolder.cs ├── Templates ├── csproj.template ├── launch.json.template ├── omnisharp.json.template ├── script.csx.template └── tasks.json.template ├── anotherscript.csx ├── csx.csproj ├── csx.sln ├── csx.sln.DotSettings.user └── samplescript.csx /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | # External tool builders 19 | .externalToolBuilders/ 20 | 21 | # Locally stored "Eclipse launch configurations" 22 | *.launch 23 | 24 | # CDT-specific 25 | .cproject 26 | 27 | # PDT-specific 28 | .buildpath 29 | 30 | 31 | ################# 32 | ## Visual Studio 33 | ################# 34 | 35 | ## Ignore Visual Studio temporary files, build results, and 36 | ## files generated by popular Visual Studio add-ons. 37 | 38 | # User-specific files 39 | *.suo 40 | *.user 41 | *.sln.docstates 42 | .vs/ 43 | 44 | # Build results 45 | [Dd]ebug/ 46 | [Rr]elease/ 47 | *_i.c 48 | *_p.c 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.vspscc 63 | .builds 64 | *.dotCover 65 | *.cs.pp 66 | *.nupkg 67 | project.lock.json 68 | LightInject.PreProcessor.dll 69 | lib/ 70 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 71 | packages/ 72 | scriptcs_packages/ 73 | 74 | # Visual C++ cache files 75 | ipch/ 76 | *.aps 77 | *.ncb 78 | *.opensdf 79 | *.sdf 80 | 81 | # Visual Studio profiler 82 | *.psess 83 | *.vsp 84 | 85 | # ReSharper is a .NET coding add-in 86 | _ReSharper* 87 | 88 | # Installshield output folder 89 | [Ee]xpress 90 | 91 | # DocProject is a documentation generator add-in 92 | DocProject/buildhelp/ 93 | DocProject/Help/*.HxT 94 | DocProject/Help/*.HxC 95 | DocProject/Help/*.hhc 96 | DocProject/Help/*.hhk 97 | DocProject/Help/*.hhp 98 | DocProject/Help/Html2 99 | DocProject/Help/html 100 | 101 | 102 | # Click-Once directory 103 | publish 104 | 105 | # Others 106 | [Bb]in 107 | [Oo]bj 108 | sql 109 | TestResults 110 | *.Cache 111 | ClientBin 112 | stylecop.* 113 | ~$* 114 | *.dbmdl 115 | Generated_Code #added for RIA/Silverlight projects 116 | *.ncrunch* 117 | 118 | # Backup & report files from converting an old project file to a newer 119 | # Visual Studio version. Backup files are not needed, because we have git ;-) 120 | _UpgradeReport_Files/ 121 | Backup*/ 122 | UpgradeLog*.XML 123 | 124 | 125 | ############ 126 | ## Windows 127 | ############ 128 | 129 | # Windows image file caches 130 | Thumbs.db 131 | 132 | # Folder config file 133 | Desktop.ini 134 | 135 | 136 | ############# 137 | ## Python 138 | ############# 139 | 140 | *.py[co] 141 | 142 | # Packages 143 | *.egg 144 | *.egg-info 145 | dist 146 | eggs 147 | parts 148 | bin 149 | var 150 | sdist 151 | develop-eggs 152 | .installed.cfg 153 | 154 | # Installer logs 155 | pip-log.txt 156 | 157 | # Unit test / coverage reports 158 | .coverage 159 | .tox 160 | 161 | #Translations 162 | *.mo 163 | 164 | #Mr Developer 165 | .mr.developer.cfg 166 | 167 | 168 | /NuGet/Build/Net/LightInject.Annotation/LightInject.Annotation.cs 169 | /NuGet/Build/Net45/LightInject/LightInject.cs 170 | 171 | 172 | -------------------------------------------------------------------------------- /NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | install: 3 | - cmd: cinst scriptcs 4 | build_script: 5 | - cmd: >- 6 | cd build 7 | 8 | scriptcs build.csx -debug 9 | test: off 10 | artifacts: 11 | - path: build/tmp/**/*.nupkg 12 | name: NuGetPackages 13 | deploy: 14 | provider: NuGet 15 | api_key: 16 | secure: ynFcRQX0oim3DdR5Y8s4BtynS/NYRG059GvWGckqhpZGNZVvhvvn5UUWgsyPKLKm 17 | skip_symbols: false 18 | artifact: NuGetPackages 19 | on: 20 | appveyor_repo_tag: true -------------------------------------------------------------------------------- /build/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // A task runner that calls the scriptcs compiler (scriptcs) and 2 | // compiles the current opened file. 3 | { 4 | "version": "0.1.0", 5 | 6 | "command": "scriptcs", 7 | 8 | // The command is a shell script 9 | "isShellCommand": true, 10 | 11 | // Show the output window only if unrecognized errors occur. 12 | "showOutput": "silent", 13 | 14 | // args is the current file to compile 15 | "args": ["${file}", "-debug"], 16 | 17 | // use the standard tsc problem matcher to find compile problems 18 | // in the output. 19 | "problemMatcher": "$msCompile" 20 | } -------------------------------------------------------------------------------- /build/build.csx: -------------------------------------------------------------------------------- 1 |  2 | #load "common.csx" 3 | 4 | string pathToBuildDirectory = @"tmp/"; 5 | private string version = "1.0.0-beta4"; 6 | 7 | WriteLine("csx version {0}" , version); 8 | 9 | Execute(() => InitializBuildDirectories(), "Preparing build directories"); 10 | Execute(() => BuildAllFrameworks(), "Building all frameworks"); 11 | Execute(() => CreateNugetPackages(), "Creating NuGet packages"); 12 | 13 | private void CreateNugetPackages() 14 | { 15 | Choco.Pack("csx.nuspec", pathToBuildDirectory); 16 | //NuGet.CreatePackage("csx.nuspec",pathToBuildDirectory); 17 | // string myDocumentsFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); 18 | // RoboCopy(pathToBuildDirectory, myDocumentsFolder, "*.nupkg"); 19 | } 20 | 21 | private void BuildAllFrameworks() 22 | { 23 | BuildDotNet(); 24 | } 25 | 26 | private void BuildDotNet() 27 | { 28 | string pathToProjectFile = Path.Combine(pathToBuildDirectory, @"csx.csproj"); 29 | DotNet.Build(pathToProjectFile); 30 | DotNet.Publish(pathToProjectFile); 31 | } 32 | 33 | private void InitializBuildDirectories() 34 | { 35 | DirectoryUtils.Delete(pathToBuildDirectory); 36 | Execute(() => InitializeNugetBuildDirectory("NETCOREAPP11"), "NetCoreApp1.1"); 37 | } 38 | 39 | private void InitializeNugetBuildDirectory(string frameworkMoniker) 40 | { 41 | CreateDirectory(pathToBuildDirectory); 42 | RoboCopy("../src", pathToBuildDirectory, "/e /XD bin obj .vs NuGet TestResults packages /XF project.lock.json"); 43 | } 44 | 45 | -------------------------------------------------------------------------------- /build/common.csx: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Xml.Linq; 3 | using System.Text.RegularExpressions; 4 | using Microsoft.Win32; 5 | using System.Globalization; 6 | using System.Collections.ObjectModel; 7 | using System.Threading; 8 | 9 | private static int depth = 0; 10 | 11 | private static string lastWriteOperation; 12 | 13 | 14 | public static class Choco 15 | { 16 | public static void Pack(string pathToMetadata, string outputDirectory) 17 | { 18 | Command.Execute("choco.exe", $"pack {pathToMetadata} --outputdirectory {outputDirectory}", "."); 19 | } 20 | } 21 | 22 | 23 | public static class DotNet 24 | { 25 | public static void Build(string pathToProjectFile) 26 | { 27 | Command.Execute("dotnet.exe","--version", "."); 28 | Command.Execute("dotnet.exe","restore " + pathToProjectFile, "."); 29 | Command.Execute("dotnet.exe","build " + pathToProjectFile + " --configuration Release" , "."); 30 | } 31 | 32 | public static void Pack(string pathToProjectFile, string outputDirectory) 33 | { 34 | Command.Execute("dotnet.exe","pack " + pathToProjectFile + " --output " + outputDirectory , "."); 35 | } 36 | 37 | public static void Publish(string pathToProjectFile) 38 | { 39 | Command.Execute("dotnet.exe", $"publish {pathToProjectFile} -c Release -r win7-x64"); 40 | } 41 | } 42 | 43 | public static void RoboCopy(string source, string destination, string arguments = null) 44 | { 45 | if (!Directory.Exists(source)) 46 | { 47 | throw new InvalidOperationException(string.Format("The directory {0} does not exist", source)); 48 | } 49 | 50 | Command.Execute("robocopy", string.Format("{0} {1} {2}", source, destination, arguments)); 51 | } 52 | 53 | public static void RoboCopy(string source, string destination, string file, string arguments) 54 | { 55 | Command.Execute("robocopy", string.Format("{0} {1} {2} {3}", source, destination, arguments)); 56 | } 57 | 58 | 59 | private static void WriteStart(string message, params object[] arguments) 60 | { 61 | StringBuilder sb = new StringBuilder(); 62 | sb.Append("\n"); 63 | sb.Append(' ', depth); 64 | sb.Append(string.Format(message, arguments)); 65 | Console.Out.Flush(); 66 | Console.Write(sb.ToString()); 67 | Console.Out.Flush(); 68 | lastWriteOperation = "WriteStart"; 69 | } 70 | 71 | private static void WriteLine(string message, params object[] arguments) 72 | { 73 | if (message == null) 74 | { 75 | return; 76 | } 77 | StringBuilder sb = new StringBuilder(); 78 | if (lastWriteOperation == "WriteStart") 79 | { 80 | sb.Append("\n"); 81 | } 82 | sb.Append(' ', depth); 83 | sb.Append(string.Format(message, arguments)); 84 | Console.WriteLine(sb.ToString()); 85 | lastWriteOperation = "WriteLine"; 86 | } 87 | 88 | private static void WriteEnd(string message, params object[] arguments) 89 | { 90 | if (lastWriteOperation == "WriteStart") 91 | { 92 | Console.WriteLine(string.Format(message,arguments)); 93 | } 94 | else 95 | { 96 | StringBuilder sb = new StringBuilder(); 97 | sb.Append(' ', depth); 98 | sb.Append(string.Format(message, arguments)); 99 | Console.WriteLine(sb.ToString()); 100 | lastWriteOperation = "WriteLine"; 101 | } 102 | } 103 | 104 | public static void RemoveDirectory(string directory) 105 | { 106 | if (Directory.Exists(directory)) 107 | { 108 | Directory.Delete(directory, true); 109 | } 110 | } 111 | 112 | public static void CreateDirectory(string directory) 113 | { 114 | RemoveDirectory(directory); 115 | Directory.CreateDirectory(directory); 116 | } 117 | 118 | public static string ResolveDirectory(string path, string filePattern) 119 | { 120 | string pathToFile = Directory.GetFiles(path, filePattern, SearchOption.AllDirectories).Single(); 121 | return Path.GetDirectoryName(pathToFile); 122 | } 123 | 124 | public static string GetFile(string path, string filePattern) 125 | { 126 | WriteLine("Looking for {0} in {1}", filePattern, path); 127 | string[] pathsToFile = Directory.GetFiles(path, filePattern, SearchOption.AllDirectories).ToArray(); 128 | if (pathsToFile.Length > 1) 129 | { 130 | WriteLine("Found multiple files"); 131 | var files = pathsToFile.Select(p => new FileInfo(p)); 132 | var file = files.OrderBy(f => f.LastWriteTime).Last(); 133 | WriteLine("Choosing {0}",file.FullName); 134 | return file.FullName; 135 | } 136 | WriteLine("Found {0}", pathsToFile[0]); 137 | return pathsToFile[0]; 138 | } 139 | 140 | private static string CopyToNuGetBuildDirectory(string projectPath) 141 | { 142 | var projectDirectory = Path.GetDirectoryName(projectPath); 143 | var tempSolutionFolder = GetTemporaryDirectory(); 144 | string projectName = Regex.Match(projectPath, @"..\\(.+)").Groups[1].Value; 145 | var tempProjectFolder = Path.Combine(tempSolutionFolder, projectName); 146 | RoboCopy(projectPath, tempProjectFolder, "/S /XD obj bin"); 147 | NuGet.Restore(tempProjectFolder); 148 | RemoveInternalsVisibleToAttribute(Path.Combine(tempProjectFolder, @"Properties\AssemblyInfo.cs")); 149 | return tempProjectFolder; 150 | } 151 | 152 | public static string GetTemporaryDirectory() 153 | { 154 | string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Path.GetRandomFileName())); 155 | Directory.CreateDirectory(tempDirectory); 156 | return tempDirectory; 157 | } 158 | 159 | public static string RemoveInternalsVisibleToAttribute(string assemblyInfo) 160 | { 161 | return Regex.Replace(assemblyInfo, @"(\[assembly: InternalsVisibleTo.+\])", string.Empty); 162 | } 163 | 164 | public static void Execute(Action action, string description) 165 | { 166 | int currentIndentation = depth; 167 | WriteStart(description); 168 | depth++; 169 | Stopwatch watch = new Stopwatch(); 170 | watch.Start(); 171 | action(); 172 | watch.Stop(); 173 | depth--; 174 | WriteEnd("...Done! ({0} ms)", watch.ElapsedMilliseconds); 175 | } 176 | 177 | public static void PatchAssemblyVersionInfo(string version, string fileVersion, string frameworkMoniker, string pathToAssemblyInfo) 178 | { 179 | var assemblyInfo = ReadFile(pathToAssemblyInfo); 180 | var patchedAssemblyInfo = Regex.Replace(assemblyInfo, @"((?<=AssemblyVersion\(.)[\d\.]+)", fileVersion); 181 | patchedAssemblyInfo = Regex.Replace(patchedAssemblyInfo, @"((?<=AssemblyFileVersion\(.)[\d\.]+)", fileVersion); 182 | patchedAssemblyInfo = Regex.Replace(patchedAssemblyInfo, @"((?<=AssemblyInformationalVersion\(.)[\d\.]+)", version); 183 | patchedAssemblyInfo = Regex.Replace(patchedAssemblyInfo, @"(AssemblyCopyright\(""\D+)(\d*)", "${1}" + DateTime.Now.Year); 184 | WriteFile(pathToAssemblyInfo, patchedAssemblyInfo); 185 | } 186 | 187 | public static string GetVersionNumberFromSourceFile(string pathToSourceFile) 188 | { 189 | var source = ReadFile(pathToSourceFile); 190 | var versionNumber = Regex.Match(source, @"version\s(\d\.\d\.\d\S*)").Groups[1].Value; 191 | return versionNumber; 192 | } 193 | 194 | public static void PatchNugetVersionInfo(string pathToNugetSpecification, string version) 195 | { 196 | ReplaceInFile(@"()(.+)(<\/version>)", "${1}" + version + "$3", pathToNugetSpecification); 197 | } 198 | 199 | public static void ReplaceInFile(string pattern, string value, string pathToFile) 200 | { 201 | var source = ReadFile(pathToFile); 202 | var replacedSource = Regex.Replace(source, pattern, value); 203 | WriteFile(pathToFile, replacedSource); 204 | } 205 | 206 | 207 | public static string ReadFile(string pathToFile) 208 | { 209 | using(var reader = new StreamReader(pathToFile)) 210 | { 211 | return reader.ReadToEnd(); 212 | } 213 | } 214 | 215 | public static void WriteFile(string pathToFile, string content) 216 | { 217 | using(var writer = new StreamWriter(pathToFile)) 218 | { 219 | writer.Write(content); 220 | } 221 | } 222 | 223 | public static class MsBuild 224 | { 225 | public static void Build(string pathToSolutionFile) 226 | { 227 | string pathToMsBuild = ResolvePathToMsBuild(); 228 | Command.Execute(pathToMsBuild, pathToSolutionFile + " /property:Configuration=Release /p:VisualStudioVersion=12.0 /verbosity:minimal","."); 229 | } 230 | 231 | private static string ResolvePathToMsBuild() 232 | { 233 | return Path.Combine(PathResolver.GetPathToMsBuildTools(), "MsBuild.exe"); 234 | } 235 | } 236 | 237 | public static class PathResolver 238 | { 239 | public static string GetPathToMsBuildTools() 240 | { 241 | string keyName = @"SOFTWARE\Wow6432Node\Microsoft\MSBuild\ToolsVersions"; 242 | string decimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; 243 | 244 | RegistryKey key = Registry.LocalMachine.OpenSubKey(keyName); 245 | string[] subKeynames = key.GetSubKeyNames().Select(n => n.Replace(".", decimalSeparator)).ToArray(); 246 | Collection versionNumbers = new Collection(); 247 | 248 | for (int i = 0; i < subKeynames.Length; i++) 249 | { 250 | decimal versionNumber; 251 | if (decimal.TryParse(subKeynames[i], out versionNumber)) 252 | { 253 | versionNumbers.Add(versionNumber); 254 | } 255 | } 256 | 257 | decimal latestVersionNumber = versionNumbers.OrderByDescending(n => n).First(); 258 | RegistryKey latestVersionSubKey = key.OpenSubKey(latestVersionNumber.ToString().Replace(decimalSeparator, ".")); 259 | string pathToMsBuildTools = (string)latestVersionSubKey.GetValue("MSBuildToolsPath"); 260 | return pathToMsBuildTools; 261 | } 262 | } 263 | 264 | 265 | 266 | public static class MsTest 267 | { 268 | public static void Run(string pathToTestAssembly, string pathToTestAdapter) 269 | { 270 | string pathToMsTest = @"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe"; 271 | string result = Command.Execute(pathToMsTest, pathToTestAssembly + " /TestAdapterPath:" + pathToTestAdapter, @"(Total tests:.*)|(Test execution time:.*)|(Failed.*)"); 272 | } 273 | 274 | public static void RunWithCodeCoverage(string pathToTestAssembly, string pathToPackagesFolder, params string[] includedAssemblies) 275 | { 276 | string pathToMsTest = @"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe"; 277 | string pathToTestAdapter = ResolveDirectory(pathToPackagesFolder, "xunit.runner.visualstudio.testadapter.dll"); 278 | string result = Command.Execute(pathToMsTest, pathToTestAssembly + " /Enablecodecoverage" + " /TestAdapterPath:" + pathToTestAdapter, @"(Test execution.*)|(Test Run.*)|(Total tests.*)|\s*(.*coverage)|(Attachments:)"); 279 | var pathToCoverageFile = GetPathToCoverageFile(result); 280 | var pathToCoverageXmlFile = GetPathToCoverageXmlFile(pathToCoverageFile); 281 | 282 | var directory = Path.GetDirectoryName(pathToCoverageFile); 283 | ConvertCoverageFileToXml(pathToCoverageFile, pathToPackagesFolder); 284 | CreateSummaryFile(pathToCoverageXmlFile, directory, includedAssemblies, pathToPackagesFolder); 285 | ValidateCodeCoverage(directory); 286 | } 287 | 288 | private static void ValidateCodeCoverage(string directory) 289 | { 290 | var pathToSummaryFile = Path.Combine(directory, "Summary.xml"); 291 | var summaryContent = ReadFile(pathToSummaryFile); 292 | var coverage = Regex.Match(summaryContent, "LineCoverage>(.*)<",RegexOptions.IgnoreCase).Groups[1].Captures[0].Value; 293 | 294 | WriteLine("Code coverage is {0}", coverage); 295 | 296 | if (coverage != "100%") 297 | { 298 | MatchCollection matchesRepresentingClassesWithInsufficientCoverage = Regex.Matches(summaryContent, @"Class name=""(.*?)"" coverage=""(\d{1,2}|\d+\.\d+)"""); 299 | foreach (Match match in matchesRepresentingClassesWithInsufficientCoverage) 300 | { 301 | var className = match.Groups[1].Captures[0].Value.Replace("<", "<").Replace(">", ">"); 302 | var classCoverage = match.Groups[2].Captures[0].Value; 303 | WriteLine("Class name: {0} has only {1}% coverage", className, classCoverage); 304 | } 305 | 306 | throw new InvalidOperationException("Deploy failed. Test coverage is only " + coverage); 307 | } 308 | 309 | 310 | } 311 | 312 | public static void ConvertCoverageFileToXml(string pathToCoverageFile, string pathToPackagesFolder) 313 | { 314 | string pathToCoverageToXml = GetFile(pathToPackagesFolder, "CoverageToXml.exe"); 315 | Command.Execute(pathToCoverageToXml, StringUtils.Quote(pathToCoverageFile), null); 316 | } 317 | 318 | public static void CreateSummaryFile(string pathToCoverageXmlFile, string directory, string[] includedAssemblies, string pathToPackagesFolder) 319 | { 320 | var filters = includedAssemblies.Select (a => "+" + a).Aggregate ((current, next) => current + ";" + next).ToLower(); 321 | string pathToReportGenerator = GetFile(pathToPackagesFolder, "ReportGenerator.exe"); 322 | Command.Execute(pathToReportGenerator, "-reports:" + StringUtils.Quote(pathToCoverageXmlFile) + " -targetdir:" + StringUtils.Quote(directory) 323 | + " -reporttypes:xmlsummary -filters:" + filters ); 324 | } 325 | 326 | 327 | public static string GetPathToCoverageFile(string result) 328 | { 329 | var path = Regex.Match(result, @"Attachments:\s*(.*.coverage)").Groups[1].Value; 330 | WriteLine(path); 331 | return path; 332 | } 333 | 334 | private static string GetPathToCoverageXmlFile(string pathToCoverageFile) 335 | { 336 | return Path.Combine(Path.GetDirectoryName(pathToCoverageFile), Path.GetFileNameWithoutExtension(pathToCoverageFile)) + ".coveragexml"; 337 | } 338 | 339 | } 340 | 341 | public static class DirectoryUtils 342 | { 343 | public static void Delete(string path) 344 | { 345 | if (!Directory.Exists(path)) 346 | { 347 | return; 348 | } 349 | 350 | // http://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true 351 | foreach (string directory in Directory.GetDirectories(path)) 352 | { 353 | Delete(directory); 354 | } 355 | 356 | try 357 | { 358 | Directory.Delete(path, true); 359 | } 360 | catch (IOException) 361 | { 362 | Directory.Delete(path, true); 363 | } 364 | catch (UnauthorizedAccessException) 365 | { 366 | Directory.Delete(path, true); 367 | } 368 | } 369 | } 370 | 371 | public class FileUtils 372 | { 373 | public static void Rename(string pathToFile, string newName) 374 | { 375 | string directory = Path.GetDirectoryName(pathToFile); 376 | string pathToNewFile = Path.Combine(directory, newName); 377 | File.Move(pathToFile, pathToNewFile); 378 | } 379 | 380 | } 381 | 382 | 383 | public static class Command 384 | { 385 | private static StringBuilder lastProcessOutput = new StringBuilder(); 386 | 387 | private static StringBuilder lastStandardErrorOutput = new StringBuilder(); 388 | 389 | public static string Execute(string commandPath, string arguments, string capture = null) 390 | { 391 | lastProcessOutput.Clear(); 392 | lastStandardErrorOutput.Clear(); 393 | var startInformation = CreateProcessStartInfo(commandPath, arguments); 394 | var process = CreateProcess(startInformation); 395 | SetVerbosityLevel(process, capture); 396 | process.Start(); 397 | RunAndWait(process); 398 | 399 | if (process.ExitCode != 0 && commandPath != "robocopy") 400 | { 401 | WriteLine(lastStandardErrorOutput.ToString()); 402 | throw new InvalidOperationException("Command failed"); 403 | } 404 | 405 | return lastProcessOutput.ToString(); 406 | } 407 | 408 | private static ProcessStartInfo CreateProcessStartInfo(string commandPath, string arguments) 409 | { 410 | var startInformation = new ProcessStartInfo(StringUtils.Quote(commandPath)); 411 | startInformation.CreateNoWindow = true; 412 | startInformation.Arguments = arguments; 413 | startInformation.RedirectStandardOutput = true; 414 | startInformation.RedirectStandardError = true; 415 | startInformation.UseShellExecute = false; 416 | 417 | return startInformation; 418 | } 419 | 420 | private static void RunAndWait(Process process) 421 | { 422 | process.BeginErrorReadLine(); 423 | process.BeginOutputReadLine(); 424 | process.WaitForExit(); 425 | } 426 | 427 | private static void SetVerbosityLevel(Process process, string capture = null) 428 | { 429 | if(capture != null) 430 | { 431 | process.OutputDataReceived += (s, e) => 432 | { 433 | if (e.Data == null) 434 | { 435 | return; 436 | } 437 | 438 | if (Regex.Matches(e.Data, capture,RegexOptions.Multiline).Count > 0) 439 | { 440 | lastProcessOutput.AppendLine(e.Data); 441 | WriteLine(e.Data); 442 | } 443 | }; 444 | } 445 | process.ErrorDataReceived += (s, e) => 446 | { 447 | lastStandardErrorOutput.AppendLine(); 448 | lastStandardErrorOutput.AppendLine(e.Data); 449 | }; 450 | } 451 | 452 | private static Process CreateProcess(ProcessStartInfo startInformation) 453 | { 454 | var process = new Process(); 455 | process.StartInfo = startInformation; 456 | //process.EnableRaisingEvents = true; 457 | return process; 458 | } 459 | } 460 | 461 | public static class StringUtils 462 | { 463 | public static string Quote(string value) 464 | { 465 | return "\"" + value + "\""; 466 | } 467 | } 468 | 469 | public static class NuGet 470 | { 471 | public static void CreatePackage(string pathToMetadata, string outputDirectory) 472 | { 473 | string arguments = "pack " + StringUtils.Quote(pathToMetadata) + " -OutputDirectory " + StringUtils.Quote(outputDirectory); 474 | Command.Execute("nuget", arguments, "."); 475 | } 476 | 477 | public static void Restore(string projectDirectory) 478 | { 479 | var result = Command.Execute("nuget", "restore " + Path.Combine(projectDirectory, "packages.config") + " -PackagesDirectory " + Path.Combine(projectDirectory, @"..\packages"),"."); 480 | } 481 | 482 | public static void Update(string pathToSolutionFile) 483 | { 484 | var result = Command.Execute("nuget" , "update " + pathToSolutionFile, "."); 485 | WriteLine(result); 486 | } 487 | 488 | public static void BumpDependenciesToLatestVersion(string pathToNuSpecfile, string pathToPackagesFolder) 489 | { 490 | var nuSpec = XDocument.Load(pathToNuSpecfile); 491 | var elements = nuSpec.Descendants("dependency"); 492 | foreach (var element in elements) 493 | { 494 | Console.WriteLine(element.Attribute("id").Value); 495 | } 496 | } 497 | } 498 | 499 | public static class Internalizer 500 | { 501 | public static void Internalize(string pathToSourceFile, string frameworkMoniker, params string[] exceptTheseTypes) 502 | { 503 | var source = ReadFile(pathToSourceFile); 504 | 505 | // Include source code that matches the framework moniker. 506 | 507 | source = Regex.Replace(source, @"#if.*" + frameworkMoniker + ".*\r\n((.*\r\n)*?)#endif\r\n", "$1"); 508 | 509 | // Exclude source code that does not match the framework moniker. 510 | source = Regex.Replace(source, @"#if.*\r\n((.*\r\n)*?)#endif\r\n",""); 511 | 512 | string negativeLookahead = string.Empty; 513 | if (exceptTheseTypes != null) 514 | { 515 | foreach (var type in exceptTheseTypes) 516 | { 517 | negativeLookahead += string.Format("(?!{0})", type); 518 | } 519 | } 520 | 521 | // Make all public classes internal 522 | source = Regex.Replace(source, "public (.*class |struct |interface )" + negativeLookahead, "internal $1"); 523 | 524 | // Exclude classes from code coverage 525 | source = Regex.Replace(source, @"(([^\S\r\n]*)internal.*class.*)", "$2[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]\r\n$1"); 526 | 527 | // Update version information with the framework moniker. 528 | source = Regex.Replace(source, @"(LightInject version \S*)", "$1 (" + frameworkMoniker + ")"); 529 | 530 | WriteFile(pathToSourceFile, source); 531 | } 532 | } 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | -------------------------------------------------------------------------------- /build/csx.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | csx 5 | csx 6 | 2.0.3 7 | Bernhard Richter 8 | Bernhard Richter 9 | C# Script Runner for .Net Core. 10 | A simple C# script runner for .Net Core with debug and NuGet support. 11 | https://github.com/seesharper/csx 12 | CSharp;Scripting;.NetCore 13 | Copyright © 2017 Bernhard Richter 14 | false 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /build/project.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /build/tmp/Program.cs: -------------------------------------------------------------------------------- 1 | namespace csx 2 | { 3 | using Dotnet.Script.NuGetMetadataResolver; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Logging.Console; 6 | using Microsoft.Extensions.CommandLineUtils; 7 | 8 | 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | var cli = new CommandLineApplication(false); 14 | cli.Description = "C# script runner for .Net Core with debug and NuGetCommand support"; 15 | cli.HelpOption("-? | -h | --help"); 16 | var debugOption = cli.Option("-d | --debug", "Outputs debug messages to the console", CommandOptionType.NoValue); 17 | 18 | cli.Command("init", config => 19 | { 20 | config.Description = "Creates the launch.json file and the tasks.json file needed to launch and debug the script."; 21 | config.OnExecute(() => 22 | { 23 | var skaffolder = new Skaffolder(); 24 | skaffolder.InitializerFolder(); 25 | return 0; 26 | }); 27 | }); 28 | 29 | cli.Command("new", config => 30 | { 31 | config.Description = "Creates a new script file"; 32 | var fileNameArgument = config.Argument("filename", "The script file name"); 33 | config.OnExecute(() => 34 | { 35 | var skaffolder = new Skaffolder(); 36 | if (fileNameArgument.Value == null) 37 | { 38 | config.ShowHelp(); 39 | return 0; 40 | } 41 | skaffolder.CreateNewScriptFile(fileNameArgument.Value); 42 | return 0; 43 | }); 44 | }); 45 | 46 | 47 | var file = cli.Argument("script", "The path to the script to be executed"); 48 | 49 | cli.OnExecute(() => 50 | { 51 | if (string.IsNullOrWhiteSpace(file.Value) ) 52 | { 53 | cli.ShowHelp(); 54 | return 0; 55 | } 56 | var scriptExecutor = CreateScriptExecutor(debugOption.HasValue()); 57 | scriptExecutor.Execute(file.Value, cli.RemainingArguments.ToArray()); 58 | return 0; 59 | }); 60 | 61 | 62 | cli.Execute(args); 63 | } 64 | 65 | private static ScriptExecutor CreateScriptExecutor(bool debug) 66 | { 67 | var loggerFactory = new LoggerFactory(); 68 | loggerFactory.AddProvider( 69 | new ConsoleLoggerProvider( 70 | (text, logLevel) => logLevel >= (debug ? LogLevel.Debug : LogLevel.Warning), true)); 71 | return new ScriptExecutor(ScriptProjectProvider.Create(loggerFactory),loggerFactory); 72 | 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /build/tmp/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "csx": { 4 | "commandName": "Project", 5 | "commandLineArgs": "samplescript.csx -d test" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /build/tmp/anotherscript.csx: -------------------------------------------------------------------------------- 1 | //#r "nuget:System.Linq/4.3.0" 2 | 3 | //#r "nuget:AutoMapper/6.0.2" 4 | //#r "nuget:Newtonsoft.Json/10.0.1" 5 | //#r "nuget:System.Runtime.Serialization.Formatters/4.3.0" 6 | //#r "nuget:System.ComponentModel.TypeConverter/4.0.0" 7 | //using Newtonsoft.Json; 8 | //using AutoMapper; 9 | using System; 10 | using System.Text.RegularExpressions; 11 | using System.Linq; 12 | 13 | public static void Test() 14 | { 15 | Console.WriteLine("hello!"); 16 | } 17 | 18 | 19 | 20 | 21 | //var regex = new Regex(@"nuget:(.+)\/(\d+\.\d+\.\d+)", RegexOptions.IgnoreCase); 22 | 23 | //var test = new { hi = "i'm json!" }; 24 | //Console.WriteLine(JsonConvert.SerializeObject(test)); 25 | 26 | //Console.WriteLine(typeof(MapperConfiguration)); 27 | 28 | 29 | -------------------------------------------------------------------------------- /build/tmp/csx.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp1.1 5 | win7-x64;win7-x86;osx.10.11-x64;ubuntu.14.04-x64;ubuntu.16.04-x64;centos.7-x64;rhel.7.2-x64;debian.8-x64;fedora.23-x64;opensuse.13.2-x64 6 | 1.0.0-beta8 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /build/tmp/csx.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26228.9 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "csx", "csx.csproj", "{B4311345-31BC-477C-B2E2-4A9EBB1D2AF4}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {B4311345-31BC-477C-B2E2-4A9EBB1D2AF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {B4311345-31BC-477C-B2E2-4A9EBB1D2AF4}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {B4311345-31BC-477C-B2E2-4A9EBB1D2AF4}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {B4311345-31BC-477C-B2E2-4A9EBB1D2AF4}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /build/tmp/csx.sln.DotSettings.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | True 4 | (Doc Ln 135 Col 0) 5 | F300DC1B-E643-4CCB-80EE-A7F840B07500/f:NuGet.cs 6 | NumberedBookmarkManager -------------------------------------------------------------------------------- /build/tmp/samplescript.csx: -------------------------------------------------------------------------------- 1 | #! "netcoreapp1.1" 2 | #r "nuget:NetStandard.Library,1.6.1" 3 | //#load "anotherscript.csx" 4 | 5 | 6 | 7 | using System; 8 | //using System.Text.RegularExpressions; 9 | //using System.Linq; 10 | //using AutoMapper; 11 | //using LightInject; 12 | 13 | 14 | Console.WriteLine("hello!"); 15 | int test = 0; 16 | int result = 10 / test; 17 | 18 | // Call a method in 'anotherscript'; 19 | //Test(); 20 | 21 | 22 | //Console.WriteLine(typeof(MapperConfiguration)); 23 | 24 | 25 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## csx 2 | 3 | A super simple C# script runner for .Net Core with debug and NuGet support. 4 | 5 | ### Installing 6 | 7 | ```shell 8 | choco install csx --source https://www.nuget.org/api/v2 9 | ``` 10 | 11 | ### Visual Studio Code 12 | 13 | Intellisense for C# script files is provided by [OmniSharp](https://github.com/OmniSharp/omnisharp-roslyn) 14 | 15 | There is still an [open pull request](https://github.com/OmniSharp/omnisharp-roslyn/pull/813) that enables intellisense for scripts with NuGet references. 16 | 17 | Until that PR gets merged, download this [prebuilt version of OmniSharp](https://github.com/seesharper/omnisharp-roslyn/releases/tag/v1.21.0-Nuget) that you can use to enable this functionality. 18 | 19 | > Note: Any editor can be used to edit script files, but we need VS Code to be able to debug. 20 | 21 | 22 | 23 | #### Windows 24 | 25 | Simply extract the [OmniSharp.zip](https://github.com/seesharper/omnisharp-roslyn/files/1065706/OmniSharp.zip) file anywhere on your drive and update VS Code settings. 26 | 27 | ```json 28 | { 29 | "omnisharp.path": "PATH_TO_OMNISHARP_FOLDER/Omnisharp.exe" 30 | } 31 | ``` 32 | 33 | 34 | 35 | ### Hello World 36 | 37 | Create a new folder somewhere and from within that folder issue the following command. 38 | 39 | ```shell 40 | csx init 41 | ``` 42 | 43 | This command creates 4 files 44 | 45 | | Name | | 46 | | ------------------- | ---------------------------------------- | 47 | | .vscode/tasks.json | Enables the script to be executed within VS Code. | 48 | | .vscode/launch.json | Configures script debugging | 49 | | omnisharp.json | Enables NuGet references in scripts (Intellisense) | 50 | | helloworld.csx | A minimal script that outputs "Hello world!" to the console. | 51 | 52 | > Note: Only the actual script file is needed for script execution. Other files are just for VS Code awesomeness. 53 | 54 | 55 | 56 | ### Running the script 57 | 58 | #### Command Line 59 | 60 | ```shell 61 | csx helloworld.csx 62 | Hello world! 63 | ``` 64 | 65 | #### VS Code 66 | 67 | Ctrl+Shift+B or "Debug->Start Without Debugging " 68 | 69 | ### Debugging 70 | 71 | Set a breakpoint anywhere in your code and hit F5 72 | 73 | ### NuGet 74 | 75 | In addition to referencing other scripts using the #load directive we can also reference NuGet packages. 76 | 77 | ```csharp 78 | #r "nuget:AutoMapper,6.0.0" 79 | ``` 80 | 81 | -------------------------------------------------------------------------------- /src/CommandRunner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Text; 4 | using Microsoft.Extensions.Logging; 5 | 6 | namespace csx 7 | { 8 | /// 9 | /// A class that is capable of running a command. 10 | /// 11 | public class CommandRunner : ICommandRunner 12 | { 13 | private readonly ILogger logger; 14 | private readonly StringBuilder lastStandardErrorOutput = new StringBuilder(); 15 | private readonly StringBuilder lastProcessOutput = new StringBuilder(); 16 | 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | /// The used to create an instance. 21 | public CommandRunner(ILoggerFactory loggerFactory) 22 | { 23 | logger = loggerFactory.CreateLogger(); 24 | } 25 | 26 | /// 27 | public string Execute(string commandPath, string arguments) 28 | { 29 | lastStandardErrorOutput.Clear(); 30 | 31 | logger.LogInformation($"Executing {commandPath} {arguments}"); 32 | var startInformation = CreateProcessStartInfo(commandPath, arguments); 33 | var process = CreateProcess(startInformation); 34 | RunAndWait(process); 35 | logger.LogInformation(lastProcessOutput.ToString()); 36 | if (process.ExitCode != 0) 37 | { 38 | logger.LogError(lastStandardErrorOutput.ToString()); 39 | throw new InvalidOperationException($"The command {commandPath} {arguments} failed to execute"); 40 | } 41 | return lastProcessOutput.ToString(); 42 | } 43 | 44 | private static ProcessStartInfo CreateProcessStartInfo(string commandPath, string arguments) 45 | { 46 | var startInformation = new ProcessStartInfo(commandPath); 47 | startInformation.CreateNoWindow = true; 48 | startInformation.Arguments = arguments; 49 | startInformation.RedirectStandardOutput = true; 50 | startInformation.RedirectStandardError = true; 51 | startInformation.UseShellExecute = false; 52 | return startInformation; 53 | } 54 | 55 | private Process CreateProcess(ProcessStartInfo startInformation) 56 | { 57 | var process = new Process(); 58 | process.StartInfo = startInformation; 59 | process.ErrorDataReceived += (s, a) => 60 | { 61 | if (!string.IsNullOrWhiteSpace(a.Data)) 62 | { 63 | lastStandardErrorOutput.AppendLine(a.Data); 64 | } 65 | 66 | }; 67 | process.OutputDataReceived += (s, a) => 68 | { 69 | lastProcessOutput.AppendLine(a.Data); 70 | }; 71 | return process; 72 | } 73 | 74 | private static void RunAndWait(Process process) 75 | { 76 | process.Start(); 77 | process.BeginErrorReadLine(); 78 | process.BeginOutputReadLine(); 79 | process.WaitForExit(); 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /src/ICommandRunner.cs: -------------------------------------------------------------------------------- 1 | namespace csx 2 | { 3 | /// 4 | /// Represents a class that is capable of running a command. 5 | /// 6 | public interface ICommandRunner 7 | { 8 | /// 9 | /// Executes the command identified by the 10 | /// with the given . 11 | /// 12 | /// The command to be executed. 13 | /// The arguments to be passed to the command. 14 | string Execute(string commandPath, string arguments); 15 | } 16 | } -------------------------------------------------------------------------------- /src/IScriptParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace csx 4 | { 5 | /// 6 | /// Represents a class that is capable of parsing a set of script files 7 | /// and return information about NuGet references and the target framework. 8 | /// 9 | public interface IScriptParser 10 | { 11 | /// 12 | /// Parses the given set of and returns a 13 | /// instance that contains a list of NuGet reference found within the script files. 14 | /// 15 | /// A list of script files for which to parse and resolve NuGet references. 16 | /// A 17 | /// instance that contains a list of NuGet reference found within the script files. 18 | ParseResult ParseFrom(IEnumerable csxFiles); 19 | } 20 | } -------------------------------------------------------------------------------- /src/NuGetMetadataReferenceResolver.cs: -------------------------------------------------------------------------------- 1 | namespace csx 2 | { 3 | using System; 4 | using System.Collections.Immutable; 5 | using System.Reflection; 6 | using Microsoft.CodeAnalysis; 7 | 8 | /// 9 | /// A decorator that handles 10 | /// references to NuGet packages in scripts. 11 | /// 12 | public class NuGetMetadataReferenceResolver : MetadataReferenceResolver 13 | { 14 | private readonly MetadataReferenceResolver metadataReferenceResolver; 15 | 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | /// The target . 20 | public NuGetMetadataReferenceResolver(MetadataReferenceResolver metadataReferenceResolver) 21 | { 22 | this.metadataReferenceResolver = metadataReferenceResolver; 23 | } 24 | 25 | /// 26 | public override bool Equals(object other) 27 | { 28 | return metadataReferenceResolver.Equals(other); 29 | } 30 | 31 | /// 32 | public override int GetHashCode() 33 | { 34 | return metadataReferenceResolver.GetHashCode(); 35 | } 36 | 37 | public override bool ResolveMissingAssemblies => metadataReferenceResolver.ResolveMissingAssemblies; 38 | 39 | public override PortableExecutableReference ResolveMissingAssembly(MetadataReference definition, AssemblyIdentity referenceIdentity) 40 | { 41 | return metadataReferenceResolver.ResolveMissingAssembly(definition, referenceIdentity); 42 | } 43 | 44 | 45 | public override ImmutableArray ResolveReference(string reference, string baseFilePath, MetadataReferenceProperties properties) 46 | { 47 | if (reference.StartsWith("nuget", StringComparison.OrdinalIgnoreCase)) 48 | { 49 | // HACK We need to return something here to "mark" the reference as resolved. 50 | // https://github.com/dotnet/roslyn/blob/master/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Resolution.cs#L838 51 | return ImmutableArray.Empty.Add( 52 | MetadataReference.CreateFromFile(typeof(NuGetMetadataReferenceResolver).GetTypeInfo().Assembly.Location)); 53 | } 54 | var resolvedReference = metadataReferenceResolver.ResolveReference(reference, baseFilePath, properties); 55 | return resolvedReference; 56 | } 57 | 58 | 59 | 60 | } 61 | } -------------------------------------------------------------------------------- /src/PackageReference.cs: -------------------------------------------------------------------------------- 1 | namespace csx 2 | { 3 | /// 4 | /// Represents a NuGet package reference found in a script file. 5 | /// 6 | public class PackageReference 7 | { 8 | /// 9 | /// Initializes a new instance of the class. 10 | /// 11 | /// The id of the NuGet package. 12 | /// The version of the NuGet package. 13 | public PackageReference(string id, string version) 14 | { 15 | Id = id; 16 | Version = version; 17 | } 18 | 19 | /// 20 | /// Gets the id of the NuGet package 21 | /// 22 | public string Id { get; } 23 | 24 | /// 25 | /// Gets the version of the NuGet package. 26 | /// 27 | public string Version { get; } 28 | 29 | 30 | /// 31 | public override int GetHashCode() 32 | { 33 | return Id.GetHashCode() ^ Version.GetHashCode(); 34 | } 35 | 36 | /// 37 | public override bool Equals(object obj) 38 | { 39 | var other = (PackageReference) obj; 40 | return other.Id == Id && other.Version == Version; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/ParseResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace csx 4 | { 5 | /// 6 | /// Represents the result of parsing a set of script files. 7 | /// 8 | public class ParseResult 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | /// A list of instances that represents 14 | /// references to NuGet packages in a given set of script files. 15 | /// The target framework inferred from the #! directive. 16 | public ParseResult(IReadOnlyCollection packageReferences, string targetFramework) 17 | { 18 | PackageReferences = packageReferences; 19 | TargetFramework = targetFramework; 20 | } 21 | 22 | /// 23 | /// Gets a list of NuGet package references found within a set of script files. 24 | /// 25 | public IReadOnlyCollection PackageReferences { get; } 26 | 27 | /// 28 | /// Gets the target framework inferred from the #! directive. 29 | /// 30 | public string TargetFramework { get; } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Loader; 3 | 4 | namespace csx 5 | { 6 | using Microsoft.Extensions.Logging; 7 | using Microsoft.Extensions.Logging.Console; 8 | using Microsoft.Extensions.CommandLineUtils; 9 | 10 | 11 | class Program 12 | { 13 | static int Main(string[] args) 14 | { 15 | var cli = new CommandLineApplication(false); 16 | cli.Description = "C# script runner for .Net Core with debug and NuGetCommand support"; 17 | cli.HelpOption("-? | -h | --help"); 18 | var debugOption = cli.Option("-d | --debug", "Outputs debug messages to the console", CommandOptionType.NoValue); 19 | 20 | cli.Command("init", config => 21 | { 22 | config.Description = "Creates the launch.json file and the tasks.json file needed to launch and debug the script."; 23 | config.OnExecute(() => 24 | { 25 | var skaffolder = new Skaffolder(); 26 | skaffolder.InitializerFolder(); 27 | return 0; 28 | }); 29 | }); 30 | 31 | cli.Command("new", config => 32 | { 33 | config.Description = "Creates a new script file"; 34 | var fileNameArgument = config.Argument("filename", "The script file name"); 35 | config.OnExecute(() => 36 | { 37 | var skaffolder = new Skaffolder(); 38 | if (fileNameArgument.Value == null) 39 | { 40 | config.ShowHelp(); 41 | return 0; 42 | } 43 | skaffolder.CreateNewScriptFile(fileNameArgument.Value); 44 | return 0; 45 | }); 46 | }); 47 | 48 | 49 | var file = cli.Argument("script", "The path to the script to be executed"); 50 | 51 | cli.OnExecute(() => 52 | { 53 | if (string.IsNullOrWhiteSpace(file.Value) ) 54 | { 55 | cli.ShowHelp(); 56 | return 0; 57 | } 58 | var scriptExecutor = CreateScriptExecutor(debugOption.HasValue()); 59 | try 60 | { 61 | scriptExecutor.Execute(file.Value, cli.RemainingArguments.ToArray()); 62 | } 63 | catch (Exception) 64 | { 65 | return -1; 66 | } 67 | return 0; 68 | }); 69 | 70 | 71 | return cli.Execute(args); 72 | } 73 | 74 | private static ScriptExecutor CreateScriptExecutor(bool debug) 75 | { 76 | 77 | 78 | var loggerFactory = new LoggerFactory(); 79 | loggerFactory.AddProvider( 80 | new ConsoleLoggerProvider( 81 | (text, logLevel) => logLevel >= (debug ? LogLevel.Debug : LogLevel.Error), true)); 82 | var scriptParser = new ScriptParser(loggerFactory); 83 | var scriptProjectProvider = new ScriptProjectProvider(scriptParser, loggerFactory); 84 | var runtimeDependencyResolver = new RuntimeDependencyResolver(new CommandRunner(loggerFactory), loggerFactory); 85 | return new ScriptExecutor(scriptProjectProvider, runtimeDependencyResolver, loggerFactory); 86 | 87 | } 88 | 89 | 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/ProjectFile.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using System.Xml.Linq; 4 | 5 | namespace csx 6 | { 7 | public class ProjectFile 8 | { 9 | private readonly XDocument document; 10 | 11 | 12 | public ProjectFile() 13 | { 14 | var template = ResourceLoader.ReadResourceFile("csproj.template"); 15 | document = XDocument.Parse(template); 16 | 17 | } 18 | 19 | public void AddPackageReference(PackageReference packageReference) 20 | { 21 | var itemGroupElement = document.Descendants("ItemGroup").Single(); 22 | var packageReferenceElement = new XElement("PackageReference"); 23 | packageReferenceElement.Add(new XAttribute("Include", packageReference.Id)); 24 | packageReferenceElement.Add(new XAttribute("Version", packageReference.Version)); 25 | itemGroupElement.Add(packageReferenceElement); 26 | } 27 | 28 | public void Save(string pathToProjectFile) 29 | { 30 | using (var fileStream = new FileStream(pathToProjectFile, FileMode.Create, FileAccess.Write)) 31 | { 32 | document.Save(fileStream); 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "csx": { 4 | "commandName": "Project", 5 | "commandLineArgs": "samplescript.csx" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /src/ResourceLoader.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Reflection; 3 | 4 | namespace csx 5 | { 6 | public static class ResourceLoader 7 | { 8 | public static string ReadResourceFile(string name) 9 | { 10 | var resourceStream = typeof(ResourceLoader).GetTypeInfo().Assembly.GetManifestResourceStream($"csx.Templates.{name}"); 11 | using (var streamReader = new StreamReader(resourceStream)) 12 | { 13 | return streamReader.ReadToEnd(); 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/RuntimeDependencyResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Runtime.Loader; 7 | using System.Text.RegularExpressions; 8 | using Microsoft.Extensions.DependencyModel; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace csx 12 | { 13 | public interface IRuntimeDependencyResolver 14 | { 15 | IEnumerable GetRuntimeDependencies(string pathToProjectFile); 16 | } 17 | 18 | public class RuntimeDependencyResolver : IRuntimeDependencyResolver 19 | { 20 | private readonly ICommandRunner _commandRunner; 21 | 22 | private readonly ILogger _logger; 23 | 24 | // Note: Windows only, Mac and Linux needs something else? 25 | [DllImport("Kernel32.dll")] 26 | private static extern IntPtr LoadLibrary(string path); 27 | 28 | public RuntimeDependencyResolver(ICommandRunner commandRunner, ILoggerFactory loggerFactory) 29 | { 30 | _commandRunner = commandRunner; 31 | _logger = loggerFactory.CreateLogger(); 32 | } 33 | 34 | private DependencyContext ReadDependencyContext(string pathToProjectFile) 35 | { 36 | Restore(pathToProjectFile); 37 | 38 | var pathToAssetsFiles = Path.Combine(Path.GetDirectoryName(pathToProjectFile), "obj", "project.assets.json"); 39 | 40 | using (FileStream fs = new FileStream(pathToAssetsFiles, FileMode.Open, FileAccess.Read)) 41 | { 42 | using (var contextReader = new DependencyContextJsonReader()) 43 | { 44 | return contextReader.Read(fs); 45 | } 46 | } 47 | } 48 | 49 | public IEnumerable GetRuntimeDependencies(string pathToProjectFile) 50 | { 51 | var pathToGlobalPackagesFolder = GetPathToGlobalPackagesFolder(); 52 | var runtimeDepedencies = new HashSet(); 53 | 54 | var context = ReadDependencyContext(pathToProjectFile); 55 | 56 | //Note: Scripting only releates to runtime libraries. 57 | var runtimeLibraries = context.RuntimeLibraries; 58 | 59 | foreach (var runtimeLibrary in runtimeLibraries) 60 | { 61 | ProcessNativeLibraries(runtimeLibrary, pathToGlobalPackagesFolder); 62 | ProcessRuntimeAssemblies(runtimeLibrary, pathToGlobalPackagesFolder, runtimeDepedencies); 63 | } 64 | 65 | return runtimeDepedencies; 66 | } 67 | 68 | private void ProcessRuntimeAssemblies(RuntimeLibrary runtimeLibrary, string pathToGlobalPackagesFolder, 69 | HashSet runtimeDepedencies) 70 | { 71 | 72 | foreach (var runtimeAssemblyGroup in runtimeLibrary.RuntimeAssemblyGroups.Where(rag => IsRelevantForCurrentRuntime(rag.Runtime))) 73 | { 74 | foreach (var assetPath in runtimeAssemblyGroup.AssetPaths) 75 | { 76 | var path = Path.Combine(runtimeLibrary.Path, assetPath); 77 | if (!path.EndsWith("_._")) 78 | { 79 | var fullPath = Path.Combine(pathToGlobalPackagesFolder, path); 80 | _logger.LogInformation(fullPath); 81 | runtimeDepedencies.Add(new RuntimeDependency(runtimeLibrary.Name, fullPath)); 82 | } 83 | } 84 | } 85 | } 86 | 87 | private void ProcessNativeLibraries(RuntimeLibrary runtimeLibrary, string pathToGlobalPackagesFolder) 88 | { 89 | foreach (var nativeLibraryGroup in runtimeLibrary.NativeLibraryGroups.Where(nlg => IsRelevantForCurrentRuntime(nlg.Runtime))) 90 | { 91 | foreach (var assetPath in nativeLibraryGroup.AssetPaths) 92 | { 93 | var fullPath = Path.Combine(pathToGlobalPackagesFolder, runtimeLibrary.Path, 94 | assetPath); 95 | _logger.LogInformation($"Loading native library from {fullPath}"); 96 | LoadLibrary(fullPath); 97 | } 98 | } 99 | } 100 | 101 | private void Restore(string pathToProjectFile) 102 | { 103 | _commandRunner.Execute("DotNet", $"restore {pathToProjectFile} -r win7-x64"); 104 | } 105 | 106 | private string GetPathToGlobalPackagesFolder() 107 | { 108 | var result = _commandRunner.Execute("dotnet", "nuget locals global-packages -l"); 109 | var match = Regex.Match(result, @"global-packages:\s*(.*)\r"); 110 | return match.Groups[1].Captures[0].ToString(); 111 | } 112 | 113 | public bool IsRelevantForCurrentRuntime(string runtime) 114 | { 115 | return string.IsNullOrWhiteSpace(runtime) || runtime == GetRuntimeIdentitifer(); 116 | } 117 | 118 | private static string GetRuntimeIdentitifer() 119 | { 120 | if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return "osx"; 121 | if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return "unix"; 122 | 123 | return "win"; 124 | } 125 | } 126 | 127 | public class RuntimeDependency 128 | { 129 | public string Name { get; } 130 | public string Path { get; } 131 | 132 | public RuntimeDependency(string name, string path) 133 | { 134 | Name = name; 135 | Path = path; 136 | } 137 | 138 | /// 139 | public override int GetHashCode() 140 | { 141 | return Name.GetHashCode() ^ Path.GetHashCode(); 142 | } 143 | 144 | /// 145 | public override bool Equals(object obj) 146 | { 147 | var other = (RuntimeDependency)obj; 148 | return other.Name == Name && other.Path == Path; 149 | } 150 | } 151 | } -------------------------------------------------------------------------------- /src/ScriptExecutor.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using System.Runtime.Loader; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using Microsoft.CodeAnalysis; 11 | using Microsoft.CodeAnalysis.CSharp.Scripting; 12 | using Microsoft.CodeAnalysis.CSharp.Scripting.Hosting; 13 | using Microsoft.CodeAnalysis.Scripting; 14 | using Microsoft.CodeAnalysis.Scripting.Hosting; 15 | using Microsoft.CodeAnalysis.Text; 16 | using Microsoft.Extensions.Logging; 17 | 18 | namespace csx 19 | { 20 | public class ScriptExecutor 21 | { 22 | [DllImport("Kernel32.dll")] 23 | private static extern IntPtr LoadLibrary(string path); 24 | 25 | 26 | private readonly IScriptProjectProvider _scriptProjectProvider; 27 | private readonly IRuntimeDependencyResolver _runtimeDependencyResolver; 28 | private readonly ILogger logger; 29 | 30 | public ScriptExecutor(IScriptProjectProvider scriptProjectProvider, IRuntimeDependencyResolver runtimeDependencyResolver, ILoggerFactory loggerFactory) 31 | { 32 | this._scriptProjectProvider = scriptProjectProvider; 33 | _runtimeDependencyResolver = runtimeDependencyResolver; 34 | this.logger = loggerFactory.CreateLogger(); 35 | } 36 | 37 | public void Execute(string pathToScript, string[] args) 38 | { 39 | //LoadLibrary( 40 | // @"C:\Users\bri\.nuget\packages\runtime.win7-x64.runtime.native.system.data.sqlclient.sni\4.3.0\runtimes\win7-x64\native\sni.dll"); 41 | 42 | if (!Path.IsPathRooted(pathToScript)) 43 | { 44 | pathToScript = Path.GetFullPath(pathToScript); 45 | } 46 | string codeAsPlainText = null; 47 | using (var fileStream = new FileStream(pathToScript, FileMode.Open)) 48 | { 49 | // We need to create a SourceText instance with an encoding 50 | var encodedSourceText = SourceText.From(fileStream, Encoding.UTF8); 51 | codeAsPlainText = encodedSourceText.ToString(); 52 | } 53 | 54 | 55 | var scriptOptions = CreateScriptOptions(pathToScript); 56 | 57 | var globals = new CommandLineScriptGlobals(Console.Out, CSharpObjectFormatter.Instance); 58 | foreach (var arg in args) 59 | { 60 | globals.Args.Add(arg); 61 | } 62 | 63 | logger.LogInformation("Creating script"); 64 | var interactiveAssemblyLoader = new InteractiveAssemblyLoader(); 65 | 66 | var script = CSharpScript.Create(codeAsPlainText, scriptOptions, typeof(CommandLineScriptGlobals), 67 | interactiveAssemblyLoader); 68 | 69 | var warnings = script.GetCompilation().GetDiagnostics() 70 | .Where(d => d.Severity == DiagnosticSeverity.Warning); 71 | foreach (var warning in warnings) 72 | { 73 | logger.LogWarning(warning.ToString()); 74 | } 75 | 76 | var errors = script.GetCompilation().GetDiagnostics() 77 | .Where(d => d.Severity == DiagnosticSeverity.Error); 78 | foreach (var error in errors) 79 | { 80 | logger.LogError(error.ToString()); 81 | } 82 | 83 | 84 | RunScript(script, globals); 85 | } 86 | 87 | 88 | 89 | 90 | private void RunScript(Script script, CommandLineScriptGlobals globals) 91 | { 92 | var scriptState = script.RunAsync(globals, exception => true).Result; 93 | if (scriptState.Exception != null) 94 | { 95 | logger.LogError(scriptState.Exception.ToString()); 96 | throw scriptState.Exception; 97 | } 98 | } 99 | 100 | 101 | 102 | private ScriptOptions CreateScriptOptions(string pathToScript) 103 | { 104 | string[] imports = 105 | { 106 | "System", 107 | "System.IO", 108 | "System.Collections.Generic", 109 | "System.Console", 110 | "System.Diagnostics", 111 | "System.Dynamic", 112 | "System.Linq", 113 | "System.Linq.Expressions", 114 | "System.Text", 115 | "System.Threading.Tasks" 116 | }; 117 | 118 | var scriptOptions = ScriptOptions.Default; 119 | 120 | scriptOptions = AddMetadataReferences(scriptOptions, pathToScript); 121 | return scriptOptions 122 | .WithEmitDebugInformation(true) 123 | .WithFileEncoding(Encoding.UTF8) 124 | .WithFilePath(pathToScript) 125 | .WithImports(imports) 126 | .WithMetadataResolver(new NuGetMetadataReferenceResolver(ScriptMetadataResolver.Default)); 127 | } 128 | 129 | private ScriptOptions AddMetadataReferences(ScriptOptions options, string pathToScript) 130 | { 131 | var targetDirectory = Path.GetDirectoryName(pathToScript); 132 | string pathToProjectFile = _scriptProjectProvider.CreateProject(targetDirectory); 133 | List runtimeDependencies = _runtimeDependencyResolver.GetRuntimeDependencies(pathToProjectFile).ToList(); 134 | var references = runtimeDependencies.Select(r => MetadataReference.CreateFromFile(r.Path)); 135 | AssemblyLoadContext.Default.Resolving += 136 | (context, assemblyName) => MapUnresolvedAssemblyToRuntimeLibrary(runtimeDependencies ,context, assemblyName); 137 | return options.WithReferences(references); 138 | } 139 | 140 | private Assembly MapUnresolvedAssemblyToRuntimeLibrary(IList runtimeDependencies, AssemblyLoadContext loadContext, AssemblyName assemblyName) 141 | { 142 | var runtimeDependency = runtimeDependencies.SingleOrDefault(r => r.Name == assemblyName.Name); 143 | if (runtimeDependency != null) 144 | { 145 | logger.LogInformation($"Unresolved assembly {assemblyName}. Loading from resolved runtime dependencies at path: {runtimeDependency.Path}"); 146 | return loadContext.LoadFromAssemblyPath(runtimeDependency.Path); 147 | } 148 | return null; 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/ScriptParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Text.RegularExpressions; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace csx 8 | { 9 | /// 10 | /// A class that is capable of parsing a set of script files 11 | /// and return information about NuGet references and the target framework. 12 | /// 13 | public class ScriptParser : IScriptParser 14 | { 15 | private readonly ILogger logger; 16 | 17 | /// 18 | /// Initializes a new insstance of the class. 19 | /// 20 | /// The used to create an instance. 21 | public ScriptParser(ILoggerFactory loggerFactory) 22 | { 23 | logger = loggerFactory.CreateLogger(); 24 | } 25 | 26 | /// 27 | public ParseResult ParseFrom(IEnumerable csxFiles) 28 | { 29 | HashSet allPackageReferences = new HashSet(); 30 | string currentTargetFramework = null; 31 | foreach (var csxFile in csxFiles) 32 | { 33 | logger.LogDebug($"Parsing {csxFile}"); 34 | var fileContent = ReadFile(csxFile); 35 | var packageReferences = ReadPackageReferences(fileContent); 36 | allPackageReferences.UnionWith(packageReferences); 37 | string targetFramework = ReadTargetFramework(fileContent); 38 | if (targetFramework != null) 39 | { 40 | if (currentTargetFramework != null && targetFramework != currentTargetFramework) 41 | { 42 | logger.LogWarning($"Found multiple target frameworks. Using {currentTargetFramework}."); 43 | } 44 | else 45 | { 46 | currentTargetFramework = targetFramework; 47 | } 48 | } 49 | } 50 | 51 | return new ParseResult(allPackageReferences, currentTargetFramework); 52 | } 53 | 54 | private IEnumerable ReadPackageReferences(string fileContent) 55 | { 56 | const string pattern = @"^\s*#r\s*""nuget:\s*(.+)\s*,\s*(.*)"""; 57 | var matches = Regex.Matches(fileContent, pattern, RegexOptions.IgnoreCase | RegexOptions.Multiline); 58 | 59 | foreach (var match in matches.Cast()) 60 | { 61 | var id = match.Groups[1].Value; 62 | var version = match.Groups[2].Value; 63 | var packageReference = new PackageReference(id, version); 64 | yield return packageReference; 65 | } 66 | } 67 | 68 | private string ReadTargetFramework(string fileContent) 69 | { 70 | const string pattern = @"^\s*#!\s*""(.*)"""; 71 | var match = Regex.Match(fileContent, pattern); 72 | if (match.Success) 73 | { 74 | return match.Groups[1].Value; 75 | } 76 | return null; 77 | } 78 | 79 | private static string ReadFile(string pathToFile) 80 | { 81 | using (var fileStream = new FileStream(pathToFile,FileMode.Open)) 82 | { 83 | using (var reader = new StreamReader(fileStream)) 84 | { 85 | return reader.ReadToEnd(); 86 | } 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /src/ScriptProjectProvider.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Runtime.InteropServices; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace csx 6 | { 7 | public interface IScriptProjectProvider 8 | { 9 | string CreateProject(string targetDirectory); 10 | } 11 | 12 | public class ScriptProjectProvider : IScriptProjectProvider 13 | { 14 | private readonly IScriptParser scriptParser; 15 | 16 | private readonly ILogger logger; 17 | /// 18 | /// Initializes a new instance of the class. 19 | /// 20 | /// The that is responsible for parsing NuGet references from script files. 21 | public ScriptProjectProvider(IScriptParser scriptParser, ILoggerFactory loggerFactory) 22 | { 23 | this.logger = loggerFactory.CreateLogger(); 24 | this.scriptParser = scriptParser; 25 | } 26 | 27 | public string CreateProject(string targetDirectory) 28 | { 29 | var csxFiles = Directory.GetFiles(targetDirectory, "*.csx", SearchOption.AllDirectories); 30 | var parseresult = scriptParser.ParseFrom(csxFiles); 31 | 32 | var pathToProjectFile = GetPathToProjectFile(targetDirectory); 33 | var projectFile = new ProjectFile(); 34 | 35 | foreach (var packageReference in parseresult.PackageReferences) 36 | { 37 | projectFile.AddPackageReference(packageReference); 38 | } 39 | 40 | projectFile.Save(pathToProjectFile); 41 | logger.LogInformation($"Project file saved to {pathToProjectFile}"); 42 | return pathToProjectFile; 43 | } 44 | 45 | private static string GetPathToProjectFile(string targetDirectory) 46 | { 47 | var tempDirectory = Path.GetTempPath(); 48 | var pathRoot = Path.GetPathRoot(targetDirectory); 49 | var targetDirectoryWithoutRoot = targetDirectory.Substring(pathRoot.Length); 50 | if (pathRoot.Length > 0 && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 51 | { 52 | var driveLetter = pathRoot.Substring(0, 1); 53 | targetDirectoryWithoutRoot = Path.Combine(driveLetter, targetDirectoryWithoutRoot); 54 | } 55 | var pathToProjectJsonDirectory = Path.Combine(tempDirectory, "scripts", targetDirectoryWithoutRoot); 56 | if (!Directory.Exists(pathToProjectJsonDirectory)) 57 | { 58 | Directory.CreateDirectory(pathToProjectJsonDirectory); 59 | } 60 | var pathToProjectJson = Path.Combine(pathToProjectJsonDirectory, "script.csproj"); 61 | return pathToProjectJson; 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /src/Skaffolder.cs: -------------------------------------------------------------------------------- 1 | namespace csx 2 | { 3 | using System; 4 | using System.IO; 5 | using System.Reflection; 6 | 7 | public class Skaffolder 8 | { 9 | public void InitializerFolder() 10 | { 11 | string currentDirectory = Directory.GetCurrentDirectory(); 12 | string vsCodeDirectory = Path.Combine(currentDirectory, ".vscode"); 13 | if (!Directory.Exists(vsCodeDirectory)) 14 | { 15 | Directory.CreateDirectory(vsCodeDirectory); 16 | } 17 | 18 | string pathToLaunchFile = Path.Combine(vsCodeDirectory, "launch.json"); 19 | if (!File.Exists(pathToLaunchFile)) 20 | { 21 | string baseDirectory = Path.GetDirectoryName(new Uri(typeof(Skaffolder).GetTypeInfo().Assembly.CodeBase).LocalPath); 22 | string csxPath = Path.Combine(baseDirectory, "csx.exe").Replace(@"\","/"); 23 | string lauchFileTemplate = ReadResourceFile("launch.json.template"); 24 | string launchFileContent = lauchFileTemplate.Replace("PATH_TO_CSX", csxPath); 25 | WriteFile(pathToLaunchFile, launchFileContent); 26 | } 27 | 28 | string pathToTasksFile = Path.Combine(vsCodeDirectory, "tasks.json"); 29 | if (!File.Exists(pathToTasksFile)) 30 | { 31 | string taskFileTemplate = ReadResourceFile("tasks.json.template"); 32 | WriteFile(pathToTasksFile, taskFileTemplate); 33 | } 34 | 35 | string pathToOmniSharpJson = Path.Combine(currentDirectory, "omnisharp.json"); 36 | if (!File.Exists(pathToOmniSharpJson)) 37 | { 38 | var omniSharpFileTemplate = ReadResourceFile("omnisharp.json.template"); 39 | WriteFile(pathToOmniSharpJson, omniSharpFileTemplate); 40 | } 41 | 42 | CreateNewScriptFile("helloworld.csx"); 43 | } 44 | 45 | public void CreateNewScriptFile(string file) 46 | { 47 | string currentDirectory = Directory.GetCurrentDirectory(); 48 | var pathToScriptFile = Path.Combine(currentDirectory, file); 49 | if (!File.Exists(pathToScriptFile)) 50 | { 51 | var scriptFileTemplate = ReadResourceFile("script.csx.template"); 52 | WriteFile(pathToScriptFile, scriptFileTemplate); 53 | } 54 | } 55 | 56 | private static string ReadResourceFile(string name) 57 | { 58 | var resourceStream = typeof(Skaffolder).GetTypeInfo().Assembly.GetManifestResourceStream($"csx.Templates.{name}"); 59 | using (var streamReader = new StreamReader(resourceStream)) 60 | { 61 | return streamReader.ReadToEnd(); 62 | } 63 | } 64 | 65 | private void WriteFile(string path, string content) 66 | { 67 | using (var fileStream = new FileStream(path,FileMode.Create)) 68 | { 69 | using (var streamWriter = new StreamWriter(fileStream)) 70 | { 71 | streamWriter.Write(content); 72 | } 73 | } 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /src/Templates/csproj.template: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp1.1 5 | win7-x64;win7-x86;osx.10.11-x64;ubuntu.14.04-x64;ubuntu.16.04-x64;centos.7-x64;rhel.7.2-x64;debian.8-x64;fedora.23-x64;opensuse.13.2-x64 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Templates/launch.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": ".NET Core Launch (console)", 6 | "type": "coreclr", 7 | "request": "launch", 8 | "program": "PATH_TO_CSX", 9 | "args": ["${file}"], 10 | "cwd": "${workspaceRoot}", 11 | "stopAtEntry": false, 12 | "console": "internalConsole", 13 | "requireExactSource": false 14 | } 15 | ] 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/Templates/omnisharp.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "script": { 3 | "enableScriptNuGetReferences": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/Templates/script.csx.template: -------------------------------------------------------------------------------- 1 | #! "netcoreapp1.1" 2 | #r "nuget:NetStandard.Library,1.6.1" 3 | 4 | Console.WriteLine("Hello world!"); -------------------------------------------------------------------------------- /src/Templates/tasks.json.template: -------------------------------------------------------------------------------- 1 | // A task runner that calls the csx script runner and 2 | // executes the current opened file. 3 | { 4 | "version": "0.1.0", 5 | 6 | "command": "csx.exe", 7 | 8 | // The command is a shell script 9 | "isShellCommand": true, 10 | 11 | // Show the output window only if unrecognized errors occur. 12 | "showOutput": "silent", 13 | 14 | // args is the current file to compile 15 | "args": ["${file}"], 16 | 17 | // use the standard tsc problem matcher to find compile problems 18 | // in the output. 19 | "problemMatcher": "$msCompile" 20 | } -------------------------------------------------------------------------------- /src/anotherscript.csx: -------------------------------------------------------------------------------- 1 | //#r "nuget:System.Linq/4.3.0" 2 | 3 | //#r "nuget:AutoMapper/6.0.2" 4 | //#r "nuget:Newtonsoft.Json/10.0.1" 5 | //#r "nuget:System.Runtime.Serialization.Formatters/4.3.0" 6 | //#r "nuget:System.ComponentModel.TypeConverter/4.0.0" 7 | //using Newtonsoft.Json; 8 | //using AutoMapper; 9 | using System; 10 | using System.Text.RegularExpressions; 11 | using System.Linq; 12 | 13 | public static void Test() 14 | { 15 | Console.WriteLine("hello!"); 16 | } 17 | 18 | 19 | 20 | 21 | //var regex = new Regex(@"nuget:(.+)\/(\d+\.\d+\.\d+)", RegexOptions.IgnoreCase); 22 | 23 | //var test = new { hi = "i'm json!" }; 24 | //Console.WriteLine(JsonConvert.SerializeObject(test)); 25 | 26 | //Console.WriteLine(typeof(MapperConfiguration)); 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/csx.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | netcoreapp1.1 5 | win7-x64;win7-x86;osx.10.11-x64;ubuntu.14.04-x64;ubuntu.16.04-x64;centos.7-x64;rhel.7.2-x64;debian.8-x64;fedora.23-x64;opensuse.13.2-x64 6 | 1.0.0-beta8 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/csx.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26228.9 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "csx", "csx.csproj", "{B4311345-31BC-477C-B2E2-4A9EBB1D2AF4}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {B4311345-31BC-477C-B2E2-4A9EBB1D2AF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {B4311345-31BC-477C-B2E2-4A9EBB1D2AF4}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {B4311345-31BC-477C-B2E2-4A9EBB1D2AF4}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {B4311345-31BC-477C-B2E2-4A9EBB1D2AF4}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /src/csx.sln.DotSettings.user: -------------------------------------------------------------------------------- 1 |  2 | ForceIncluded 3 | 4 | True 5 | (Doc Ln 135 Col 0) 6 | F300DC1B-E643-4CCB-80EE-A7F840B07500/f:NuGet.cs 7 | NumberedBookmarkManager -------------------------------------------------------------------------------- /src/samplescript.csx: -------------------------------------------------------------------------------- 1 | #! "netcoreapp1.1" 2 | //#r "nuget:NetStandard.Library,1.6.1" 3 | #r "nuget:Microsoft.EntityFrameworkCore.SqlServer,1.1.2" 4 | // #r "nuget:System.Data.Common,4.3.0" 5 | //#r "nuget:System.Data.SqlClient,4.1.0" 6 | 7 | using System.Linq; 8 | using Microsoft.EntityFrameworkCore; 9 | using System.Data.Common; 10 | 11 | // throw new Exception(); 12 | return; 13 | public class TempContext : DbContext 14 | { 15 | public DbSet Logs { get; set; } 16 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseSqlServer(@"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\Users\bri\AppData\Local\Microsoft\Microsoft SQL Server Local DB\Instances\MSSQLLocalDB\TestBase.mdf;Integrated Security=True"); 17 | } 18 | 19 | public class Log 20 | { 21 | public int Id { get; set; } 22 | public string Value { get; set; } 23 | } 24 | var context = new TempContext(); 25 | var logQuery = context.Logs.Where(l => l.Id == 1).ToList(); 26 | WriteLine(logQuery); 27 | WriteLine("sdfsdf"); --------------------------------------------------------------------------------