1+ namespace SlnGen . Editor
2+ {
3+ using System ;
4+ using System . Collections . Generic ;
5+ using System . Linq ;
6+ using System . Text ;
7+ using System . Text . RegularExpressions ;
8+ using System . Xml . Linq ;
9+ using UnityEditor ;
10+ using UnityEngine ;
11+ using UnityEngine . Assertions ;
12+
13+ internal class CsProjPostProcessor : AssetPostprocessor
14+ {
15+ private static readonly string editorRefConditional ;
16+ private static readonly string platformDefines ;
17+
18+ static CsProjPostProcessor ( )
19+ {
20+ platformDefines = GetPlatformDefinePattern ( SlnGen . Platforms ) ;
21+ editorRefConditional = GetEditorConfigCondition ( SlnGen . Configurations ) ;
22+ }
23+
24+ private static string GetPlatformDefinePattern ( Platform [ ] pForms )
25+ {
26+ StringBuilder stringBuilder = new StringBuilder ( ) ;
27+
28+ HashSet < string > platformDefines = new HashSet < string > ( ) ;
29+ foreach ( Platform platform in pForms )
30+ {
31+ foreach ( string define in platform . Defines )
32+ {
33+ if ( ! platformDefines . Add ( define ) )
34+ {
35+ continue ;
36+ }
37+
38+ stringBuilder . Append ( @"\b" ) ;
39+ stringBuilder . Append ( define ) ;
40+ stringBuilder . Append ( @"\b|" ) ;
41+ }
42+ }
43+
44+ if ( stringBuilder . Length > 0 )
45+ {
46+ // Remove the last pipe character.
47+ stringBuilder . Length = stringBuilder . Length - 1 ;
48+ }
49+
50+ return stringBuilder . ToString ( ) ;
51+ }
52+
53+ private static string GetEditorConfigCondition ( Configuration [ ] configs )
54+ {
55+ List < string > conditions = new List < string > ( ) ;
56+
57+ foreach ( Configuration config in configs )
58+ {
59+ if ( ! config . UseEditorReferences )
60+ {
61+ continue ;
62+ }
63+ foreach ( Platform platform in SlnGen . Platforms )
64+ {
65+ conditions . Add ( " '$(Configuration)' == '" + config . Name + platform . Name + "' " ) ;
66+ }
67+ }
68+
69+ return string . Join ( " OR " , conditions . ToArray ( ) ) ;
70+ }
71+
72+ private static string OnGeneratedCSProject ( string path , string content )
73+ {
74+ try
75+ {
76+ XDocument doc = XDocument . Parse ( content ) ;
77+ XElement root = doc . Root ;
78+
79+ Assert . IsNotNull ( root ) ;
80+
81+ XNamespace nSpace = root . Name . Namespace ;
82+
83+ List < XElement > propertyGroups = root . Elements ( nSpace + "PropertyGroup" ) . ToList ( ) ;
84+
85+ // Ensure the "Release" source PropertyGroup has Unity's defines.
86+ CopyDefinesToReleaseGroup ( propertyGroups , nSpace ) ;
87+
88+ // Generate property groups for all configurations/platforms.
89+ List < XElement > newGroups = GeneratePropertyGroups ( propertyGroups , nSpace ) ;
90+ propertyGroups [ propertyGroups . Count - 1 ] . AddAfterSelf ( newGroups ) ;
91+ foreach ( XElement propertyGroup in propertyGroups )
92+ {
93+ propertyGroup . Remove ( ) ;
94+ }
95+
96+ // Add editor-config conditions to project references.
97+ AddConditionsToReferences ( root , nSpace ) ;
98+
99+ return doc . ToString ( ) ;
100+ }
101+ catch ( Exception e )
102+ {
103+ // Error occurred. Return the unmodified project file.
104+ Debug . LogException ( e ) ;
105+ return content ;
106+ }
107+ }
108+
109+ /// <summary>
110+ /// Generated project files do not declare defines in the release configuration.
111+ /// We copy these defines from the Debug configuration. They are then filtered from the defined configurations.
112+ /// </summary>
113+ private static void CopyDefinesToReleaseGroup ( List < XElement > propertyGroups , XNamespace nSpace )
114+ {
115+ XElement debugDefines = null ;
116+ XElement releasePropGroup = null ;
117+
118+ // Add non-debug defines to the release mode.
119+ foreach ( XElement propertyGroup in propertyGroups )
120+ {
121+ // The correct property group has a configuration/platform condition.
122+ XAttribute condition = propertyGroup . Attribute ( "Condition" ) ;
123+ if ( condition == null )
124+ {
125+ continue ;
126+ }
127+
128+ // The correct property group contains an "Optimize" value.
129+ XElement optimizeProp = propertyGroup . Element ( nSpace + "Optimize" ) ;
130+ if ( optimizeProp == null )
131+ {
132+ continue ;
133+ }
134+
135+ if ( condition . Value . Contains ( "Release" ) )
136+ {
137+ releasePropGroup = propertyGroup ;
138+ }
139+ else
140+ {
141+ debugDefines = propertyGroup . Element ( nSpace + "DefineConstants" ) ;
142+ }
143+ }
144+
145+ Assert . IsNotNull ( releasePropGroup ) ;
146+ Assert . IsNotNull ( debugDefines ) ;
147+
148+ releasePropGroup . Add ( new XElement ( nSpace + "DefineConstants" , debugDefines . Value . Replace ( "DEBUG;" , "" ) ) ) ;
149+ }
150+
151+ private static List < XElement > GeneratePropertyGroups ( List < XElement > sources , XNamespace nSpace )
152+ {
153+ List < XElement > newElements = new List < XElement > ( ) ;
154+
155+ foreach ( XElement propertyGroup in sources )
156+ {
157+ // Determine where this condition is saved.
158+ XAttribute condition = propertyGroup . Attribute ( "Condition" ) ;
159+ if ( condition == null ) // This is a startup configuration.
160+ {
161+ XElement rootConfig = propertyGroup . Element ( nSpace + "Configuration" ) ;
162+
163+ if ( rootConfig != null )
164+ {
165+ rootConfig . SetValue ( SlnGen . DefaultConfig . Name + '-' + SlnGen . CurrentPlatform . Name ) ;
166+ }
167+
168+ newElements . Add ( propertyGroup ) ;
169+ continue ;
170+ }
171+
172+ GenerateProjectConfigurations ( condition , propertyGroup , ref newElements ) ;
173+ }
174+
175+ return newElements ;
176+ }
177+
178+ /// <summary>
179+ /// Creates duplicate PropertyGroups nodes for all platforms, replacing the platform name, and defines for that platform.
180+ /// </summary>
181+ private static void GenerateProjectConfigurations ( XAttribute condition , XElement sourcePropGroup , ref List < XElement > newGroups )
182+ {
183+ Assert . IsNotNull ( condition ) ;
184+
185+ Configuration . BaseType sourceType = condition . Value . Contains ( "Debug" ) ? Configuration . BaseType . Debug : Configuration . BaseType . Release ;
186+
187+ foreach ( Configuration configuration in SlnGen . Configurations )
188+ {
189+ // Does not match source property group, skip.
190+ if ( configuration . BaseConfigType != sourceType )
191+ {
192+ continue ;
193+ }
194+
195+ foreach ( Platform platform in SlnGen . Platforms )
196+ {
197+ XElement clone = new XElement ( sourcePropGroup ) ;
198+ clone . SetAttributeValue ( "Condition" , " '$(Configuration)|$(Platform)' == '" + configuration . Name + platform . Name + "|AnyCPU' " ) ;
199+
200+ XElement defines = clone . Element ( clone . Name . Namespace + "DefineConstants" ) ;
201+ if ( defines != null ) // There are other platform property groups that do not contain defines.
202+ {
203+ string configDefines = defines . Value ;
204+ if ( ! string . IsNullOrEmpty ( configuration . InvalidDefines ) )
205+ {
206+ configDefines = Regex . Replace ( defines . Value , configuration . InvalidDefines , "" ) ;
207+ }
208+
209+ // Don't modify platform defines if we are Unity's current platform.
210+ if ( platform != SlnGen . CurrentPlatform )
211+ {
212+ // Remove existing platform defines.
213+ configDefines = Regex . Replace ( configDefines , platformDefines , "" ) ;
214+ // Add defines for this platform.
215+ configDefines += ";" + string . Join ( ";" , platform . Defines ) ;
216+ }
217+
218+ // Remove duplicate separators
219+ configDefines = configDefines . Replace ( ";;" , ";" ) ;
220+ defines . SetValue ( configDefines ) ;
221+ }
222+
223+ newGroups . Add ( clone ) ;
224+ }
225+ }
226+ }
227+
228+ private static void AddConditionsToReferences ( XElement root , XNamespace nSpace )
229+ {
230+ IEnumerable < XElement > itemGroups = root . Elements ( nSpace + "ItemGroup" ) ;
231+ foreach ( XElement itemGroup in itemGroups )
232+ {
233+ ProcessItemGroup ( itemGroup ) ;
234+ }
235+ }
236+
237+ private static void ProcessItemGroup ( XElement itemGroup )
238+ {
239+ IEnumerable < XElement > references = itemGroup . Elements ( ) ;
240+ foreach ( XElement reference in references )
241+ {
242+ XAttribute attribute = reference . Attribute ( "Include" ) ;
243+ if ( attribute == null )
244+ {
245+ continue ;
246+ }
247+
248+ string include = attribute . Value ;
249+ switch ( reference . Name . LocalName )
250+ {
251+ // Script Assets
252+ case "Compile" :
253+ case "None" :
254+ break ;
255+ // Dll references
256+ case "Reference" :
257+ if ( include . ToLower ( ) . Contains ( "editor" ) )
258+ {
259+ reference . SetAttributeValue ( "Condition" , editorRefConditional ) ;
260+ }
261+ break ;
262+ // Project refs (asmdef, etc)
263+ case "ProjectReference" :
264+ if ( include . EndsWith ( "editor.csproj" , StringComparison . OrdinalIgnoreCase ) )
265+ {
266+ reference . SetAttributeValue ( "Condition" , editorRefConditional ) ;
267+ }
268+ break ;
269+ }
270+ }
271+ }
272+ }
273+ }
0 commit comments