Giter Club home page Giter Club logo

scripty's Introduction

Please note: it's been a while since a Scripty release. I'm not entirely sure when (or if) development on this project will resume. While I would love to pick up development here again at some point, I don't know if it makes sense to dedicate any more time given that the Roslyn team has committed to ship source generators as part of .NET 5.

In the meantime some other projects in this area include:

Also note that the Scripty Visual Studio extension has been removed from the Visual Studio Extension Gallery due to reports of incompatibilities with the most recent versions of Visual Studio. It will be replaced when the next release comes out, but in the meantime you can download it directly from the GitHub version 0.7.4 release page.


Tools to let you use Roslyn-powered C# scripts for code generation. You can think of it as a scripted alternative to T4 templates.

Note that this document corresponds to the currently active source code branch, which may be in development. To view documentation for the latest release of Scripty make sure you are viewing the master branch.

Quick Start

There are two different ways to use Scripty, and they can be used together or separately depending on your use case.

The first is to use the "custom tool" functionality contained in the Scripty Visual Studio extension. Just add that extension to Visual Studio and it's ready to go. Once the extension is installed, you have to "turn it on" for specific files. You do this by manually entering ScriptyGenerator as the "Custom Tool" for a file or files in the project (presumably .csx files, though it'll work for any extension). The "Custom Tool" setting can be found in the file properties in Visual Studio. Once that's set, just right-click on the file and select "Run Custom Tool". Note that the scripts will not be evaluated on every build automatically using this approach.

The other way to use Scripty is with the MSBuild task. In this case, add the Scripty.MsBuild NuGet package to your project. It will automatically pick up any .csx files in your project at build time, evaluate them, and add the results to your compilation. You won't actually see the results in your project. If you want the output of the scripts to show up in the project after using the MSBuild task, you'll need to manually add them after the first build.

I'd recommend looking at the Scripty Visual Studio extension first since it's more obvious when the generation happens and you can immediately see the results. Then once you're comfortable, maybe experiment with the build task. You can also use both together to run code generation on-demand with the custom tool and also on every build with the MsBuild task. Also note that you shouldn't need to install the Roslyn SDK or manually add any of the Roslyn packages or projects to the solution. Everything is self contained in the Scripty extensions and packages.

Scripts

Scripty scripts are just standard Roslyn C# scripts with some special global properties to make them suitable for powering code generation. All the standard C# scripting conventions still apply such as using the #r preprocessor directive to load assemblies and the #load directive to load external script files. They are also generally given .csx extensions just like normal C# scripts. This makes it easy to bootstrap evaluating them outside Scripty if you need to, providing whichever Scripty globals you use in the script yourself.

It is now also possible to use the #load directive to load c# files (.cs). This makes it convenient to use intellisense when writing files to be used by Scripty. An example of this usage can be seen below.

The following references are added to every script by default:

  • Microsoft.CodeAnalysis.Workspaces
  • Microsoft.Build
  • Scripty.Core

The following namespaces are imported to every script by default:

  • System
  • Scripty.Core
  • Scripty.Core.Output
  • Scripty.Core.ProjectTree

Script File Extension

By default, Scripty looks for files with a .csx extension. This is the same extension used by standard C# scripts and there are a couple reasons why an alternate extension wasn't chosen:

  • .csx is already recognized by Visual Studio and other tooling as a Roslyn/scriptcs script and so syntax highlighting, Intellisense, etc. will automatically light up. While this could be handled in Visual Studio for a different extension, there are lots of other tools like Visual Studio Code that also have this recognition built-in.
  • While all Scripty scripts are not necessarily generic C# scripts because of the global properties (see below), those that don't make use of the global properties will also work with a normal C# script runner such as Roslyn or scriptcs out of the box. In addition, all normal C# scripts are also Scripty scripts. This provides some nice interoperability when you want to design a script that is both usable from Scripty and from a C# script runner. Of course, you could specify a different extension for a script when sending it to the Roslyn or scriptcs script runner, but why make it more complicated?

If you don't like this behavior, it's easy to change the extension Scripty uses. For the MSBuild task you can change the extension or only process a subset of files by adding an ItemGroup item named ScriptyFile and including whatever files you want the task to process. The custom tool is opt-in to begin with since you have to set the custom tool property. You can have the custom tool process any file, .csx extension or not, by setting the Custom Tool property.

Global Properties

The following global properties are available when evaluating your script with Scripty:

  • Context

    This is a reference to the global ScriptContext class that holds each of the properties described below. You can use it to pass into a method that needs access to the full context. To put it another way, this property holds a class that contains all of the global properties in one object. This can be useful because classes and methods in the script are "lifted" and don't have access to the same global properties unscoped code does.

    WritePaths(Context);
    
    public void WritePaths(ScriptContext context)
    {
      context.Output.WriteLine($"Script File: {context.ScriptFilePath}");
      context.Output.WriteLine($"Project File: {context.ProjectFilePath}");
    }
  • ScriptFilePath

    The full path to the currently evaluating script.

  • ProjectFilePath

    The full path to the current project file.

  • Project

    A leaky abstraction over both the MSBuild API and the Roslyn Workspaces API. This object allows you to traverse a hierarchical model of the project, providing access to the MSBuild ProjectItem and Roslyn Document for any item. This property is a ProjectRoot that exposes other ProjectNode objects by implementing IReadOnlyDictionary<string, ProjectNode> (or you can also access child nodes via the Children property).

    • Analysis

      A Roslyn Microsoft.CodeAnalysis.Project that represents the current project. You can use this to access the files in the project as well as other information, including getting the compilation for the project. For example, this script will output comments with the path of each source file in the project:

      using Microsoft.CodeAnalysis;
      
      foreach(Document document in Project.Analysis.Documents)
      {
          Output.WriteLine($"// {document.FilePath}");
      }

      The documentation on Roslyn and specifically the Workspace API isn't great right now. The Roslyn source code browser is a good place to start.

    • Build

      A Microsoft.Build.Evaluation.Project. This API and the Roslyn Workspaces API exposed in the Analysis property have overlapping functionality, but are also complementary. While the Roslyn Workspaces API is more focused on the compilation, the MSBuild API is more focused on the makeup of the project file. Similar to the example for the Roslyn API, this script will output comments with the path of each source file in the project using the MSBuild API:

      using Microsoft.Build.Evaluation;
      
      foreach(ProjectItem item in Project.Build.Items.Where(x => x.ItemType == "Compile"))
      {
        Output.WriteLine($"// {item.EvaluatedInclude}");
      }
    • Children

      A IReadOnlyDictionary<string, ProjectNode> collection that contains the children of the current node.

    • Parent

      The parent ProjectNode.

    • Name

      The name of this node (folder or file name). This is string.Empty for the root node.

    • ProjectItem

      The MSBuild Microsoft.Build.Evaluation.ProjectItem. This is null for the root node.

    • Document

      The Roslyn Microsoft.CodeAnalysis.Document if there is one. Not all project items have a Roslyn document since Roslyn generally only supports source code files at this time. This is null for the root node.

  • Output

    A thin wrapper around TextWriter that should be used to output generated content. Using this object instead of direct file writing mechanisms ensures that Scripty can keep track of which files were generated and pass that information back to the build process as needed. By default, a file with the same name as the script but with a .cs extension is output. A handy pattern is to use script interpolation along with verbatim strings to output large chunks of code at once:

    string propertyName = "Bar";
    Output.WriteLine($@"
    class Foo
    {{
      int {propertyName} => 42;
    }}");

    Given the above script named script.csx, the following generated code will be output to script.cs:

    class Foo
    {
      int Bar => 42;
    }

    You can output more than one file by using the Output indexer.

    Output["other.cs"].WriteLine("// Another file");
    • FilePath

      Gets the currently set file path for the output file.

    • SetFilePath(string filePath)

      Changes the file path for the main output file.

    • SetExtension(string extension)

      Sets the file path for the main output file to the same path as the script with the specified extension. Note that if an alternate file path is specified using SetFilePath(), this method will ignore that and will base the new file path on the script file path.

    • BuildAction

      You can also control the build action for the generated file using the BuildAction property. By default, any output file that ends in .cs is compiled and all others are included in the project but not compiled (I.e., a build action of "None"). The BuildAction property takes the following enum:

      public enum BuildAction
      {
        /// <summary>
        /// Only generates the file but does not add it to the project.
        /// </summary>
        GenerateOnly,
      
        /// <summary>
        /// Adds the file to the project and does not set a build action.
        /// </summary>
        None,
      
        /// <summary>
        /// Adds the file to the project and sets the compile build action.
        /// </summary>
        Compile,
      
        /// <summary>
        /// Adds the file to the project and sets the content build action.
        /// </summary>
        Content,
      
        /// <summary>
        /// Adds the file to the project and sets the embedded resource build action.
        /// </summary>
        EmbeddedResource
      }

      For example, to set the build action for the default generated file and an alternate generated file you would write:

      Output.BuildAction = BuildAction.None;
      Output["embedded.xml"].BuildAction = BuildAction.EmbeddedResource;

Libraries

Scripty support is provided via a variety of libraries (and corresponding NuGet packages) for different scenarios.

Scripty.MsBuild

This installs an MsBuild task into your project that will evaluate script files on each build.

By default, all files in the project with the .csx extension are evaluated. You can customize this with the ScriptyFiles ItemGroup (for example, if you have .csx files that aren't part of code generation or that you intend to load with #load and thus shouldn't be evaluated directly):

<ItemGroup>
    <ScriptyFiles Include="codegen.csx" />
</ItemGroup>

Files that get generated using the MsBuild task are included during compilation (as long as their Compile flag is set in the script), but not in the project (unless your script modifies the project to include them). If you'd like to have them in the project as well (for example, to enable Intellisense) just include them manually after the first generation. You may want to also commit an empty placeholder file to any shared repository if you do include generated files in the project so that people obtaining the project won't get a missing file prior to their first build.

By default, the MSBuild task will cause the build to fail if there are any problems evaluating scripts. You can override this behavior and continue with the build on errors by placing the following in your project file:

<PropertyGroup>
  <ScriptyContinueOnError>true</ScriptyContinueOnError>
</PropertyGroup>

Scripty.CustomTool

This library provides a single file generator (otherwise known as a custom tool) in Visual Studio that can be used to evaluate Scripty scripts whenever the underlying script file changes. Unlike Scripty.MsBuild, generated output from the scripts will get automatically included in the project. This library and Scripty.MsBuild can be used together to provide script evaluation on changes and on build.

The generator is provided as a Visual Studio extension and you can install it from the gallery (just search for "Scripty"). To use it, set the "Custom Tool" property for any .csx file to "ScriptyGenerator". After setting the custom tool. Scripty will automatically run whenever the underlying script is saved or when you right-click the script and select "Run Custom Tool."

Scripty

A console application that can evaluate Scripty scripts. This can be used to integrate Scripty into alternate build systems. It's also used by Scripty.MsBuild to evaluate scripts outside the process space of the currently running build.

>Scripty.exe --help
usage:  <ProjectFilePath> <ScriptFilePaths>...

    <ProjectFilePath>       The full path of the project file.
    <ScriptFilePaths>...    The path(s) of script files to evaluate (can
                            be absolute or relative to the project).

Scripty.Core

This library is the foundation of Scripty and can be used if you want to embed Scripty evaluation. It lets you run the Scripty engine directly, supplying any data needed to set up the special global properties like the current project path.

Cake.Scripty

A Cake addin for Scripty that allows you to evaluate Scripty scripts in Cake. The recommended approach is to use the Scripty.MsBuild library so that you also get Scripty evaluation when building from Visual Studio, If you are calling MsBuild from Cake (which most Cake scripts do) this addin is not needed. However, this addin lets you integrate Scripty evaluation into Cake in situations when you want to completely replace MsBuild.

When using the addin it is important to include both the addin and the Scripty tool. To use the addin call Scripty constructor and then chain the Evaluate method off it.

The constructor takes an string for the project file and an optional ScriptySettings which inherits from ToolSettings with no extra settings added. The Evaluate method takes a param list of script files to process. A simple Cake file would look like:

#addin nuget:?package=Cake.Scripty
#tool nuget:?package=Scripty

var target = Argument("target", "Default");

Task("Default")
.Does(() => {
    Scripty("Project.csproj", new ScriptySettings()
        {
            WorkingDirectory = "./OptionalWorkingDirectory"
        })
        .Evaluate("Script1.csx", "Script2.csx", "Script3.csx");
});

RunTarget(target);

Sample Usage:

Within a Solution and given a script file (TestScript.csx)

//TestScript.csx
#load "TestCSharpFile.csx.cs"
var sc = new ScriptContainer(Context);
await sc.OutputProjectStructure();

And c# file (TestCSharpFile.csx.cs). The .cs extension is critical

//TestCSharpFile.csx.cs
using Microsoft.CodeAnalysis;
using Scripty.Core;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace ScriptContainer
{
    public class ScriptContainer
    {
        private readonly string _documentStructureFile;
        private readonly string _descriptionFile;
        private readonly ScriptContext _context;

        public ScriptContainer(ScriptContext context)
        {
            _documentStructureFile = "GeneratedFile.cs";
            _descriptionFile = "Description.txt";
            _context = context;
        }

        public async Task OutputProjectStructure()
        {
            _context.Output[_descriptionFile].BuildAction = Scripty.Core.Output.BuildAction.GenerateOnly;
            _context.Output[_documentStructureFile].BuildAction = Scripty.Core.Output.BuildAction.Compile;
            _context.Output[_descriptionFile].WriteLine($"I am a text file generated by a scripty template");

            var task = Task.Run(async () =>
            {
                return await _context.Project.Analysis.GetCompilationAsync();
            });
            var c = task.Result;

            //Get NameSpace tp Enumerate / process
            var zAddressNamespaceSymbol = c.GlobalNamespace.GetNamespaceMembers().Single(ns => ns.Name == "MyRootNameSpace");

            RecurseCompilationSymbol(zAddressNamespaceSymbol, 1);
        }

        public void RecurseCompilationSymbol(ISymbol compilationSymbol, int level)
        {
            level++;
            if (compilationSymbol is INamespaceOrTypeSymbol)
            {
                WriteDetailLine(level, compilationSymbol.Kind, compilationSymbol.Name, "");
                foreach (var member in ((INamespaceOrTypeSymbol)compilationSymbol).GetMembers())
                {
                    RecurseCompilationSymbol(member, level);
                }
            }
            else if (compilationSymbol is IPropertySymbol)
            {
                WriteDetailLine(level, compilationSymbol.Kind, compilationSymbol.Name,
                    ((IPropertySymbol) compilationSymbol).GetMethod.ReturnType.Name);
            }
            else
            {
                WriteDetailLine(level, compilationSymbol.Kind, compilationSymbol.Name,"");
            }
        }

        private void WriteDetailLine(int level, SymbolKind symbolKind, string symbolName, string symbolReturnType)
        {
            var symbolKindName = Enum.GetName(typeof(SymbolKind), symbolKind);

            _context.Output[_documentStructureFile].WriteLine($"// {GetStringOfSpaces(level)} {symbolKindName} {symbolName} {symbolReturnType}");
        }

        public static string GetStringOfSpaces(int number)
        {
            number = number * 4;
            string s = "";
            for (int i = 0; i < number; i++)
            {
                s = s + " ";
            }
            return s;
        }    
    }
}

We would have an output of 2 files. Namely "DescriptionText.txt" which will contain the text specified, and "GeneratedFile.cs" which will contain a listing of all namespaces, classes and their properties and methods.

Help!

If you need help, have a feature request, or notice a bug, just submit a GitHub issue.

scripty's People

Contributors

agc93 avatar ap0llo avatar crash-dive avatar daveaglick avatar herdo avatar maartenx avatar nillis avatar phillipsj avatar reduckted avatar stingyjack avatar thebigb avatar tylerbrinkley avatar zanetdev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

scripty's Issues

Brainstorming

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.

Make OutputFile API Fluent

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("}");

Trying to call a method of a class from Project.Analysis.Documents

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

Fail build if MsBuild fails

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.

Add WriteNamespace() and GetNamespace() methods

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.

Automatically find Roslyn DLLs for scripts.

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.

Add arguments into global context by passing them via MSBuild

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.

Diificulty calling SemanticModel.GetDeclaredType etc

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.

Project.Build for Scripty.MsBuild doesn't preserve property values

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.

dotnet CLI

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?

Support for NuGet package.config and/or package restore for references.

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" />

Add Empty Scripty File Item Template

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.

Generate dependent items on arbitrary solution files with one master csx file. eg SQL code wrapper

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:

  • native VS support is non existent (resharper helps)
  • generated code formatting is hard to get right (T4 escapes code and renders everything else as output vs csx is code and explicitly renders output)
  • spaghetti code
  • unreadable and hard to follow
  • it's not Scripty

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:

  • Set the CustomTool property in the file project properties window to "ScriptyGenerator"
  • The Visual Studio Extension adds additional field to Properties window to specify path to csx file
  • csx file is automatically executed when ever referenced file is saved
  • generated code is attached as nested item on referenced file

Desired project structure:
image
where the file name Sample.generated.cs is under control of the script

Desired file properties window:
image

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.

Discussion: Generic Scripty.Extension namespace & classes

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.

Example:

OutputFile.Use<IIndentionExtension>()
          .Use<IPascalCasingExtension>() // Just an example!
          ...

Calling OutputFile.WriteLine("myText") would invoke the IIndentionExtension, then the IPascalCasingExtension:

  1. "myText"
  2. " myText"
  3. " MyText"
  4. and so on...

Configuring the extensions

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.

VS2015 integration

I've given try to VS extension and Scripty.MSBuild. What I've observed:

  • Simply installing VS extension and adding new file with extension "csx", does not result in solution explorer custom icon file and do not automatically set Custom Tool.
  • After that adding NuGet Scripty.MSBuild does not result in adding file to solution.
  • Settings manually custom tool in properties in VS2015, results in attaching generated file to solution explorer.
    Is that expected behavior?

Fluent indention methods

A fluent indention API should be introduced.

Background:

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.

Example:

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("}");

Fluent indention API

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)

StackOverflowException while running Scripty from CLI

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.

Example Wrong

int propertyName = "Bar";
Output.WriteLine($@"
class Foo
{{
int {propertyName} => 42;
}}");

It should be

string propertyName = "Bar";

Also any plans on adding more samples ?

Ability to write raw bytes to files

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.

System Requirments

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:

  • Before .Net 4.6 installed:
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[])

Adds checks to make sure output isn't added by both MsBuild task and VS extension

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:

  • created a new project and reference the Scripty.MsBuild Nuget package (I could not find a Nuget package for Scripty.CustomTool)
  • add a file called 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; 
}}");
  • change the custom tool of the file to use the ScriptyGenerator
  • build the project

There is a warning available in the Error list window. Is this fixable?

Thanks

ScriptyGenerator needs to auto-checkout generated files in TFS

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.Script1.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()

Documentation: Add processing info graphic

Given the following graphic:
black magic

It would be nice to know more about the "black magic" in the middle.

  • Where are my *.csx files coming in?
  • What is "injected" (as globals) into those files?
  • If not via the "Run Custom Tool" trigger, what's creating my list of files
  • etc.

The explanation should be in a graphic, displaying the different steps being executed - from the trigger (grey box) to the final file (green file).

Add Support To Change The File Extension

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.

Add Option To Prevent Generating Log

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.

Add an item template to Visual Studio

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.

Move ScriptContext and dependencies to own library/package

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.

Add support for MSBuild project model

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.

Clarify installation and first-use instructions (especially VS extension vs. NuGet package)

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.

Consider use of an analyzer to kick-off code gen

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?

Mix "raw" output and generated code

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.

InvalidOperationException when loading build project

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!

Can an assembly reference dynamically use the Solution configuration (Debug/Release)?

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.

.NET Core Support

Scripty works with Visual studio code (.NET Core) as T4 cross platform alternative or not ?

No IntelliSense on Scripty objects like Context

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.

VS 2015 Extension fails to run

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."

:(

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.