daveaglick
The code for the daveaglick.com website.
Tools to let you use Roslyn-powered C# scripts for code generation
License: MIT License
The code for the daveaglick.com website.
It should be noted somewhere, that Scripty requires .Net 4.6 and (Microsoft Build Tools 2015 or Visual Studio 2015).
Probably it worth create better error messages if requirements are not met.
Right now:
error MSB4018: The "ScriptyTask" task failed unexpectedly.
System.MissingMethodException: Method not found: '!!0[] System.Array.Empty()'.
at Scripty.MsBuild.ScriptyTask.Execute()
at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
at Microsoft.Build.BackEnd.TaskBuilder.<ExecuteInstantiatedTask>d__20.MoveNext()
Before Microsoft Build Tools 2015 installed:
Console:
Unhandled Exception:
Cannot print exception string because Exception.ToString() failed.
Event Log:
Application: Scripty.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.Security.SecurityException
Exception Info: System.IO.FileLoadException
at Scripty.Core.ProjectTree.ProjectNode..ctor(Scripty.Core.ProjectTree.ProjectRoot, System.String, Scripty.Core.ProjectTree.ProjectNode, Microsoft.Build.Evaluation.ProjectItem)
at Scripty.Core.ScriptEngine..ctor(System.String)
at Scripty.Program.Run(System.String[])
at Scripty.Program.Main(System.String[])
It would be nice to know more about the "black magic" in the middle.
The explanation should be in a graphic, displaying the different steps being executed - from the trigger (grey box) to the final file (green file).
as titled, thanks!
Roslyn includes a code formatter, which could useful to run on generated code.
I have implemented it in Scripty (thebigb@6583b31). The unit test succeeds, and with the Visual Studio integration it works fine, but I can't get the command-line version to work (and I haven't tested the MSBuild integration).
The issue is that the following line throws a StackOverflowException in the CLI, and I can't figure out why.
await CSharpScript.EvaluateAsync(source.Code, options, context);
Could anyone have a look. Perhaps I'm missing something obvious.
I'm not sure how much work this would be but currently I need to manually checkout generated files for edit in TFS in order that Scripty won't fail with an UnauthorizedAccessException. Below is the stacktrace
System.UnauthorizedAccessException: Access to the path 'C:\Dev\StifelBank-EncompassFormsAndPlugins\Release\2016.06.01\Stifel.Bank.Encompass.Forms\LoanOfficerForm.config.json' is denied.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
at System.IO.StreamWriter.CreateFile(String path, Boolean append, Boolean checkHost)
at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding encoding, Int32 bufferSize, Boolean checkHost)
at System.IO.StreamWriter..ctor(String path)
at Scripty.Core.OutputFileWriter..ctor(String filePath)
at Scripty.Core.OutputFileCollection.get_Item(String filePath)
at Submission#0.<>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.CodeAnalysis.Scripting.ScriptExecutionState.d__91.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.CodeAnalysis.Scripting.Script
1.d__19.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.CodeAnalysis.Scripting.ScriptStateTaskExtensions.d__1`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
at Scripty.Core.ScriptEngine.d__4.MoveNext()
This discussion is based on this comment:
#15 (comment)
I like the idea of having a pluggable extension API for the OutputFile
.
Given the following interfaces:
public interface IExtension
{
// Might provide basics for further extensions later on
}
public interface IOutputFileExtension : IExtension
{
string ModifyOutput(string input);
}
The OutputFile
class could provide this functionality:
public OutputFile Use<TExtension>() where TExtension : IOutputFileExtension
{
// Resolve TExtension and add to internal list
}
The Write
methods could then use the "registered" extensions, chained one after another,
before writing the output to the stream writer.
OutputFile.Use<IIndentionExtension>()
.Use<IPascalCasingExtension>() // Just an example!
...
Calling OutputFile.WriteLine("myText")
would invoke the IIndentionExtension
, then the IPascalCasingExtension
:
"myText"
" myText"
" MyText"
The instance of an "activated" extension can be retrieved via a Get
method:
public TExtension Get<TExtension>()
{
// Get activated extension in OutputFile instance
}
The instance retrieved can then be used to configure the extension.
This would create a .csx
file and would pre-populate the custom tool property and would include a sample script with some boilerplate as initial content.
Explain reasoning for choosing .csx
and show how to use an alternate extension if desired. See #10 (comment)
It'd be great if you could add the ability to change Output's file extension without having to manually specify the entire filename. My use case for this is that I currently use T4 to generate json configuration settings and as such would like the output to have a .json extension.
A previous issue (#9) showed how to reference another DLL in the same solution. However, the example hardcoded the configuration type in the path to Release
.
Is there a way to dynamically reference the configuration type in the include? In T4 you can do this:
<#@ assembly name="$(SolutionDir)OtherProject\bin\$(Configuration)\OtherAssembly.dll" #>
This is difficult to search for because "Configuration" is such a generic term.
I'd love to use Scripty for some generation that i'm currently using T4 for however i'm currently hitting a DB to generate some Enumerations using the connection string pulled from the configuration.
This works fine on user machines as they will have filled in their own credentials into the App.config it would be nice if I could set an MSBuild parameter and pass that into the Scripty process via some mechanism.
Is it possible to mix "raw" output and generated parts?
I think it's a little bit painful to write an entire c# file using statements.. I'd prefer to mix a basic part written as "plain text" with parts generated.
I'm referring to something like Razor, but also T4 and any other kind of template engine (like handlebars for js) already supports it.
Needs to be capable of generating multiple files, however.
I have no idea what this would take (or if it's even possible), but sure would be helpful.
Hi,
I have a warning about a .cs file that is included twice in the project when compiling and I think it is related to the ScriptyGenerator custom tool. Here are the steps to reproduce the issue:
mapper.csx
and add this:using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Build.Evaluation;
string propertyName = "Bar";
Output.WriteLine($@"
public class Foo
{{
public int {propertyName} => 42;
}}");
There is a warning available in the Error list
window. Is this fixable?
Thanks
Is there a way to get this working with the new RTM version of dotnet CLI and ASP.NET Core (MVC)? Or is the .xproj model not supporter?
Similar to T4 parameter directives
This simple script:
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis;
using System.Linq;
var doc = Project.Analysis.Documents.First();
var model = doc.GetSemanticModelAsync().Result;
var tree = doc.GetSyntaxTreeAsync().Result;
var typeDeclarations = tree.GetRoot().DescendantNodes().OfType<BaseTypeDeclarationSyntax>();
Output.WriteLine($"//{doc.Name}");
foreach (var type in typeDeclarations)
{
var symbol = model.GetDeclaredSymbol(type);
Output.WriteLine($"// {symbol.Name}");
}
Fails with the error: "Custom tool error: Argument 2: cannot convert from 'Microsoft.CodeAnalysis.CSharp.Syntax.BaseTypeDeclarationSyntax' to 'Microsoft.CodeAnalysis.SyntaxNode'"
Which is weird since BaseTypeDeclarationSyntax eventually inherits from SyntaxNode, so it should be able to convert just fine.
type as SyntaxNode
and (SyntaxNode)type
also fail, with similar errors.
Running the entire IEnumerable through .Cast<SyntaxNode>()
works just fine however. Still, it's an annoying workaround since your various syntax elements won't always be in an IEnumerable and even when they are you may still want to get at the data in the more specific classes.
No clue if this is a Scripty issue or a Roslyn issue, but I thought it was worth mentioning. I'm wondering if it has something to do with the script not having a reference to Microsoft.CodeAnalysis.CSsarp.dll, but I don't know where it might be hiding I can't test that idea.
It may be possible to kick off code gen given certain conditions using an analyzer. I.e., user creates an object of some type, code gen immediately starts processing one or more csx files. One question is how to approach this broadly, how to tell the analyzer which conditions should trigger code gen? Or is this just a matter of documenting the approach and making sure it's possible with the analyzer implementation up to the user?
I don't know if this feasible within the Roslyn script API, but it would be nice if all #r Microsoft.CodeAnalysis.*
directives automatically resolved to the same Roslyn DLLs Visual Studio / Scripty itself are using.
int propertyName = "Bar";
Output.WriteLine($@"
class Foo
{{
int {propertyName} => 42;
}}");
It should be
string propertyName = "Bar";
Also any plans on adding more samples ?
First instinct is to create an MSBuild task that runs before compilation. Primary question is how to identify which files should be processed. If we have it run all .csx files, that might pick up other .csx files that we don't mean to process. If we use a different extension, what should it be? There's also the question of Intellisense and ability to run it out-of-band if not .csx. Maybe there's a way to designate specific .csx files to be processed (a Custom Tool or Build Action)? Custom Tool has the benefit of also being able to run automatically when the file changes and/or on right-click.
Thinking right now is to make a custom tool that does the work and then an MSBuild task that looks for this custom tool to be set on specific files and runs them if so on every build.
I have a script which requires Newtonsoft.Json - In my case it is also used by the project that contains the script but this should not be a pre-requisite.
At the moment I am using #r "..\\bin\\Debug\\Newtonsoft.Json.dll"
but this obviously only works after the build has been done once and not on a CI server. I am not currently using the project's packages path as if I update the package it will break the script.
I suggest that either a local packages.config gets parsed and restored relative to the script and using that, or a pre-processing of the csx occurs to replace package references with dll references.
e.g. #r "{Newtonsoft.Json}"
-> #r "C:\\...\\packages\\Newtonsoft.Json.8.0.3\\lib\\net45\\Newtonsoft.Json.dll"
assuming that there is a packages.config entry or MSBuild ItemGroup or other mechanism to define <package id="Newtonsoft.Json" version="8.0.3" targetFramework="net452" />
Could you add an option in Scripty on ScriptContext to prevent generating the log file? I'd rather not have them added to the solution and source control.
Installed the extension in the latest version of VS 2015 update 3. Created a blank C# console project, made a file called TestScriptGen.csx
and pasted the following code in it:
foreach (Document document in Project.Documents)
{
Output.WriteLine($"// {document.FilePath}");
}
Added the Custom Tool ScriptyGenerator
and attempted to run it - I get an error:
"The custom tool 'ScriptyGenerator' failed."
:(
Scripty works with Visual studio code (.NET Core) as T4 cross platform alternative or not ?
A fluent indention API should be introduced.
If the scripts reach a deeper nesting level, the amount of additional whitespaces in the output will be overwhelming. Most lines of the scripts will be way longer than they should be.
This one might be readable, as long as you can write a single interpolated string...
Output.WriteLine(@"MyNamespace
{
public class MyClass : IMyInterface
{
public string MyStringProperty { get; set; }
}
}");
... but the more complex the script gets...
Output.WriteLine(@"MyNamespace
{");
foreach(var enumType in enumTypesFromDatabase)
{
// One shouldn't need to write the beginning whitespaces every time:
Output.Write($" public enum {enumType.ClassName}");
if (enumType.BaseType != typeof(int))
{
Output.Write($" : {enumType.BaseType.Name}");
}
Output.WriteLine("");
// As well as those whitespaces:
Output.WriteLine(" {");
foreach(var enumMember in enumType.Members)
{
// Already now, the number of additional whitespaces is...well...
Output.WriteLine($" {enumMember.ValueName} = {enumMember.Value},");
}
// And those:
Output.WriteLine(" }");
}
Output.WriteLine("}");
The fluent indention API would a very easy thing to read:
Output.WriteLine(@"MyNamespace
{");
using (Output.Indent())
{
// Everything inside this block would be indented
Output.WriteLine(@"public class MyClass
{");
using (Output.Indent())
{
// Everything inside this block would be indented by another level
}
Output.WriteLine("}");
}
Output.WriteLine("}");
Additionally, the indention should be configurable:
Output.IndentionCharacter
- will get or set the indention character (default whitespace)
Output.IndentionRepeat
- will get or set the amount of times, the IndentionCharacer
will be repeated for each Indent()
(default 4)
I'm running the custom tool version of Scripty and I have a problem when accessing the Context.Project.Build property. The first time this is working well but when I run the script a second time I get the following exception:
System.InvalidOperationException: An equivalent project (a project with the same global properties and tools version) is already present in the project collection, with the path %PATH%. To load an equivalent into this project collection, unload this project first.
at Microsoft.Build.Shared.ErrorUtilities.ThrowInvalidOperation(String resourceName, Object[] args)
at Microsoft.Build.Evaluation.ProjectCollection.LoadedProjectCollection.AddProject(Project project)
at Microsoft.Build.Evaluation.ProjectCollection.OnAfterRenameLoadedProject(String oldFullPathIfAny, Project project)
at Microsoft.Build.Evaluation.Project.<Initialize>b__152_0(String oldFullPath)
at Microsoft.Build.Evaluation.Project.Initialize(IDictionary`2 globalProperties, String toolsVersion, String subToolsetVersion, ProjectLoadSettings loadSettings)
at Microsoft.Build.Evaluation.Project..ctor(String projectFile, IDictionary`2 globalProperties, String toolsVersion, String subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings loadSettings)
at Scripty.Core.ProjectTree.ProjectRoot.get_Build()
at Scripty.Core.ProjectTree.ProjectRoot.get_Children()
at Submission#0.Utils.FindTemplates(ScriptContext context)
at Submission#0.ProcessTemplates()
at Submission#0.<<Initialize>>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.CodeAnalysis.Scripting.ScriptExecutionState.<RunSubmissionsAsync>d__9`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.CodeAnalysis.Scripting.Script`1.<RunSubmissionsAsync>d__21.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.CodeAnalysis.Scripting.ScriptStateTaskExtensions.<GetEvaluationResultAsync>d__1`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Scripty.Core.ScriptEngine.<Evaluate>d__5.MoveNext()
I think this is because the running process is still the same when running a second time and the project is never unloaded from it.
I worked around this by loading the project by myself and calling
ProjectCollection.GlobalProjectCollection.UnloadProject(project);
when I'm done with it.
Alternativly loading the project via
ProjectCollection.GlobalProjectCollection.GetLoadedProjects(context.ProjectFilePath)
is also working well but I don't think it will outside of VS.
P.S.: T4 is dead, long live Scripty. Thanks for a really nice tool!
If processed script ended with exception, MsBuild project build should receive error message and also fail.
Now it wrote error message in console output, but it has no affect on later build.
I recently tried using scripty to create compressed files that can be embedded into an assembly. Right now I am just using File.WriteAllBytes but would like to know if scripty currently has or if it could in the future have the ability to write bytes instead of just text.
There's a new Roslyn.Microsoft.Build package that appears to offer the MSBuild libraries. This would probably be preferable to the Roslyn Workspaces API since it doesn't require actually building the project to get information about it.
Waiting for confirmation of if this is an "official" package (I.e., will it get regular updates). More specifically, will it be updated with cross-platform MSBuild libraries as those become available?
If it will be official supported, the question is what should change in Scripty? We could replace the Roslyn Workspaces global property Project
with the Project
class from Microsoft.Build.Evaluation
. This may also make it possible to run the Scripty MSBuild task in-process since it doesn't actually require a build. If the change is made, make sure to document how to get the old behavior back by manually loading the Roslyn Workspaces Project
class inside a script.
I can get the SyntaxNode for the class I want from the SemanticModel of Project.Analysis.Documents as well as the SyntaxNode for the method I want and I would like to dynamically call that method by using SemanticModel.GetOperation(methodSyntaxNode) but when I do I get an exception:
Custom tool error: System.InvalidOperationException: Feature 'IOperation' is disabled.
Is this the correct way to calling the method (a static public property actually)? How Should I do this?
-Russ
This will avoid collisions given the name is overly generic
I'm not sure if this is a general issue with Roslyn, or whether there is anything the Scripty extension can do about it in the meantime, but it's significant discouragement to getting started.
If no scripts are given to the CLI, we should proactively load the project and use the default behavior of evaluating any file with a .csx
extension.
I've given try to VS extension and Scripty.MSBuild. What I've observed:
I'm trying to create script var config = Project.Build.GetPropertyValue("Configuration");
and hope that it would be resolved into current project build configuration, but instead it is always resolved into default value "Debug".
Root cause of problem is that MsBuild task internally just call command-line utility, instead of calling it through API with passing correct MsBuild context.
I currently use T4 to generate wrapper classes to execute sql with dapper. To do this I use T4Toolbox.
This works well ok but T4 has some drawbacks:
It would be great to attach a custom tool to my *.sql
files which executes a script file to build a wrapper class that allows me to easily execute my sql
To do this I would like to:
Desired project structure:
where the file name Sample.generated.cs
is under control of the script
Desired file properties window:
This shouldn't be limited to sql
files as i can see this being useful for xml
, json
and many other file types where you would want to generate code based on some file.
I'm using the NuGet package and the file that is created by default is compiled but not added to the project (even establishing "Output.BuildAction = BuildAction.Compile;").
In Visual Studio, to prevent needing to manually specify the file extension as .csx and setting the Custom Tool to ScriptyGenerator an Empty Scripty File Item Template should be included in the Visual Studio Extension.
By the way, I'm loving Scripty so much vs T4. Now, if only we could get Intellisense on assemblies loaded with the #r directive in Visual Studio this would be the ultimate solution.
How to add reference to some assembly file in non-locking way?
Like reference from one project another one and still be able to compile the referenced one without failure?
To support chaining I believe OutputFile should implement a fluent API such that one could do the following.
Output.WriteLine("class Foo")
.WriteLine("{")
.WriteLine(" int Property => 42;")
.WriteLine("}");
Related to #36.
It would be nice if there would not be a truckload of dependencies (Roslyn and friends) when you only need the ScriptContext
type.
An issue would be the ProjectRoot
type referenced in ScriptContext
, because it has direct references to the Microsoft.CodeAnalysis
namespace. FormatterOptions
has some indirect references, but that could be solved with an interface.
The only way I see to solve this issue would be to remove ProjectRoot
from ScriptContext
, and to make it available separately in the global scope. This would be a breaking change, but perhaps a good one to make before a 1.0.0 release.
i.e. get your addin listed here:
Have a look at this PR:
This assumes that you have added XML Documentation to your addin, as described here:
http://cakebuild.net/docs/contributing/documentation
Let me know if you have any questions. Thanks!
Do we know of a way to set this in a Visual Studio project with a project.config?
I do not see a way to set the MSBuild, maybe there is a way to set this with the Scripty CLI ?
Did read this, but didn't help me identify how to get this working.
Thanks!
Really anxious to replace my T4 templates with Scripty.
I am, most likely, being thick here. But adding the NuGet package (actually, all three of them) seems to have no effect. I've added it to a regular Class Library project on .NET 4.6. I also installed the Roslyn SDK to Visual Studio, and added the Microsoft.CodeAnalysis.CSharp.Scripting project. I've never actually used any Roslyn tools so I'm sure I'm missing something obvious.
What switch do I need to flick to make it start doing its thing? My goal is to press Ctrl+Shift+B and my generated code to magically appear. Can I do that? I'd like to avoid installing the extension as I would like to be able to onboard new team members without them having to install it too.
Add a method to the Output
class, that adds the namespace to the output, depending on the current file location.
Example usage:
// This line creates a namespace, taking the location of the the file into account.
Output.CreateNamespace();
{
/* Content (classes, interfaces, enums and so on) is going inside here */
// This line creates a nested namespace, taking the existing namespace structure into account.
// The final segment of the namespace will be called "MyNestedNamespace".
Output.CreateNamespace("MyNestedNamespace");
}
Depending on the possibilities, the CreateNamespace
method should act equal to the folder structure, but take the Namespace Provider
flag of the folder into account.
I would like to use Scripty to generate TypeScript file from C# file. In other words, I need to generate a file with .ts
extension with build action set to TypeScriptCompile
. Is it possible?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.