Skip to content

Commit 5913d44

Browse files
authored
Merge pull request #594 from pzgulyas/roslyn
ScriptManager migration to the CodeAnalysis class. This is in preparation for the net 6 migration.
2 parents 68f4736 + 69f8601 commit 5913d44

File tree

4 files changed

+124
-81
lines changed

4 files changed

+124
-81
lines changed

Source/Orts.Simulation/Common/Scripting/ScriptManager.cs

Lines changed: 72 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -15,46 +15,42 @@
1515
// You should have received a copy of the GNU General Public License
1616
// along with Open Rails. If not, see <http://www.gnu.org/licenses/>.
1717

18-
using Microsoft.CodeDom.Providers.DotNetCompilerPlatform;
1918
using Orts.Simulation;
2019
using ORTS.Common;
2120
using System;
22-
using System.CodeDom.Compiler;
2321
using System.Collections.Generic;
2422
using System.Diagnostics;
2523
using System.IO;
24+
using System.Linq;
2625
using System.Reflection;
2726
using System.Text;
2827
using System.Threading;
28+
using Microsoft.CodeAnalysis;
29+
using Microsoft.CodeAnalysis.CSharp;
30+
using Microsoft.CodeAnalysis.Emit;
2931

3032
namespace Orts.Common.Scripting
3133
{
3234
[CallOnThread("Loader")]
3335
public class ScriptManager
3436
{
35-
readonly Simulator Simulator;
3637
readonly IDictionary<string, Assembly> Scripts = new Dictionary<string, Assembly>();
37-
static readonly ProviderOptions ProviderOptions = new ProviderOptions(Path.Combine(new Uri(Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase)).LocalPath, "roslyn", "csc.exe"), 10);
38-
static readonly CSharpCodeProvider Compiler = new CSharpCodeProvider(ProviderOptions);
39-
40-
static CompilerParameters GetCompilerParameters()
38+
static readonly string[] ReferenceAssemblies = new[]
4139
{
42-
var cp = new CompilerParameters()
43-
{
44-
GenerateInMemory = true,
45-
IncludeDebugInformation = Debugger.IsAttached,
46-
};
47-
cp.ReferencedAssemblies.Add("System.dll");
48-
cp.ReferencedAssemblies.Add("System.Core.dll");
49-
cp.ReferencedAssemblies.Add("ORTS.Common.dll");
50-
cp.ReferencedAssemblies.Add("Orts.Simulation.dll");
51-
return cp;
52-
}
40+
typeof(System.Object).Assembly.Location,
41+
typeof(System.Diagnostics.Debug).Assembly.Location,
42+
typeof(ORTS.Common.ElapsedTime).Assembly.Location,
43+
typeof(ORTS.Scripting.Api.Timer).Assembly.Location,
44+
typeof(System.Linq.Enumerable).Assembly.Location,
45+
};
46+
static MetadataReference[] References = ReferenceAssemblies.Select(r => MetadataReference.CreateFromFile(r)).ToArray();
47+
static CSharpCompilationOptions CompilationOptions = new CSharpCompilationOptions(
48+
OutputKind.DynamicallyLinkedLibrary,
49+
optimizationLevel: Debugger.IsAttached ? OptimizationLevel.Debug : OptimizationLevel.Release);
5350

5451
[CallOnThread("Loader")]
55-
internal ScriptManager(Simulator simulator)
52+
internal ScriptManager()
5653
{
57-
Simulator = simulator;
5854
}
5955

6056
public object Load(string[] pathArray, string name, string nameSpace = "ORTS.Scripting.Script")
@@ -65,7 +61,7 @@ public object Load(string[] pathArray, string name, string nameSpace = "ORTS.Scr
6561
if (pathArray == null || pathArray.Length == 0 || name == null || name == "")
6662
return null;
6763

68-
if (Path.GetExtension(name) != ".cs")
64+
if (Path.GetExtension(name).ToLower() != ".cs")
6965
name += ".cs";
7066

7167
var path = ORTSPaths.GetFileFromFolders(pathArray, name);
@@ -78,30 +74,65 @@ public object Load(string[] pathArray, string name, string nameSpace = "ORTS.Scr
7874
var type = String.Format("{0}.{1}", nameSpace, Path.GetFileNameWithoutExtension(path).Replace('-', '_'));
7975

8076
if (!Scripts.ContainsKey(path))
81-
Scripts[path] = CompileScript(path);
77+
Scripts[path] = CompileScript(new string[] { path });
8278
return Scripts[path]?.CreateInstance(type, true);
8379
}
8480

85-
private static Assembly CompileScript(string path)
81+
private static Assembly CompileScript(string[] path)
8682
{
83+
var scriptPath = path.Length > 1 ? Path.GetDirectoryName(path[0]) : path[0];
84+
var scriptName = Path.GetFileName(scriptPath);
85+
var symbolsName = Path.ChangeExtension(scriptName, "pdb");
8786
try
8887
{
89-
var compilerResults = Compiler.CompileAssemblyFromFile(GetCompilerParameters(), path);
90-
if (!compilerResults.Errors.HasErrors)
88+
var syntaxTrees = path.Select(file => CSharpSyntaxTree.ParseText(File.ReadAllText(file), null, file, Encoding.UTF8));
89+
var compilation = CSharpCompilation.Create(
90+
scriptName,
91+
syntaxTrees,
92+
References,
93+
CompilationOptions);
94+
95+
var emitOptions = new EmitOptions(
96+
debugInformationFormat: DebugInformationFormat.PortablePdb,
97+
pdbFilePath: symbolsName);
98+
99+
var assemblyStream = new MemoryStream();
100+
var symbolsStream = new MemoryStream();
101+
102+
var result = compilation.Emit(
103+
assemblyStream,
104+
symbolsStream,
105+
options: emitOptions);
106+
107+
if (result.Success)
91108
{
92-
var script = compilerResults.CompiledAssembly;
109+
assemblyStream.Seek(0, SeekOrigin.Begin);
110+
symbolsStream.Seek(0, SeekOrigin.Begin);
111+
112+
var script = Assembly.Load(assemblyStream.ToArray(), symbolsStream.ToArray());
113+
// in netcore:
114+
//var script = AssemblyLoadContext.Default.LoadFromStream(ms);
93115
if (script == null)
94-
Trace.TraceWarning($"Script file {path} could not be loaded into the process.");
116+
Trace.TraceWarning($"Script {scriptPath} could not be loaded into the process.");
95117
return script;
96118
}
97119
else
98120
{
121+
var errors = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error);
122+
99123
var errorString = new StringBuilder();
100-
errorString.AppendFormat("Skipped script {0} with error:", path);
124+
errorString.AppendFormat("Skipped script {0} with error:", scriptPath);
101125
errorString.Append(Environment.NewLine);
102-
foreach (CompilerError error in compilerResults.Errors)
126+
foreach (var error in errors)
103127
{
104-
errorString.AppendFormat(" {0}, line: {1}, column: {2}", error.ErrorText, error.Line /*- prefixLines*/, error.Column);
128+
var textSpan = error.Location.SourceSpan;
129+
var fileName = Path.GetFileName(error.Location.SourceTree.FilePath);
130+
var lineSpan = error.Location.SourceTree.GetLineSpan(textSpan);
131+
var line = lineSpan.StartLinePosition.Line + 1;
132+
var column = lineSpan.StartLinePosition.Character + 1;
133+
errorString.AppendFormat("\t{0}: {1}, ", error.Id, error.GetMessage());
134+
if (path.Length > 1) errorString.AppendFormat("file: {0}, ", fileName);
135+
errorString.AppendFormat("line: {0}, column: {1}", line, column);
105136
errorString.Append(Environment.NewLine);
106137
}
107138

@@ -111,21 +142,22 @@ private static Assembly CompileScript(string path)
111142
}
112143
catch (InvalidDataException error)
113144
{
114-
Trace.TraceWarning("Skipped script {0} with error: {1}", path, error.Message);
145+
Trace.TraceWarning("Skipped script {0} with error: {1}", scriptPath, error.Message);
115146
return null;
116147
}
117148
catch (Exception error)
118149
{
119-
if (File.Exists(path))
120-
Trace.WriteLine(new FileLoadException(path, error));
150+
if (File.Exists(scriptPath) || Directory.Exists(scriptPath))
151+
Trace.WriteLine(new FileLoadException(scriptPath, error));
121152
else
122-
Trace.TraceWarning("Ignored missing script file {0}", path);
153+
Trace.TraceWarning("Ignored missing script {0}", scriptPath);
123154
return null;
124155
}
125156
}
126157

127158
public Assembly LoadFolder(string path)
128159
{
160+
129161
if (Thread.CurrentThread.Name != "Loader Process")
130162
Trace.TraceError("ScriptManager.Load incorrectly called by {0}; must be Loader Process or crashes will occur.", Thread.CurrentThread.Name);
131163

@@ -138,50 +170,16 @@ public Assembly LoadFolder(string path)
138170

139171
if (files == null || files.Length == 0) return null;
140172

141-
try
173+
if (!Scripts.ContainsKey(path))
142174
{
143-
var compilerResults = Compiler.CompileAssemblyFromFile(GetCompilerParameters(), files);
144-
if (!compilerResults.Errors.HasErrors)
145-
{
146-
return compilerResults.CompiledAssembly;
147-
}
148-
else
149-
{
150-
var errorString = new StringBuilder();
151-
errorString.AppendFormat("Skipped script folder {0} with error:", path);
152-
errorString.Append(Environment.NewLine);
153-
foreach (CompilerError error in compilerResults.Errors)
154-
{
155-
errorString.AppendFormat(" {0}, file: {1}, line: {2}, column: {3}", error.ErrorText, error.FileName, error.Line /*- prefixLines*/, error.Column);
156-
errorString.Append(Environment.NewLine);
157-
}
158-
159-
Trace.TraceWarning(errorString.ToString());
175+
var assembly = CompileScript(files);
176+
if (assembly == null)
160177
return null;
161-
}
162-
}
163-
catch (InvalidDataException error)
164-
{
165-
Trace.TraceWarning("Skipped script folder {0} with error: {1}", path, error.Message);
166-
return null;
167-
}
168-
catch (Exception error)
169-
{
170-
Trace.WriteLine(new FileLoadException(path, error));
171-
return null;
172-
}
173-
}
174-
175-
/*
176-
static ClassType CreateInstance<ClassType>(Assembly assembly) where ClassType : class
177-
{
178-
foreach (var type in assembly.GetTypes())
179-
if (typeof(ClassType).IsAssignableFrom(type))
180-
return Activator.CreateInstance(type) as ClassType;
181178

182-
return default(ClassType);
179+
Scripts[path] = assembly;
180+
}
181+
return Scripts[path];
183182
}
184-
*/
185183

186184
[CallOnThread("Updater")]
187185
public string GetStatus()

Source/Orts.Simulation/Orts.Simulation.csproj

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<Import Project="..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.2\build\Microsoft.CodeAnalysis.Analyzers.props" Condition="Exists('..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.2\build\Microsoft.CodeAnalysis.Analyzers.props')" />
34
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
45
<PropertyGroup>
56
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -43,17 +44,45 @@
4344
<Reference Include="GNU.Gettext">
4445
<HintPath>..\3rdPartyLibs\GNU.Gettext.dll</HintPath>
4546
</Reference>
47+
<Reference Include="Microsoft.CodeAnalysis, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
48+
<HintPath>..\packages\Microsoft.CodeAnalysis.Common.4.0.1\lib\netstandard2.0\Microsoft.CodeAnalysis.dll</HintPath>
49+
</Reference>
50+
<Reference Include="Microsoft.CodeAnalysis.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
51+
<HintPath>..\packages\Microsoft.CodeAnalysis.CSharp.4.0.1\lib\netstandard2.0\Microsoft.CodeAnalysis.CSharp.dll</HintPath>
52+
</Reference>
4653
<Reference Include="MonoGame.Framework">
4754
<HintPath>$(SolutionDir)\3rdPartyLibs\MonoGame\MonoGame.Framework.dll</HintPath>
4855
</Reference>
49-
<Reference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=3.6.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
50-
<HintPath>..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll</HintPath>
51-
</Reference>
5256
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
5357
<SpecificVersion>False</SpecificVersion>
5458
<HintPath>..\3rdPartyLibs\Newtonsoft.Json.dll</HintPath>
5559
</Reference>
5660
<Reference Include="System" />
61+
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
62+
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
63+
</Reference>
64+
<Reference Include="System.Collections.Immutable, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
65+
<HintPath>..\packages\System.Collections.Immutable.5.0.0\lib\net461\System.Collections.Immutable.dll</HintPath>
66+
</Reference>
67+
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
68+
<HintPath>..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath>
69+
</Reference>
70+
<Reference Include="System.Numerics" />
71+
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
72+
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
73+
</Reference>
74+
<Reference Include="System.Reflection.Metadata, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
75+
<HintPath>..\packages\System.Reflection.Metadata.5.0.0\lib\net461\System.Reflection.Metadata.dll</HintPath>
76+
</Reference>
77+
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
78+
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
79+
</Reference>
80+
<Reference Include="System.Text.Encoding.CodePages, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
81+
<HintPath>..\packages\System.Text.Encoding.CodePages.4.5.1\lib\net461\System.Text.Encoding.CodePages.dll</HintPath>
82+
</Reference>
83+
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
84+
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
85+
</Reference>
5786
</ItemGroup>
5887
<ItemGroup>
5988
<Compile Include="Common\CommandLog.cs" />
@@ -198,14 +227,19 @@
198227
<ItemGroup>
199228
<None Include="packages.config" />
200229
</ItemGroup>
230+
<ItemGroup>
231+
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.2\analyzers\dotnet\cs\Microsoft.CodeAnalysis.Analyzers.dll" />
232+
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.2\analyzers\dotnet\cs\Microsoft.CodeAnalysis.CSharp.Analyzers.dll" />
233+
</ItemGroup>
201234
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
202-
<Import Project="..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\build\net472\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.targets" Condition="Exists('..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\build\net472\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.targets')" />
203235
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
204236
<PropertyGroup>
205237
<ErrorText>Ce projet fait référence à des packages NuGet qui sont manquants sur cet ordinateur. Utilisez l'option de restauration des packages NuGet pour les télécharger. Pour plus d'informations, consultez http://go.microsoft.com/fwlink/?LinkID=322105. Le fichier manquant est : {0}.</ErrorText>
206238
</PropertyGroup>
207-
<Error Condition="!Exists('..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\build\net472\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\build\net472\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.targets'))" />
239+
<Error Condition="!Exists('..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.2\build\Microsoft.CodeAnalysis.Analyzers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.2\build\Microsoft.CodeAnalysis.Analyzers.props'))" />
240+
<Error Condition="!Exists('..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.2\build\Microsoft.CodeAnalysis.Analyzers.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.2\build\Microsoft.CodeAnalysis.Analyzers.targets'))" />
208241
</Target>
242+
<Import Project="..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.2\build\Microsoft.CodeAnalysis.Analyzers.targets" Condition="Exists('..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.2\build\Microsoft.CodeAnalysis.Analyzers.targets')" />
209243
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
210244
Other similar extension points exist, see Microsoft.Common.targets.
211245
<Target Name="BeforeBuild">

Source/Orts.Simulation/Simulation/Simulator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ public Simulator(UserSettings settings, string activityPath, bool useOpenRailsDi
354354
Confirmer = new Confirmer(this, 1.5);
355355
HazzardManager = new HazzardManager(this);
356356
FuelManager = new FuelManager(this);
357-
ScriptManager = new ScriptManager(this);
357+
ScriptManager = new ScriptManager();
358358
Log = new CommandLog(this);
359359
}
360360

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
3-
<package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="3.6.0" targetFramework="net472" />
3+
<package id="Microsoft.CodeAnalysis.Analyzers" version="3.3.2" targetFramework="net472" developmentDependency="true" />
4+
<package id="Microsoft.CodeAnalysis.Common" version="4.0.1" targetFramework="net472" />
5+
<package id="Microsoft.CodeAnalysis.CSharp" version="4.0.1" targetFramework="net472" />
6+
<package id="System.Buffers" version="4.5.1" targetFramework="net472" />
7+
<package id="System.Collections.Immutable" version="5.0.0" targetFramework="net472" />
8+
<package id="System.Memory" version="4.5.4" targetFramework="net472" />
9+
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net472" />
10+
<package id="System.Reflection.Metadata" version="5.0.0" targetFramework="net472" />
11+
<package id="System.Runtime.CompilerServices.Unsafe" version="5.0.0" targetFramework="net472" />
12+
<package id="System.Runtime.Loader" version="4.3.0" targetFramework="net472" />
13+
<package id="System.Text.Encoding.CodePages" version="4.5.1" targetFramework="net472" />
14+
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net472" />
415
</packages>

0 commit comments

Comments
 (0)