Skip to content
This repository has been archived by the owner on Oct 21, 2020. It is now read-only.

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Ben Clive committed Jul 23, 2019
0 parents commit 457cc8b
Show file tree
Hide file tree
Showing 108 changed files with 7,481 additions and 0 deletions.
11 changes: 11 additions & 0 deletions .buildkite/premerge.definition.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
agent_queue_id: trigger-pipelines
description: Dotnet worker.
github:
branch_configuration: []
default_branch: master
pull_request_branch_filter_configuration: []
teams:
- name: Everyone
permission: BUILD_AND_READ
- name: gbu/ecosystems/online-services
permission: MANAGE_BUILD_AND_READ
53 changes: 53 additions & 0 deletions .buildkite/premerge.steps.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
linux: &linux
agents:
- "capable_of_building=online-services"
- "environment=production"
- "permission_set=builder"
- "platform=linux" # if you need a different platform, configure this: macos|linux|windows.
- "queue=v3-1561728034-9178d5adb4ab9963-------z"
timeout_in_minutes: 60 # TODO(ENG-548): reduce timeout once agent-cold-start is optimised.
retry:
automatic:
# This is designed to trap and retry failures because agent lost connection. Agent exits with -1 in this case.
- exit_status: -1
limit: 3

windows: &windows
agents:
- "capable_of_building=online-services"
- "environment=production"
- "permission_set=builder"
- "platform=windows" # if you need a different platform, configure this: macos|linux|windows.
- "queue=v3-1559053399-f2c8651809a877b4-------z"
- "scaler_version=2"
timeout_in_minutes: 60 # TODO(ENG-548): reduce timeout once agent-cold-start is optimised.
retry:
automatic:
# This is designed to trap and retry failures because agent lost connection. Agent exits with -1 in this case.
- exit_status: -1
limit: 3
# Workaround for flaky Git clones, likely due to - https://github.com/PowerShell/Win32-OpenSSH/issues/1322
- exit_status: 128
limit: 3

# NOTE: step labels turn into commit-status names like {org}/{repo}/{pipeline}/{step-label}, lower-case and hyphenated.
# These are then relied on to have stable names by other things, so once named, please beware renaming has consequences.

steps:
- label: "build-linux"
command: "ci/bk/build-linux.sh"
artifact_paths:
- "/tmp/${BUILDKITE_BUILD_ID}/**/*"
<<: *linux

- label: "test-linux"
command: "ci/bk/test-linux.sh"
artifact_paths:
- "/tmp/${BUILDKITE_BUILD_ID}/**/*"
<<: *linux

- label: "build-windows"
command: "powershell -NoProfile -NonInteractive -File ci/bk/build-win.ps1"
artifact_paths:
- "/tmp/${BUILDKITE_BUILD_ID}/**/*"
<<: *windows
21 changes: 21 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
root = true

[*]
indent_style = space
indent_size = 4
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.json]
indent_size = 2

[*.schema]
indent_size = 2

[*.{targets,props,csproj}]
indent_size = 2

# CSharp formatting settings:
[*.cs]
csharp_space_after_cast = true
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto
25 changes: 25 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Tools and IDEs
bin/
obj/
logs/
.vs/
.vscode/
.idea/
_ReSharper.Caches/
*.pubxml
*.user
launchSettings.json

# Generated code
GeneratedCode/gen/
CSharpCodeGenerator/CSharpCodeGenerator.Test/gen/

# Ignore Worker SDK files that are downloaded in place in order to create Nuget packages.
Improbable/WorkerSdkInterop/Improbable.WorkerSdkInterop/lib
Improbable/WorkerSdkInterop/Improbable.WorkerSdkInterop/runtimes
Improbable/Schema/Improbable.Schema.Compiler/tools
Improbable/Stdlib/Improbable.Stdlib.Schema/schema
Improbable/Test/Improbable.Test.Schema/schema

# Local nuget packages source
nupkgs/
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Platforms>x64</Platforms>
<Platform Condition="'$(Platform)' == ''">x64</Platform>
<LangVersion>latest</LangVersion>
<SelfContained>false</SelfContained>
</PropertyGroup>

<ItemGroup>
<Compile Remove="gen\**" />
<EmbeddedResource Remove="gen\**" />
<None Remove="gen\**" />
</ItemGroup>

<!-- Input items for Improbable.Schema.Compiler -->
<ItemGroup>
<SchemaInputDir Include="$(MSBuildProjectDirectory)\schema" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Improbable.Stdlib" Version="0.0.1-preview" />
<PackageReference Include="Improbable.Stdlib.Schema" Version="13.8.2" />
<PackageReference Include="Improbable.Test.Schema" Version="13.8.2" />
<PackageReference Include="Improbable.WorkerSdkInterop" Version="13.8.2" />
<PackageReference Include="Improbable.Schema.Compiler" Version="13.8.2" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="Npgsql" Version="4.0.7" />
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
</ItemGroup>
</Project>
25 changes: 25 additions & 0 deletions CSharpCodeGenerator/CSharpCodeGenerator.Test/schema/csharp.schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package improbable.test.csharp;

// This file exists to ensure the C# code generators handle fields and events that
// overlap with reserved C# keywords.

type KeywordsReservedType {
int64 while = 1;
string for = 2;
}

component KeywordsReservedComponent {
id = 200001;

int64 var = 1;
string do = 2;

KeywordsReservedType private = 3;
list<KeywordsReservedType> protected = 4;
map<KeywordsReservedType, KeywordsReservedType> public = 5;
option<KeywordsReservedType> static = 6;

event KeywordsReservedType while;

command KeywordsReservedType volatile(KeywordsReservedType);
}
20 changes: 20 additions & 0 deletions CSharpCodeGenerator/CSharpCodeGenerator/CSharpCodeGenerator.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<ApplicationIcon />
<OutputType>Exe</OutputType>
<StartupObject />
<LangVersion>latest</LangVersion>
<SelfContained>false</SelfContained>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.4.3" />
<PackageReference Include="Improbable.CSharpCodeGen" Version="0.0.1-preview" />
<PackageReference Include="Improbable.Stdlib.CSharpCodeGen" Version="0.0.1-preview" />
<PackageReference Include="Improbable.WorkerSdkInterop.CSharpCodeGen" Version="0.0.1-preview" />
<PackageReference Include="Improbable.Schema.Bundle" Version="0.0.1-preview" />
</ItemGroup>
</Project>
203 changes: 203 additions & 0 deletions CSharpCodeGenerator/CSharpCodeGenerator/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using CommandLine;
using Improbable.CSharpCodeGen;
using Improbable.Schema.Bundle;
using Improbable.Stdlib.CSharpCodeGen;
using Improbable.WorkerSdkInterop.CSharpCodeGen;
using static Improbable.CSharpCodeGen.Case;
using Types = Improbable.CSharpCodeGen.Types;

namespace CodeGenerator
{
public class Options
{
[Option("input-bundle", Required = true,
HelpText = "The path to the JSON Bundle file output by the SpatialOS schema_compiler.")]
public string InputBundle { get; set; }

[Option("output-marker",
HelpText = "The path to a file that is written when code is successfully generated. Useful for timestamp checking in build systems.")]
public string OutputMarker { get; set; }

[Option("output-dir", Required = true,
HelpText = "The path to write the generated code to.")]
public string OutputDir { get; set; }
}

internal class Program
{
private static void Main(string[] args)
{
Parser.Default.ParseArguments<Options>(args)
.WithParsed(Run)
.WithNotParsed(errors =>
{
foreach (var error in errors)
{
Console.Error.WriteLine(error);
}

Environment.ExitCode = 1;
});
}

private static void Run(Options options)
{
Console.WriteLine(Parser.Default.FormatCommandLine(options));

var timer = new Stopwatch();
timer.Start();

try
{
try
{
File.Delete(options.OutputMarker);
}
catch
{
// Nothing interesting to do here.
}

var bundle = SchemaBundleLoader.LoadBundle(options.InputBundle);

// Sort the types by the depth of their declaration, so that nested types are generated first so they can be used by their declaring type.
var types = bundle.Types.Select(kv => new TypeDescription(kv.Key, bundle))
.Union(bundle.Components.Select(kv => new TypeDescription(kv.Key, bundle)))
.OrderByDescending(t => t.QualifiedName.Count(c => c == '.'))
.ToList();

var baseGenerator = new Generator(bundle);
var generators = new List<ICodeGenerator>
{
baseGenerator,
new StdlibGenerator(bundle),
new SchemaObjectGenerator(bundle),
};

var allContent = new Dictionary<string, StringBuilder>();
var nestedTypes = new HashSet<string>();

// Pass 1: generate all type content.
foreach (var t in types)
{
if (!allContent.TryGetValue(t.QualifiedName, out var builder))
{
builder = allContent[t.QualifiedName] = new StringBuilder();
}

// Embed nested enums.
builder.AppendJoin(Environment.NewLine, t.NestedEnums.Select(e => GenerateEnum(e, bundle).TrimEnd()));

// Class implementation.
builder.AppendJoin(Environment.NewLine, generators.Select(g =>
{
var result = g.Generate(t).TrimEnd();
if (string.IsNullOrWhiteSpace(result))
{
return string.Empty;
}

return $@"
#region {g.GetType().FullName}
{result}
#endregion {g.GetType().FullName}
";

}).Where(s => !string.IsNullOrEmpty(s)));

// Nested types.
builder.AppendJoin(Environment.NewLine, t.NestedTypes.Select(e => GenerateType(e, allContent[e.QualifiedName].ToString(), bundle)));

nestedTypes.UnionWith(t.NestedTypes.Select(type => type.QualifiedName));
nestedTypes.UnionWith(t.NestedEnums.Select(type => type.QualifiedName));
}

// Pass 2: Generate final content.
foreach (var t in types.Where(type => !nestedTypes.Contains(type.QualifiedName)))
{
var content = allContent[t.QualifiedName];

WriteFile(options, Types.TypeToFilename(t.QualifiedName), $@"
namespace {GetPascalCaseNamespaceFromTypeName(t.QualifiedName)}
{{
{Indent(1, GenerateType(t, content.ToString().TrimEnd(), bundle))}
}}");
}

// Enums.
foreach (var (key, value) in bundle.Enums.Where(type => !nestedTypes.Contains(type.Key)))
{
WriteFile(options, Types.TypeToFilename(key), $@"
namespace {GetPascalCaseNamespaceFromTypeName(key)}
{{
{Indent(1, GenerateEnum(value, bundle))}
}}");
}

File.WriteAllText(options.OutputMarker, string.Empty);
}
catch (Exception exception)
{
Console.Error.WriteLine(exception);
Environment.ExitCode = 1;
}
finally
{
timer.Stop();

Console.WriteLine($"Processed schema bundle in {timer.Elapsed}.");
}
}

private static void WriteFile(Options options, string filename, string text)
{
var outputPath = Path.Combine(options.OutputDir, filename);
var folder = Path.GetDirectoryName(outputPath);
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}

// Normalize line endings so editors don't complain.
text = "// Generated by SpatialOS C# CodeGen.\n" + text.Trim() + "\n";
text = text.Replace("\r\n", "\n").Replace("\n", Environment.NewLine);

File.WriteAllText(outputPath, text, Encoding.UTF8);
}

private static string GenerateEnum(EnumDefinition enumDef, Bundle bundle)
{
var values = new StringBuilder();
foreach (var v in enumDef.Values)
{
values.AppendLine($"{AllCapsSnakeCaseToPascalCase(v.Name)} = {v.Value},");
}

return $@"// Generated from {bundle.TypeToFile[enumDef.QualifiedName].CanonicalPath}({enumDef.SourceReference.Line},{enumDef.SourceReference.Column})
public enum {enumDef.Name}
{{
{Indent(1, values.ToString().TrimEnd())}
}}";
}

private static string GenerateType(TypeDescription type, string content, Bundle bundle)
{
var typeName = GetPascalCaseNameFromTypeName(type.QualifiedName);


return $@"// Generated from {bundle.TypeToFile[type.QualifiedName].CanonicalPath}({type.SourceReference.Line},{type.SourceReference.Column})
public readonly struct {typeName} : global::System.IEquatable<{typeName}>
{{
{Indent(1, content)}
}}";
}
}
}
Loading

0 comments on commit 457cc8b

Please sign in to comment.