11using System ;
2- using System . Collections ;
3- using System . Diagnostics ;
42using System . IO ;
53using System . Linq ;
6- using System . Reflection ;
74using System . Text . RegularExpressions ;
85using UnityEditor ;
96using UnityEngine ;
10- using UnityEditor . Compilation ;
117using Assembly = System . Reflection . Assembly ;
128
139namespace Coffee . CSharpCompilerSettings
1410{
11+ internal interface ICustomCompiler : IDisposable
12+ {
13+ bool IsValid ( ) ;
14+ void Register ( ) ;
15+ }
16+
1517 internal static class Core
1618 {
1719 private static bool IsGlobal { get ; set ; }
1820
21+ private static bool IsDevelopAssembly
22+ {
23+ get { return typeof ( Core ) . Assembly . GetName ( ) . Name == "CSharpCompilerSettings_" ; }
24+ }
25+
26+ private static readonly ICustomCompiler [ ] customCompilers = new ICustomCompiler [ ]
27+ {
28+ CustomCompiler_Legacy . instance ,
29+ } ;
30+
31+ private static ICustomCompiler currentCustomCompiler { get ; set ; }
32+
33+ public static bool ShouldToRecompile ( string assemblyName , string asmdef )
34+ {
35+ if ( assemblyName == typeof ( Core ) . Assembly . GetName ( ) . Name )
36+ {
37+ // Logger.LogWarning(" <color=#bbbb44><Skipped> Assembly <b>'{0}'</b> requires default csc.</color>", assemblyName);
38+ return false ;
39+ }
40+ else if ( IsGlobal && GetPortableDllPath ( asmdef ) != null )
41+ {
42+ // Logger.LogWarning(" <color=#bbbb44><Skipped> Local CSharpCompilerSettings.*.dll for <b>'{0}'</b> is found.</color>", assemblyName);
43+ return false ;
44+ }
45+ else if ( ! IsGlobal && ! IsInSameDirectory ( asmdef ) )
46+ {
47+ // Logger.LogWarning(" <color=#bbbb44><Skipped> Assembly <b>'{0}'</b> is not target.</color>", assemblyName);
48+ return false ;
49+ }
50+ else if ( ! GetSettings ( ) . ShouldToRecompile ( asmdef ) )
51+ {
52+ // Logger.LogWarning(" <color=#bbbb44><Skipped> Assembly <b>'{0}'</b> does not need to be recompiled.</color>", assemblyName);
53+ return false ;
54+ }
55+ return true ;
56+ }
57+
1958 public static string GetAssemblyName ( string asmdefPath )
2059 {
2160 if ( string . IsNullOrEmpty ( asmdefPath ) ) return null ;
@@ -68,8 +107,8 @@ public static CscSettingsAsset GetSettings()
68107 public static string ModifyResponseFile ( CscSettingsAsset setting , string assemblyName , string asmdefPath , string text )
69108 {
70109 var asmdefDir = string . IsNullOrEmpty ( asmdefPath ) ? null : Path . GetDirectoryName ( asmdefPath ) ;
71- text = Regex . Replace ( text , "[\r \n ]+" , "\n " ) ;
72- text = Regex . Replace ( text , "^-" , "/" ) ;
110+ text = Regex . Replace ( text , "[\r \n ]+" , "\n " , RegexOptions . Multiline ) ;
111+ text = Regex . Replace ( text , "^-" , "/" , RegexOptions . Multiline ) ;
73112 text = Regex . Replace ( text , "\n /debug\n " , "\n /debug:portable\n " ) ;
74113 text += "\n /preferreduilang:en-US" ;
75114
@@ -139,127 +178,6 @@ public static string ModifyResponseFile(CscSettingsAsset setting, string assembl
139178 return text ;
140179 }
141180
142- private static void ChangeCompilerProcess ( object compiler , object scriptAssembly , CscSettingsAsset setting )
143- {
144- if ( IsDevelopAssembly )
145- return ;
146-
147- var tProgram = Type . GetType ( "UnityEditor.Utils.Program, UnityEditor" ) ;
148- var tScriptCompilerBase = Type . GetType ( "UnityEditor.Scripting.Compilers.ScriptCompilerBase, UnityEditor" ) ;
149- var fiProcess = tScriptCompilerBase . GetField ( "process" , BindingFlags . NonPublic | BindingFlags . Instance ) ;
150- var psi = compiler . Get ( "process" , fiProcess ) . Call ( "GetProcessStartInfo" ) as ProcessStartInfo ;
151- var oldCommand = ( psi . FileName + " " + psi . Arguments ) . Replace ( '\\ ' , '/' ) ;
152- var command = oldCommand . Replace ( EditorApplication . applicationContentsPath . Replace ( '\\ ' , '/' ) , "@APP_CONTENTS@" ) ;
153- var isDefaultCsc = Regex . IsMatch ( command , "@APP_CONTENTS@/[^ ]*(mcs|csc)" ) ;
154- var assemblyName = Path . GetFileNameWithoutExtension ( scriptAssembly . Get ( "Filename" ) as string ) ;
155- var asmdefDir = scriptAssembly . Get ( "OriginPath" ) as string ;
156- var asmdefPath = string . IsNullOrEmpty ( asmdefDir ) ? "" : FindAsmdef ( asmdefDir ) ;
157-
158- // csc is not Unity default. It is already modified.
159- if ( ! isDefaultCsc )
160- {
161- Logger . LogWarning ( " <color=#bbbb44><Skipped> current csc is not Unity default. It is already modified.</color>" ) ;
162- return ;
163- }
164-
165- // Kill current process.
166- compiler . Call ( "Dispose" ) ;
167-
168- // Response file.
169- var responseFile = Regex . Replace ( psi . Arguments , "^.*@(.+)$" , "$1" ) ;
170-
171- // Change to custom compiler.
172- if ( setting . ShouldToUseCustomCompiler ( asmdefPath ) )
173- {
174- var compilerInfo = CompilerInfo . GetInstalledInfo ( setting . CompilerPackage . PackageId ) ;
175-
176- // csc is not installed. Restart current process.
177- if ( ! compilerInfo . IsValid )
178- {
179- Logger . LogWarning ( " <color=#bbbb44><Skipped> C# compiler '{0}' is not installed. Restart compiler process: {1}</color>" , compilerInfo . Path , oldCommand ) ;
180-
181- var currentProgram = tProgram . New ( psi ) ;
182- currentProgram . Call ( "Start" ) ;
183- compiler . Set ( "process" , currentProgram , fiProcess ) ;
184- return ;
185- }
186-
187- // Change exe file path.
188- compilerInfo . Setup ( psi , responseFile , Application . platform ) ;
189- }
190-
191- // Modify response file.
192- var text = File . ReadAllText ( responseFile ) ;
193- text = ModifyResponseFile ( setting , assemblyName , asmdefPath , text ) ;
194- File . WriteAllText ( responseFile , text ) ;
195-
196- // Logging
197- if ( CscSettingsAsset . instance . EnableDebugLog )
198- Logger . LogDebug ( "Response file '{0}' has been modified:\n {1}" , responseFile , Regex . Replace ( text , "\n /reference.*" , "" ) + "\n \n * The references are skipped because it was too long." ) ;
199-
200- // Restart compiler process.
201- Logger . LogDebug ( "Restart compiler process: {0} {1}\n old command = {2}" , psi . FileName , psi . Arguments , oldCommand ) ;
202- var program = tProgram . New ( psi ) ;
203- program . Call ( "Start" ) ;
204- compiler . Set ( "process" , program , fiProcess ) ;
205- }
206-
207- public static void OnAssemblyCompilationStarted ( string name )
208- {
209- try
210- {
211- var assemblyName = Path . GetFileNameWithoutExtension ( name ) ;
212- if ( assemblyName == typeof ( Core ) . Assembly . GetName ( ) . Name )
213- {
214- Logger . LogWarning ( " <color=#bbbb44><Skipped> Assembly <b>'{0}'</b> requires default csc.</color>" , assemblyName ) ;
215- return ;
216- }
217-
218- var tEditorCompilationInterface = Type . GetType ( "UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface, UnityEditor" ) ;
219- var compilerTasks = tEditorCompilationInterface . Get ( "Instance" ) . Get ( "compilationTask" ) . Get ( "compilerTasks" ) as IDictionary ;
220- var scriptAssembly = compilerTasks . Keys . Cast < object > ( ) . FirstOrDefault ( x => ( x . Get ( "Filename" ) as string ) == assemblyName + ".dll" ) ;
221- if ( scriptAssembly == null )
222- {
223- Logger . LogWarning ( " <color=#bbbb44><Skipped> scriptAssembly <b>'{0}'</b> is not found.</color>" , assemblyName ) ;
224- return ;
225- }
226-
227- var asmdefDir = scriptAssembly . Get ( "OriginPath" ) as string ;
228- var asmdefPath = string . IsNullOrEmpty ( asmdefDir ) ? "" : FindAsmdef ( asmdefDir ) ;
229- if ( IsGlobal && GetPortableDllPath ( asmdefPath ) != null )
230- {
231- Logger . LogWarning ( " <color=#bbbb44><Skipped> Local CSharpCompilerSettings.*.dll for <b>'{0}'</b> is found.</color>" , assemblyName ) ;
232- return ;
233- }
234-
235- if ( ! IsGlobal && ! IsInSameDirectory ( asmdefPath ) )
236- {
237- Logger . LogWarning ( " <color=#bbbb44><Skipped> Assembly <b>'{0}'</b> is not target.</color>" , assemblyName ) ;
238- return ;
239- }
240-
241- var globalSettings = CscSettingsAsset . instance ;
242- var settings = GetSettings ( ) ;
243- if ( ! globalSettings . ShouldToRecompile ( asmdefPath ) )
244- {
245- Logger . LogWarning ( " <color=#bbbb44><Skipped> Assembly <b>'{0}'</b> does not need to be recompiled.</color>" , assemblyName ) ;
246- return ;
247- }
248-
249- // Create new compiler to recompile.
250- Logger . LogDebug ( "<color=#22aa22>Assembly compilation started: <b>{0} should be recompiled.</b></color>\n settings = {1}" , assemblyName , JsonUtility . ToJson ( settings ) ) ;
251- ChangeCompilerProcess ( compilerTasks [ scriptAssembly ] , scriptAssembly , settings ) ;
252- }
253- catch ( Exception e )
254- {
255- Logger . LogException ( e ) ;
256- }
257- }
258-
259- static bool IsDevelopAssembly
260- {
261- get { return typeof ( Core ) . Assembly . GetName ( ) . Name == "CSharpCompilerSettings_" ; }
262- }
263181
264182 [ InitializeOnLoadMethod ]
265183 public static void Initialize ( )
@@ -270,7 +188,9 @@ public static void Initialize()
270188 if ( IsGlobal )
271189 {
272190 Logger . Setup (
273- "<b><color=#bb4444>[CscSettings]</color></b> " ,
191+ IsDevelopAssembly
192+ ? "<b><color=#bb4444>[CscSettings(dev)]</color></b> "
193+ : "<b><color=#bb4444>[CscSettings]</color></b> " ,
274194 ( ) => CscSettingsAsset . instance . EnableDebugLog
275195 ) ;
276196 }
@@ -286,27 +206,18 @@ public static void Initialize()
286206 Logger . LogException ( "Target assembly is not found. {0}" , typeof ( Core ) . Assembly . Location . Replace ( Environment . CurrentDirectory , "." ) ) ;
287207 }
288208
289- // Dump loaded assemblies
290- if ( CscSettingsAsset . instance . EnableDebugLog )
209+ // This is global assembly, but the dev assembly is found: do nothing.
210+ if ( IsGlobal && ! IsDevelopAssembly && ( Type . GetType ( "UnityEditor.EditorAssemblies, UnityEditor" ) . Get ( "loadedAssemblies" ) as Assembly [ ] ) . Any ( asm => asm . GetName ( ) . Name == "CSharpCompilerSettings_" ) )
291211 {
292- var sb = new System . Text . StringBuilder ( "<color=#22aa22><b>InitializeOnLoad,</b></color> the loaded assemblies:\n " ) ;
293- foreach ( var asm in Type . GetType ( "UnityEditor.EditorAssemblies, UnityEditor" ) . Get ( "loadedAssemblies" ) as Assembly [ ] )
294- {
295- var name = asm . GetName ( ) . Name ;
296- var path = asm . Location ;
297- if ( path . Contains ( Path . GetDirectoryName ( EditorApplication . applicationPath ) ) )
298- sb . AppendFormat ( " > {0}:\t {1}\n " , name , "APP_PATH/.../" + Path . GetFileName ( path ) ) ;
299- else
300- sb . AppendFormat ( " > <color=#22aa22><b>{0}</b></color>:\t {1}\n " , name , path . Replace ( Environment . CurrentDirectory , "." ) ) ;
301- }
302-
303- Logger . LogDebug ( sb . ToString ( ) ) ;
212+ Logger . LogWarning ( "This is global assembly, but the dev assembly is found: ignored." ) ;
213+ return ;
304214 }
305215
306216 // Register callback.
307- Logger . LogDebug ( "<color=#22aa22><b>InitializeOnLoad:</b></color> start watching assembly compilation." ) ;
308- CompilationPipeline . assemblyCompilationStarted -= OnAssemblyCompilationStarted ;
309- CompilationPipeline . assemblyCompilationStarted += OnAssemblyCompilationStarted ;
217+ currentCustomCompiler ? . Dispose ( ) ;
218+ currentCustomCompiler = customCompilers . FirstOrDefault ( c => c . IsValid ( ) ) ;
219+ currentCustomCompiler ? . Register ( ) ;
220+ Logger . LogDebug ( "<color=#22aa22><b>InitializeOnLoad:</b></color> A custom compiler registered: {0}" , currentCustomCompiler ) ;
310221
311222 // Install custom compiler package before compilation.
312223 var settings = GetSettings ( ) ;
0 commit comments