using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using System.Text.Json.Serialization; namespace Sandbox; partial class Compiler { public enum ReleaseMode { Debug, Release } public struct Configuration { public Configuration() { Clean(); } public string RootNamespace { get; set; } [Title( "Symbols" ), Description( "A list of pre-processor symbols to define when compiling, separated with semi-colons." )] public string DefineConstants { get; set; } = "SANDBOX"; public string NoWarn { get; set; } = "1701;1702;1591;"; public string WarningsAsErrors { get; set; } = ""; public bool TreatWarningsAsErrors { get; set; } public bool Nullables { get; set; } /// /// If true, we will be using the whitelist system. If false then this package won't /// be "sandboxed", so won't be able to be published on the platform. /// [JsonIgnore] public bool Whitelist { get; set; } = true; /// /// If true, we'll compile with /unsafe. This means that the package won't be able to /// be published on the platform. /// [JsonIgnore] public bool Unsafe { get; set; } = false; /// /// The current release mode. This only matters during local development. /// Published games are always built in release mode, where optimizations are enabled and debugging is limited (breakpoints, sequence points, and locals may be unavailable). /// public ReleaseMode ReleaseMode { get; set; } /// /// References to non-package assemblies, by assembly name. /// public List AssemblyReferences { get; set; } /// /// Maps file patterns to preprocessor directives they should be wrapped in /// public Dictionary ReplacementDirectives { get; set; } /// /// Strips disabled text trivia from the syntax tree. This is stuff like `#if false` blocks that are not compiled. /// internal bool StripDisabledTextTrivia { get; set; } = false; /// /// Folders to ignore when walking the tree /// public HashSet IgnoreFolders { get; set; } = new HashSet(); static bool IsPermittedAssemblyReference( string assemblyName ) { return assemblyName.StartsWith( "Sandbox.", StringComparison.OrdinalIgnoreCase ) || assemblyName.StartsWith( "Facepunch.", StringComparison.OrdinalIgnoreCase ) || assemblyName == "Microsoft.AspNetCore.Components"; } /// /// Each unique element of /// public IReadOnlySet DistinctAssemblyReferences => new HashSet( AssemblyReferences.Where( IsPermittedAssemblyReference ), StringComparer.OrdinalIgnoreCase ); public void Clean() { if ( string.IsNullOrWhiteSpace( RootNamespace ) ) RootNamespace = "Sandbox"; AssemblyReferences ??= new(); IgnoreFolders ??= new(); ReplacementDirectives ??= new() { { ".Server.cs", "SERVER" } // Include .Server.cs replacements by default }; } internal Dictionary GetReportDiagnostics() { var diags = NoWarn.Split( ";", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries ); var diagnostics = diags.ToDictionary( x => $"CS{x}", y => ReportDiagnostic.Suppress ); // alex: I had support for the "nullable" shorthand in here but it doesn't appear to work with CSharpCompilationOptions // (see https://github.com/dotnet/roslyn/issues/52414) so I've removed it for now. var warns = WarningsAsErrors.Split( ";", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries ); foreach ( var warn in warns ) diagnostics.Add( $"CS{warn}", ReportDiagnostic.Error ); return diagnostics; } /// /// Fetches the preprocessor symbols, which might've changed based on criteria /// /// public HashSet GetPreprocessorSymbols() { var symbols = DefineConstants.Split( ";", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries ) .ToHashSet(); if ( ReleaseMode == ReleaseMode.Debug ) { symbols.Add( "DEBUG" ); } else { symbols.Remove( "DEBUG" ); } return symbols; } /// /// Returns the CSharpParseOptions for this configuration, which includes the preprocessor symbols defined in . /// /// public CSharpParseOptions GetParseOptions() { return CSharpParseOptions.Default .WithLanguageVersion( LanguageVersion.CSharp14 ) .WithPreprocessorSymbols( GetPreprocessorSymbols() ); } } }