• This repository has been archived on 12/Dec/2020
  • Stars
    star
    408
  • Rank 102,081 (Top 3 %)
  • Language
    C#
  • License
    Microsoft Public ...
  • Created over 8 years ago
  • Updated over 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Assists in performing Roslyn-based code generation during a build.

Roslyn-based Code Generation

Build Status GitHub Actions CI status

πŸ›‘ This project is no longer maintained now that Roslyn offers source generators which offer superior performance, reliability, and IDE integration. See #229 for a discussion leading up to this decision. Existing generators based on this project should be easily portable to Source Generators just by changing the wrapping and keeping all the syntax tree generating code. For an example migration, see refactor: Use .NET 5 Source Generators PR.


Assists in performing Roslyn-based code generation during a build. This includes design-time support, such that code generation can respond to changes made in hand-authored code files by generating new code that shows up to Intellisense as soon as the file is saved to disk.

See who's generating code or consuming it using CodeGeneration.Roslyn.

Instructions on development and using this project's source code are in CONTRIBUTING.md.

Packages

Package Latest Preview Description
CodeGeneration.Roslyn.Tool NuGet package NuGet preview package Tool that loads Plugins and MSBuild targets that run it during build.
CodeGeneration.Roslyn.Templates NuGet package NuGet preview package Templates for dotnet new that help create Plugins items and projects.
CodeGeneration.Roslyn NuGet package NuGet preview package API for generators to build against.
CodeGeneration.Roslyn.Attributes NuGet package NuGet preview package Attributes to annotate plugin attributes with.
CodeGeneration.Roslyn.Engine NuGet package NuGet preview package Engine called by Tool; useful for testing generators.
CodeGeneration.Roslyn.Plugin.Sdk NuGet package NuGet preview package MSBuild project Sdk that facilitates correct Plugin project setup.
CodeGeneration.Roslyn.PluginMetapackage.Sdk NuGet package NuGet preview package MSBuild project Sdk that facilitates correct Plugin metapackage project setup.

Table of Contents

How to write your own code generator

In this walkthrough, we will define a code generator that replicates any class (annotated with our custom attribute) with a suffix (specified in the attribute) appended to its name.

Prerequisites

Use template pack

To install the template pack, run:

dotnet new -i CodeGeneration.Roslyn.Templates

You'll then have our template pack installed and ready for use with dotnet new. For details see templates Readme.

Prepare a directory where you want to create your Plugin projects, e.g. mkdir DemoGeneration. Then, in that directory, create the set of Plugin projects (we add the --sln to create solution as well):

dotnet new cgrplugin -n Duplicator --sln

This will create 3 ready-to-build projects. You can now skip through the next steps of creating and setting up projects, just apply the following changes to have the same content:

  • rename Duplicator.Generators/Generator1.cs to DuplicateWithSuffixGenerator.cs
    • also replace the DuplicateWithSuffixGenerator class with the following:
      public class DuplicateWithSuffixGenerator : ICodeGenerator
      {
          private readonly string suffix;
      
          public DuplicateWithSuffixGenerator(AttributeData attributeData)
          {
              suffix = (string)attributeData.ConstructorArguments[0].Value;
          }
      
          public Task<SyntaxList<MemberDeclarationSyntax>> GenerateAsync(TransformationContext context, IProgress<Diagnostic> progress, CancellationToken cancellationToken)
          {
              // Our generator is applied to any class that our attribute is applied to.
              var applyToClass = (ClassDeclarationSyntax)context.ProcessingNode;
      
              // Apply a suffix to the name of a copy of the class.
              var copy = applyToClass.WithIdentifier(SyntaxFactory.Identifier(applyToClass.Identifier.ValueText + suffix));
      
              // Return our modified copy. It will be added to the user's project for compilation.
              var results = SyntaxFactory.SingletonList<MemberDeclarationSyntax>(copy);
              return Task.FromResult(results);
          }
      }
  • rename Duplicator.Attributes/Generator1Attribute.cs file to DuplicateWithSuffixAttribute.cs
    • also replace DuplicateWithSuffixAttribute class with the following:
      [AttributeUsage(AttributeTargets.Class)]
      [CodeGenerationAttribute("Duplicator.Generators.DuplicateWithSuffixGenerator, Duplicator.Generators")]
      [Conditional("CodeGeneration")]
      public class DuplicateWithSuffixAttribute : Attribute
      {
          public DuplicateWithSuffixAttribute(string suffix)
          {
              Suffix = suffix;
          }
      
          public string Suffix { get; }
      }

Define code generator

Your generator cannot be defined in the same project that will have code generated for it. That's because code generation runs before the consuming project is itself compiled. We'll start by creating the generator. This must be done in a library that targets netcoreapp2.1. Let's create one called Duplicator:

dotnet new classlib -f netcoreapp2.1 -o Duplicator.Generators

Now we'll use an MSBuild project SDK CodeGeneration.Roslyn.Plugin.Sdk to speed up configuring our generator plugin. Edit your project file and add the <Sdk> element:

<!-- Duplicator.Generators/Duplicator.Generators.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
  <!-- Add the following element above any others: -->
  <Sdk Name="CodeGeneration.Roslyn.Plugin.Sdk" Version="{replace with actual version used}" />

  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>

</Project>

This SDK will implicitly add a PackageReference to the corresponding version of CodeGeneration.Roslyn nuget, and set properties to make plugin build correctly. We can now write our generator:

⚠ Note: constructor is required to have exactly a single AttributeData parameter.

// Duplicator.Generators/DuplicateWithSuffixGenerator.cs
using System;
using System.Threading;
using System.Threading.Tasks;
using CodeGeneration.Roslyn;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Duplicator.Generators
{
    public class DuplicateWithSuffixGenerator : ICodeGenerator
    {
        private readonly string suffix;

        public DuplicateWithSuffixGenerator(AttributeData attributeData)
        {
            suffix = (string)attributeData.ConstructorArguments[0].Value;
        }

        public Task<SyntaxList<MemberDeclarationSyntax>> GenerateAsync(TransformationContext context, IProgress<Diagnostic> progress, CancellationToken cancellationToken)
        {
            // Our generator is applied to any class that our attribute is applied to.
            var applyToClass = (ClassDeclarationSyntax)context.ProcessingNode;

            // Apply a suffix to the name of a copy of the class.
            var copy = applyToClass.WithIdentifier(SyntaxFactory.Identifier(applyToClass.Identifier.ValueText + suffix));

            // Return our modified copy. It will be added to the user's project for compilation.
            var results = SyntaxFactory.SingletonList<MemberDeclarationSyntax>(copy);
            return Task.FromResult(results);
        }
    }
}

Define attribute

To activate your code generator, you need to define an attribute with which we'll annotate the class to be copied. Let's do that in a new project:

dotnet new classlib -f netstandard2.0 -o Duplicator.Attributes

Install Attributes package:

dotnet add Duplicator.Attributes package CodeGeneration.Roslyn.Attributes

Then, define your attribute class:

// Duplicator.Attributes/DuplicateWithSuffixAttribute.cs
using System;
using System.Diagnostics;
using CodeGeneration.Roslyn;

namespace Duplicator
{
    [AttributeUsage(AttributeTargets.Class)]
    [CodeGenerationAttribute("Duplicator.Generators.DuplicateWithSuffixGenerator, Duplicator.Generators")]
    [Conditional("CodeGeneration")]
    public class DuplicateWithSuffixAttribute : Attribute
    {
        public DuplicateWithSuffixAttribute(string suffix)
        {
            Suffix = suffix;
        }

        public string Suffix { get; }
    }
}

CodeGenerationAttribute is crucial - this tells the CG.R Tool which generator to invoke for a member annotated with our DuplicateWithSuffixAttribute. It's parameter is an assembly-qualified generator type name (incl. namespace): Full.Type.Name, Full.Assembly.Name

The [Conditional("CodeGeneration")] attribute is not necessary, but it will prevent the attribute from persisting in the compiled assembly that consumes it, leaving it instead as just a compile-time hint to code generation, and allowing you to not ship with a dependency on your code generation assembly.

β„Ή Of course, the attribute will persist if you define compilation symbol "CodeGeneration"; we assume it won't be defined.

Create consuming console app

We'll consume our generator in a Reflector app:

dotnet new console -o Reflector

dotnet add Reflector reference Duplicator.Attributes

dotnet add Reflector reference Duplicator.Generators

Let's write a simple program that prints all types in its assembly:

// Reflector/Program.cs
using System;

namespace Reflector
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach (var type in typeof(Program).Assembly.GetTypes())
                Console.WriteLine(type.FullName);
        }
    }
}

Now, when we dotnet run -p Reflector we should get:

Reflector.Program

Apply code generation

Applying code generation is incredibly simple. Just add the attribute on any type or member supported by the attribute and generator you wrote. We'll test our Duplicator on a new Test class:

// Reflector/Program.cs
using System;
using System.Diagnostics;
using CodeGeneration.Roslyn;
using Duplicator;

namespace Reflector
{
    [DuplicateWithSuffix("Passed")]
    class Test {}

    class Program
    {
        // ...
    }
}

Let's check our app again:

> dotnet run -p Reflector

Reflector.Program

Reflector.Test

Still nothing except what we wrote. Now all that's left is to plumb the build pipeline with code generation tool; that tool will handle invoking our Duplicator at correct time during build and write generated file, passing it into the compilation.

You'll need to add a reference to CodeGeneration.Roslyn.Tool package:

dotnet add Reflector package CodeGeneration.Roslyn.Tool

Also, you need to add the following metadata to your generator project reference: OutputItemType="CodeGenerationRoslynPlugin". This will add the path to the Duplicator.Generators.dll to the list of plugins the tool runs.

This is how your project file can look like:

<!-- Reflector/Reflector.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <!-- This ProjectReference to the generator needs to have the OutputItemType metadata -->
    <ProjectReference Include="..\Duplicator.Generators\Duplicator.Generators.csproj"
                      OutputItemType="CodeGenerationRoslynPlugin" />
    <PackageReference Include="CodeGeneration.Roslyn.Attributes"
                      Version="{replace with actual version used}" />
    <!--
      This contains the generation tool and MSBuild targets that invoke it,
      and so can be marked with PrivateAssets="all"
    -->
    <PackageReference Include="CodeGeneration.Roslyn.Tool"
                      Version="{replace with actual version used}"
                      PrivateAssets="all" />
  </ItemGroup>
</Project>

And if all steps were done correctly:

> dotnet run -p Reflector

Reflector.Program

Reflector.Test

Reflector.TestPassed

πŸ’‘ Notice that there is a TestPassed type in the assembly now.

What's even better is that you should see that new type in IntelliSense as well! Try executing Go to Definition (F12) on it - your IDE (VS/VS Code) should open the generated file for you (it'll be located in IntermediateOutputPath - most commonly obj/).

Advanced scenarios

While the sample worked, it was also unrealistically simple and skipped many complex issues that will inevitably arise when you try to use this project in real world. What follows is a deep dive into more realistic solutions.

Most of the issues are about two things: TargetFramework and dependecies. TargetFramework/TFM/TxM (e.g. netcoreapp2.1) of the generator is restricted to an exact version and there's not a lot of wiggle room there. In contrast, projects consuming generators and their outputs will target any existing TFM. If you will try to add a P2P (project-to-project) reference to a generator (targeting netcoreapp2.1) to a business model project targeting netstandard1.0, you'll get errors.

Customize generator reference

We don't need the generator as a compile reference. However, the magic OutputItemType metadata is important - it adds a path to the generator dll to the list of plugins run by the CodeGeneration.Roslyn.Tool tool. Additionally, we want to specify that there's a build dependency of the consuming project on the generator. So we modify the reference:

<!-- Reflector/Reflector.csproj -->
  <ItemGroup>
    <ProjectReference Include="..\Duplicator\Duplicator.csproj"
      ReferenceOutputAssembly="false"
      SkipGetTargetFrameworkProperties="true"
      OutputItemType="CodeGenerationRoslynPlugin" />
  </ItemGroup>

We add two new metadata attributes:

  • ReferenceOutputAssembly="false" - this causes the compilation to not add a reference to Duplicator.Generators.dll to the ReferencePath - so the source code has no dependency and doesn't know anything about that project.
  • SkipGetTargetFrameworkProperties="true" - this prevents build tasks from checking compatibility of the generator's TFM with this project's TFM.

Now we can retarget our Reflector to any TFM compatible with Attributes package (so netstandard1.0-compatible), e.g. netcoreapp2.0 - and it should run just fine.

Multitargeting generator

It can happen that your generator project will become multi-targeting. You could need to do that to use C#8's Nullable Reference Types feature in the Duplicator; the generator has to target netcoreapp2.1 as this is the framework it'll be run in by the CG.R.Tool - on the other hand, NRT feature is only supported in newer TFMs, starting with netcoreapp3.1. So you'll do:

<!-- Duplicator.Generators/Duplicator.Generators.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>netcoreapp2.1;netcoreapp3.1</TargetFrameworks>
    <PackAsCodeGenerationRoslynPlugin>$(TargetFramework.Equals('netcoreapp2.1'))</PackAsCodeGenerationRoslynPlugin>
  </PropertyGroup>
  <!-- ... -->
</Project>

β„Ή PackAsCodeGenerationRoslynPlugin is necessary to explicitly tell Plugin.Sdk which TargetFramework's build output to pack - there can be only one.

There'll be a build error, because the consumer (Reflector) doesn't know which output to use (and assign to the CodeGenerationRoslynPlugin Item). To fix that we have to use SetTargetFramework metadata. Setting it implies SkipGetTargetFrameworkProperties="true" so we can replace it.

<!-- Reflector/Reflector.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
  <!-- ... -->
  <ItemGroup>
    <ProjectReference Include="..\Duplicator\Duplicator.csproj"
      ReferenceOutputAssembly="false"
      SetTargetFramework="TargetFramework=netcoreapp2.1"
      OutputItemType="CodeGenerationRoslynPlugin" />
    <!-- ... -->
  </ItemGroup>
</Project>

Package your code generator

β„Ή If you've used cgrplugin template, you've already got metapackage project ready.

You can also package up your code generator as a NuGet package for others to install and use. A project using CodeGeneration.Roslyn.Plugin.Sdk is automatically configured to produce a correct Plugin nuget package.

Separate out the attribute

⚠ This section is deprecated since it's now the default.

The triggering attribute has to be available in consuming code. Your consumers can write it themselves, but it's not a good idea to require them do so. So we'll separate the attribute into another project that has TFM allowing all your consumers to reference it, for example netstandard1.0 - you're constrained only by what CodeGeneration.Roslyn.Attributes targets.

Let's call this new project Duplicator.Attributes.

dotnet new classlib -o Duplicator.Attributes dotnet add Duplicator.Attributes package CodeGeneration.Roslyn.Attributes dotnet add Reflector reference Duplicator.Attributes

Now, move the attribute definition from Reflector to our new project. Dont' forget to change the namespace. The app should work the same, except for not printing DuplicateWithSuffixAttribute type.

If you annotate your triggering attribute with [Conditional("...")], your consumers can make any references to the attribute package non-transient via PrivateAssets="all" and/or exclude from runtime assets via ExcludeAssets="runtime".

Create the metapackage

Your consumers will now have to depend (have PackageReference) on the following:

  • CodeGeneration.Roslyn.Tool tool
  • Duplicator.Attributes (your attributes package)
  • Duplicator.Generators (your generator/plugin package)

An example consuming project file would contain:

<!-- Reflector/Reflector.csproj -->
<ItemGroup>
  <PackageReference Include="Duplicator.Generators" Version="1.0.0" PrivateAssets="all" />
  <PackageReference Include="Duplicator.Attributes" Version="1.0.0" PrivateAssets="all" />
  <PackageReference Include="CodeGeneration.Roslyn.Tool"
                    Version="{CodeGeneration.Roslyn.Tool version}"
                    PrivateAssets="all" />
</ItemGroup>

βœ” There's a much better approach: metapackage.

For this, we'll use CodeGeneration.Roslyn.PluginMetapackage.Sdk MSBuild project SDK in a new project called simply Duplicator, which will reference our attributes:

dotnet new classlib -o Duplicator dotnet add Duplicator reference Duplicator.Attributes

Remove Class1.cs file.

Modify project file:

  • Add <Sdk> element
  • Add NupkgAdditionalDependency in ItemGroup
<!-- Duplicator/Duplicator.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
  <!-- Add the following element above any others: -->
  <Sdk Name="CodeGeneration.Roslyn.PluginMetapackage.Sdk" Version="{replace with actual version used}"/>

  <PropertyGroup>
    <!-- Declare the TargetFramework(s) the same as in your Attributes package -->
    <TargetFramework>netstandard1.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <!-- Reference your Attributes project normally -->
    <ProjectReference Include="../Duplicator.Attributes/Duplicator.Attributes.csproj" />
    <!--
      Reference your generators package by adding an item to NupkgAdditionalDependency
      with IncludeAssets="all" to flow "build" assets.
      Version used will be the PackageVersion Pack resolves,
      but you can specify Version metadata to override it.

      This is necessary to do like that, because it ensures the dependency is setup
      correctly (e.g. simple transient dependency), and skips validation of TFM (Plugin is a tool,
      it's TFM has no meaning for the consumer).
    -->
    <NupkgAdditionalDependency
        Include="Duplicator.Generators"
        IncludeAssets="all" />
  </ItemGroup>
</Project>

This project will now produce a nupkg that will not contain anything on it's own, but will "pull in" all the other dependecies consumers will need:

  • CodeGeneration.Roslyn.Tool (added implicitly by the Sdk)
  • Duplicator.Attributes
  • Duplicator.Generators

Our metapackage should be versioned in the same manner as it's dependant packages.

⚠ Please note that Metapackage project doesn't change how P2P (ProjectReference) setup works - it only works as a NuGet package!

β„Ή For a sample metapackage, see MetapackageSample.

Add extra build/ content in Plugin package

CG.R.Plugin.Sdk creates custom build/PackageId.props/targets files. If you want to add custom MSBuild props/targets into NuGet package's build folder (and have it imported when package is referenced), you'll need to use PackageBuildFolderProjectImport ItemGroup, as shown in PackagedGenerator sample.

Accesss MSBuild Properties

You may access MSBuild property values of the project being generated for, by first adding the property name to the PluginRequestedProperty item list. For example, if you want to access the TargetFramework build property, you would do the following in your generator's .csproj file:

<ItemGroup>
  <PluginRequestedProperty Include="ExampleBuildProperty" />
</ItemGroup>

Then, you can access its value like this:

public Task<SyntaxList<MemberDeclarationSyntax>> GenerateAsync(TransformationContext context, IProgress<Diagnostic> progress, CancellationToken cancellationToken)
{
    var targetFramework = context.BuildProperties["TargetFramework"];
    // ...
}

β„Ή For a sample generator that accesses MSBuild properties, see BuildPropsGenerator and its consuming project, BuildPropsConsumer

More Repositories

1

IronPigeon

IronPigeon is a decentralized communication protocol that provides high confidentiality and authenticity for the messages.
C#
261
star
2

PCLCrypto

Platform crypto for portable libraries
C#
227
star
3

ImmutableObjectGraph

Code generation for immutable types
C#
160
star
4

Validation

Method input validation and runtime checks that report errors or throw exceptions when failures are detected.
C#
127
star
5

Xunit.Combinatorial

Adds combinatorial and pairwise testing capability to Xunit tests
C#
123
star
6

Xunit.SkippableFact

Adds Xunit dynamic skipping of facts and theories.
PowerShell
121
star
7

Library.Template

A template for a NuGet package with tests, stylecop, fxcop, versioning, and Azure Pipelines build ready to go.
PowerShell
114
star
8

Xunit.StaFact

Run your xunit-based tests on an STA thread with the WPF Dispatcher, a WinForms SynchronizationContext, or even a cross-platform generic UI thread emulation with a SynchronizationContext that keeps code running on a "main thread" for that test.
C#
80
star
9

MoneyMan

A financial money management library and applications that utilize it.
C#
39
star
10

SPSS.NET

A .NET library for read/writing SPSS Data (.sav) files. This wraps the functionality exposed by the spssio32.dll native library that comes with SPSS.
C#
31
star
11

StreamJsonRpc.Sample

Sample use of StreamJsonRpc that demonstrate separate client/server processes over named pipes.
C#
28
star
12

Nerdbank.MSBuildExtension

Package to make writing MSBuild extensions easier that work both with MSBuild Core and MSBuild (Full).
C#
23
star
13

PPApiForDotNet

Write PPAPI extensions for Chromium in your favorite .NET language.
C++
20
star
14

CSharpIsNull

C# null test syntax analyzers to guard against bugs in testing null against a struct.
PowerShell
14
star
15

AssemblyRefScanner

Scans a given directory tree for interesting assembly references
C#
14
star
16

Nerdbank.Algorithms

A collection of algorithms I've implemented and found generally useful
C#
13
star
17

Pfx2Snk

Converts PFX files to SNK files.
Visual Basic
12
star
18

DotNetRepoTools

A CLI tool with commands to help maintain .NET codebases
C#
11
star
19

PInvoke.exp

C#
10
star
20

nerdbank.stylecop.rules

Custom rules for StyleCop
C#
9
star
21

Bitcoin.NET

A .NET Bitcoin library
C#
9
star
22

GuidGen

A GuidGen tool similar to the one that ships with VS, but less than a decade old.
PowerShell
8
star
23

ReadOnlySourceTree

A NuGet package that makes your project build to top-level bin and obj directories.
C#
7
star
24

PlugInServer

A network server that can do whatever the live-dropin modules tell it to (HTTP, telnet, ASP.NET, PHP, etc.)
C#
6
star
25

MessagePackDualVersions

Demonstrates how an application can run multiple incompatible versions of a library at once (MessagePack).
C#
5
star
26

Node.js.redist

Builds packages that contain Node.js that can be used to redistribute across platforms
PowerShell
5
star
27

OpenIDForSharepoint

A MembershipProvider for Sharepoint that enabled OpenID logins (forked from CodePlex). BEWARE: Read the README.txt file.
C#
5
star
28

pr-autocomplete-app

A GitHub App that adds auto-complete functionality to pull requests similar to that found in Azure Repos.
TypeScript
4
star
29

csharplibrarystarterkit

A starting point for C# library development, including logging, tests and build system
JavaScript
4
star
30

YouTubeDownloader

A CLI tool for downloading YouTube videos.
C#
4
star
31

win-buildagent

Docker image build for a windows build agent
Dockerfile
4
star
32

dotnetxri

A C# port of the OpenXri4j library
C#
4
star
33

cloudbuild-task

Contracts and adapter NPM packages for various cloud build services
TypeScript
4
star
34

JavascriptCssPackerTargets

An MSBuild-based .targets file and task assembly for minifying js and css embedded resources
C#
4
star
35

PCLCommandBase

A common base class for ICommand implementations, and a BindableBase class for your viewmodels
PowerShell
3
star
36

Nerdbank.Qif

C#
3
star
37

HttpClientEcho

Enables automated tests with HTTP calls to avoid incurring the cost and instability of network traffic by recording a "live" test run and replaying it for subsequent test runs.
C#
3
star
38

dasblog

A private dasblog playground for assembling patches for the official version on CodePlex
C#
3
star
39

StreamingMsgPackSample

A sample of streaming MessagePack from a child process to a parent process
C#
3
star
40

VSIXProjectWithPackageReferences

A sample of a VSIX project that uses PackageReference instead of packages.config. The grass is greener here.
C#
2
star
41

ClueBuddy

A program to run while playing the popular game Clue to help you deduce all the possible clues possible and win
C#
2
star
42

AsyncAndThreadingDemo.Wpf

A simple WPF that shows up async/threading patterns, including JTF
C#
2
star
43

atom2blogger

A blog-to-blog conversion utility for transferring a blog published by Atom feed to Blogger.
C#
2
star
44

Clue

A Clue board game partner to help you deduce clues. Written in Ruby.
Ruby
1
star
45

IdentitySelector

FireFox plug-in for Information Cards support
C++
1
star
46

GoogleAppEngineHiPy

The Hello World app for Google App Engine in Python
Python
1
star
47

setup-dotnet-test

C#
1
star
48

StaticPGO_Example

How to collect a static (not the dynamic one) PGO and re-use it during "dotnet publish"
C#
1
star
49

googauth

Sample .NET web site that uses Google authentication
C#
1
star
50

EnlistmentInfo

A NuGet package that imports EnlistmentInfo .props and .targets from a parent directory.
1
star
51

VSPerfDemo

A repo demonstrating a VS Package that evolves from causing UI delays to a responsive extension.
C#
1
star
52

Nerdbank.Cryptocurrencies

.NET libraries for processing Zcash and other cryptocurrencies.
C#
1
star
53

MyNHibernateContrib

C#
1
star
54

Nerdbank.Algorithms.rs

Rust
1
star
55

pr-autocomplete-scratch

Just a scratch pad for testing PR auto-complete GitHub Actions
PowerShell
1
star