├── config-build.gif ├── config-switch.gif ├── Scripts ├── SlnGen.cs.meta ├── Platform.cs.meta ├── Project.cs.meta ├── Configuration.cs.meta ├── SlnPostProcessor.cs.meta ├── CsProjPostProcessor.cs.meta ├── Platform.cs ├── Project.cs ├── Configuration.cs ├── SlnGen.cs ├── SlnPostProcessor.cs └── CsProjPostProcessor.cs ├── LICENSE.meta ├── README.md.meta ├── package.json.meta ├── Scripts.meta ├── SlnGen.Editor.asmdef.meta ├── SlnGen.Editor.asmdef ├── package.json ├── .gitignore ├── README.md ├── LICENSE ├── config-build.gif.meta └── config-switch.gif.meta /config-build.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhett12321/Unity-SlnGen/HEAD/config-build.gif -------------------------------------------------------------------------------- /config-switch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhett12321/Unity-SlnGen/HEAD/config-switch.gif -------------------------------------------------------------------------------- /Scripts/SlnGen.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6f70927980564cc197aed34b3fd2ba37 3 | timeCreated: 1542088494 -------------------------------------------------------------------------------- /Scripts/Platform.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4fb4baa410ec4a8a9c4b0e6d46f85d55 3 | timeCreated: 1542088317 -------------------------------------------------------------------------------- /Scripts/Project.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2201f837f878458081182ad691d70758 3 | timeCreated: 1542088410 -------------------------------------------------------------------------------- /Scripts/Configuration.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5e8903a988604f1b964c63799cccca9e 3 | timeCreated: 1542088359 -------------------------------------------------------------------------------- /Scripts/SlnPostProcessor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2fa737a8f9d54d1083d7feaec5eb9521 3 | timeCreated: 1540444810 -------------------------------------------------------------------------------- /Scripts/CsProjPostProcessor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1263f8242f2a42ffa5fda8df9ce3b08d 3 | timeCreated: 1542088735 -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e23fdbb548993eb4aa69c853677705d6 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a414c910330242647a36e5edb2c3816f 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7d166eac92d7d374faa59d8502c77254 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ae58ca522d010164e950ad24702a9dcf 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /SlnGen.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 092d2211592e8c54ca13c8140d74fc13 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /SlnGen.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SlnGen.Editor", 3 | "references": [], 4 | "optionalUnityReferences": [], 5 | "includePlatforms": [ 6 | "Editor" 7 | ], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": false 10 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.blackfeatherproductions.slngen", 3 | "displayName": "SlnGen", 4 | "version": "1.0.1", 5 | "unity": "2018.1", 6 | "description": "SlnGen is a editor plugin that generates project/solution configurations for different targets in the Unity Editor.", 7 | "keywords": [ 8 | "tool" 9 | ], 10 | "category": "Editor Utilities" 11 | } -------------------------------------------------------------------------------- /Scripts/Platform.cs: -------------------------------------------------------------------------------- 1 | namespace SlnGen.Editor 2 | { 3 | internal class Platform 4 | { 5 | // TODO Platform References 6 | public readonly string Name; 7 | public readonly string[] Defines; 8 | 9 | public Platform(string name, params string[] defines) 10 | { 11 | Name = name; 12 | Defines = defines; 13 | } 14 | 15 | public override string ToString() 16 | { 17 | return Name; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Scripts/Project.cs: -------------------------------------------------------------------------------- 1 | namespace SlnGen.Editor 2 | { 3 | internal class Project 4 | { 5 | public string FullProjectString; 6 | public string ProjectId; 7 | public bool IsEditorProject; 8 | 9 | public Project(string projectString) 10 | { 11 | FullProjectString = projectString; 12 | 13 | // double quote (1) + GUID Length with parenthesis (38) 14 | ProjectId = projectString.Substring(projectString.Length - 39, 38); 15 | IsEditorProject = projectString.ToLower().Contains("editor"); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [Ll]ibrary/ 2 | [Tt]emp/ 3 | [Oo]bj/ 4 | [Bb]uild/ 5 | [Bb]uilds/ 6 | Assets/AssetStoreTools* 7 | 8 | # Visual Studio cache directory 9 | .vs/ 10 | 11 | # Autogenerated VS/MD/Consulo solution and project files 12 | ExportedObj/ 13 | .consulo/ 14 | *.csproj 15 | *.unityproj 16 | *.sln 17 | *.suo 18 | *.tmp 19 | *.user 20 | *.userprefs 21 | *.pidb 22 | *.booproj 23 | *.svd 24 | *.pdb 25 | *.opendb 26 | 27 | # Unity3D generated meta files 28 | *.pidb.meta 29 | *.pdb.meta 30 | 31 | # Unity3D Generated File On Crash Reports 32 | sysinfo.txt 33 | 34 | # Builds 35 | *.apk 36 | *.unitypackage 37 | -------------------------------------------------------------------------------- /Scripts/Configuration.cs: -------------------------------------------------------------------------------- 1 | namespace SlnGen.Editor 2 | { 3 | internal class Configuration 4 | { 5 | public readonly string Name; 6 | public readonly string InvalidDefines; 7 | public readonly BaseType BaseConfigType; 8 | 9 | public readonly bool UseEditorReferences; 10 | 11 | public Configuration(string name, BaseType baseConfigType, bool useEditorReferences, string invalidDefines) 12 | { 13 | Name = name; 14 | BaseConfigType = baseConfigType; 15 | InvalidDefines = invalidDefines; 16 | UseEditorReferences = useEditorReferences; 17 | } 18 | 19 | public enum BaseType 20 | { 21 | Debug, 22 | Release 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity-SlnGen 2 | SlnGen is a editor plugin that generates project/solution configurations for different targets in the Unity Editor. 3 | 4 | ![VS Preview](https://github.com/jhett12321/Unity-SlnGen/raw/master/config-switch.gif) 5 | 6 | ![VS Build Config](https://github.com/jhett12321/Unity-SlnGen/raw/master/config-build.gif) 7 | 8 | ## Features 9 | * Adds solution configurations for Editor, Player, and Development Builds. 10 | * Compile-time checking (in IDE) for editor, and platform code usage. 11 | 12 | ## Compatibility 13 | Unity: 2018.1 or greater 14 | 15 | IDEs: Tested with Visual Studio 2017, Rider 2018. 16 | 17 | ## Installation 18 | ### 2018.3 or Higher 19 | 20 | Add the following line to your project's "/Packages/manifest.json" file. 21 | 22 | ``` 23 | "com.blackfeatherproductions.slngen": "https://github.com/jhett12321/Unity-SlnGen.git" 24 | ``` 25 | 26 | ### Others 27 | 28 | See [Releases](https://github.com/jhett12321/Unity-Slngen/releases) for a .unitypackage. 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jhett Black 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Scripts/SlnGen.cs: -------------------------------------------------------------------------------- 1 | namespace SlnGen.Editor 2 | { 3 | internal static class SlnGen 4 | { 5 | public static readonly Platform CurrentPlatform; 6 | public static readonly Platform[] Platforms; 7 | 8 | public static readonly Configuration DefaultConfig; 9 | public static readonly Configuration[] Configurations; 10 | 11 | static SlnGen() 12 | { 13 | Platforms = new[] 14 | { 15 | new Platform("_Current Platform_"), 16 | new Platform("Windows", "PLATFORM_STANDALONE", "PLATFORM_STANDALONE_WIN", "UNITY_STANDALONE", "UNITY_STANDALONE_WIN", "UNITY_EDITOR_WIN"), 17 | new Platform("OSX", "PLATFORM_STANDALONE", "PLATFORM_STANDALONE_OSX", "UNITY_STANDALONE", "UNITY_STANDALONE_OSX", "UNITY_EDITOR_OSX"), 18 | new Platform("Linux", "PLATFORM_STANDALONE", "PLATFORM_STANDALONE_LINUX", "UNITY_STANDALONE", "UNITY_STANDALONE_LINUX"), 19 | new Platform("Wii", "PLATFORM_WII", "UNITY_WII"), 20 | new Platform("iOS", "PLATFORM_IOS", "UNITY_IOS"), 21 | new Platform("Android", "PLATFORM_ANDROID", "UNITY_ANDROID"), 22 | new Platform("PS4", "PLATFORM_PS4", "UNITY_PS4"), 23 | new Platform("Xbox One", "PLATFORM_XBOXONE", "UNITY_XBOXONE"), 24 | new Platform("Tizen", "PLATFORM_TIZEN", "UNITY_TIZEN"), 25 | new Platform("tvOS", "PLATFORM_TVOS", "UNITY_TVOS"), 26 | new Platform("WebGL", "PLATFORM_WEBGL", "UNITY_WEBGL") 27 | }; 28 | 29 | Configurations = new[] 30 | { 31 | new Configuration("Editor", Configuration.BaseType.Debug, true, null), 32 | new Configuration("Player (Debug)", Configuration.BaseType.Debug, false, @"\bUNITY_EDITOR\b|\bUNITY_EDITOR_64\b|\bUNITY_EDITOR_WIN\b|\bUNITY_EDITOR_OSX\b|\bUNITY_EDITOR_LINUX\b"), 33 | new Configuration("Player", Configuration.BaseType.Release, false, @"\bDEBUG\b|\bUNITY_EDITOR\b|\bUNITY_EDITOR_64\b|\bUNITY_EDITOR_WIN\b|\bUNITY_EDITOR_OSX\b|\bUNITY_EDITOR_LINUX\b"), 34 | }; 35 | 36 | DefaultConfig = Configurations[0]; // Editor 37 | CurrentPlatform = Platforms[0]; // Current Platform 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /config-build.gif.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 377df12377eea1b479a4d428c44fbef7 3 | TextureImporter: 4 | fileIDToRecycleName: {} 5 | externalObjects: {} 6 | serializedVersion: 7 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 0 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | grayScaleToAlpha: 0 27 | generateCubemap: 6 28 | cubemapConvolution: 0 29 | seamlessCubemap: 0 30 | textureFormat: 1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | serializedVersion: 2 34 | filterMode: -1 35 | aniso: -1 36 | mipBias: -100 37 | wrapU: 1 38 | wrapV: 1 39 | wrapW: 1 40 | nPOTScale: 0 41 | lightmap: 0 42 | compressionQuality: 50 43 | spriteMode: 1 44 | spriteExtrude: 1 45 | spriteMeshType: 1 46 | alignment: 0 47 | spritePivot: {x: 0.5, y: 0.5} 48 | spritePixelsToUnits: 100 49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 50 | spriteGenerateFallbackPhysicsShape: 1 51 | alphaUsage: 1 52 | alphaIsTransparency: 1 53 | spriteTessellationDetail: -1 54 | textureType: 8 55 | textureShape: 1 56 | singleChannelComponent: 0 57 | maxTextureSizeSet: 0 58 | compressionQualitySet: 0 59 | textureFormatSet: 0 60 | platformSettings: 61 | - serializedVersion: 2 62 | buildTarget: DefaultTexturePlatform 63 | maxTextureSize: 2048 64 | resizeAlgorithm: 0 65 | textureFormat: -1 66 | textureCompression: 1 67 | compressionQuality: 50 68 | crunchedCompression: 0 69 | allowsAlphaSplitting: 0 70 | overridden: 0 71 | androidETC2FallbackOverride: 0 72 | spriteSheet: 73 | serializedVersion: 2 74 | sprites: [] 75 | outline: [] 76 | physicsShape: [] 77 | bones: [] 78 | spriteID: c7082e1105a8de449a674ac2f727720d 79 | vertices: [] 80 | indices: 81 | edges: [] 82 | weights: [] 83 | spritePackingTag: 84 | pSDRemoveMatte: 0 85 | pSDShowRemoveMatteOption: 0 86 | userData: 87 | assetBundleName: 88 | assetBundleVariant: 89 | -------------------------------------------------------------------------------- /config-switch.gif.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fe45b44642dea9e46bdd9ecdbb869847 3 | TextureImporter: 4 | fileIDToRecycleName: {} 5 | externalObjects: {} 6 | serializedVersion: 7 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 0 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | grayScaleToAlpha: 0 27 | generateCubemap: 6 28 | cubemapConvolution: 0 29 | seamlessCubemap: 0 30 | textureFormat: 1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | serializedVersion: 2 34 | filterMode: -1 35 | aniso: -1 36 | mipBias: -100 37 | wrapU: 1 38 | wrapV: 1 39 | wrapW: 1 40 | nPOTScale: 0 41 | lightmap: 0 42 | compressionQuality: 50 43 | spriteMode: 1 44 | spriteExtrude: 1 45 | spriteMeshType: 1 46 | alignment: 0 47 | spritePivot: {x: 0.5, y: 0.5} 48 | spritePixelsToUnits: 100 49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 50 | spriteGenerateFallbackPhysicsShape: 1 51 | alphaUsage: 1 52 | alphaIsTransparency: 1 53 | spriteTessellationDetail: -1 54 | textureType: 8 55 | textureShape: 1 56 | singleChannelComponent: 0 57 | maxTextureSizeSet: 0 58 | compressionQualitySet: 0 59 | textureFormatSet: 0 60 | platformSettings: 61 | - serializedVersion: 2 62 | buildTarget: DefaultTexturePlatform 63 | maxTextureSize: 2048 64 | resizeAlgorithm: 0 65 | textureFormat: -1 66 | textureCompression: 1 67 | compressionQuality: 50 68 | crunchedCompression: 0 69 | allowsAlphaSplitting: 0 70 | overridden: 0 71 | androidETC2FallbackOverride: 0 72 | spriteSheet: 73 | serializedVersion: 2 74 | sprites: [] 75 | outline: [] 76 | physicsShape: [] 77 | bones: [] 78 | spriteID: 87645178cab579541a8acb8cbced9df2 79 | vertices: [] 80 | indices: 81 | edges: [] 82 | weights: [] 83 | spritePackingTag: 84 | pSDRemoveMatte: 0 85 | pSDShowRemoveMatteOption: 0 86 | userData: 87 | assetBundleName: 88 | assetBundleVariant: 89 | -------------------------------------------------------------------------------- /Scripts/SlnPostProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace SlnGen.Editor 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Text; 7 | using System.Threading; 8 | using UnityEditor; 9 | using UnityEngine; 10 | using UnityEngine.Assertions; 11 | 12 | internal class SlnPostProcessor : AssetPostprocessor 13 | { 14 | private const int SLN_WRITE_DELAY_MS = 500; 15 | 16 | private static string OnGeneratedSlnSolution(string path, string content) 17 | { 18 | try 19 | { 20 | StringBuilder stringBuilder = new StringBuilder(); 21 | using (StringReader stringReader = new StringReader(content)) 22 | { 23 | List projects = GetProjects(stringReader, stringBuilder); 24 | 25 | CopyUntilLine(stringReader, stringBuilder, " GlobalSection(SolutionConfigurationPlatforms) = preSolution"); 26 | CreateConfigurationPlatforms(stringBuilder); 27 | 28 | SkipTo(stringReader, " EndGlobalSection"); 29 | stringBuilder.AppendLine(" EndGlobalSection"); 30 | 31 | CopyUntilLine(stringReader, stringBuilder, " GlobalSection(ProjectConfigurationPlatforms) = postSolution"); 32 | 33 | CreateConfigurations(stringBuilder, projects); 34 | 35 | SkipTo(stringReader, " EndGlobalSection"); 36 | stringBuilder.AppendLine(" EndGlobalSection"); 37 | 38 | stringBuilder.Append(stringReader.ReadToEnd()); 39 | } 40 | 41 | string newSln = stringBuilder.ToString(); 42 | 43 | // TODO Unity Bug Workaround 44 | WriteSlnDelayed(path, newSln); 45 | 46 | return newSln; 47 | } 48 | catch (Exception e) 49 | { 50 | // Exception occurred. Return the unmodified solution. 51 | Debug.LogException(e); 52 | return content; 53 | } 54 | } 55 | 56 | private static void WriteSlnDelayed(string path, string newSln) 57 | { 58 | // HACK - we write the sln file after a short delay as Unity does not use the returned value. 59 | ThreadPool.QueueUserWorkItem(state => 60 | { 61 | Thread.Sleep(SLN_WRITE_DELAY_MS); 62 | File.WriteAllText(path, newSln); 63 | }); 64 | } 65 | 66 | private static List GetProjects(StringReader stringReader, StringBuilder builder) 67 | { 68 | List projects = new List(); 69 | 70 | for (;;) 71 | { 72 | string line = stringReader.ReadLine(); 73 | if (line == null) 74 | { 75 | throw new EndOfStreamException("Reached the end of the sln before finding project."); 76 | } 77 | 78 | builder.AppendLine(line); 79 | 80 | if (line.StartsWith("Project")) 81 | { 82 | projects.Add(new Project(line)); 83 | } 84 | else if (line == "Global") 85 | { 86 | break; 87 | } 88 | } 89 | 90 | return projects; 91 | } 92 | 93 | /// 94 | /// Skips to the specified line, without writing. 95 | /// 96 | private static void SkipTo(StringReader stringReader, string line) 97 | { 98 | ProcessSeek(stringReader, line, false, null); 99 | } 100 | 101 | /// 102 | /// Skips to the specified line while writing lines to the specified string builder. 103 | /// 104 | private static void CopyUntilLine(StringReader stringReader, StringBuilder stringBuilder, string line) 105 | { 106 | ProcessSeek(stringReader, line, true, stringBuilder); 107 | } 108 | 109 | private static void ProcessSeek(StringReader stringReader, string skipToLine, bool write, StringBuilder builder) 110 | { 111 | Assert.IsTrue(!write || builder != null); 112 | 113 | for (;;) 114 | { 115 | string line = stringReader.ReadLine(); 116 | if (line == null) 117 | { 118 | throw new EndOfStreamException("Reached the end of the sln before finding " + skipToLine); 119 | } 120 | 121 | if (write) 122 | { 123 | builder.AppendLine(line); 124 | } 125 | 126 | if (line == skipToLine) 127 | { 128 | break; 129 | } 130 | } 131 | } 132 | 133 | private static void CreateConfigurationPlatforms(StringBuilder stringBuilder) 134 | { 135 | foreach (Configuration configuration in SlnGen.Configurations) 136 | { 137 | foreach (Platform platform in SlnGen.Platforms) 138 | { 139 | stringBuilder.AppendFormat(" {0}|{1} = {0}|{1}", configuration.Name, platform.Name); 140 | stringBuilder.AppendLine(); 141 | } 142 | } 143 | } 144 | 145 | private static void CreateConfigurations(StringBuilder stringBuilder, List projects) 146 | { 147 | foreach (Project project in projects) 148 | { 149 | foreach (Configuration configuration in SlnGen.Configurations) 150 | { 151 | foreach (Platform platform in SlnGen.Platforms) 152 | { 153 | stringBuilder.AppendFormat(" {0}.{1}|{2}.ActiveCfg = {1}{2}|Any CPU", project.ProjectId, configuration.Name, platform.Name); 154 | stringBuilder.AppendLine(); 155 | 156 | if (configuration.UseEditorReferences || !project.IsEditorProject) 157 | { 158 | stringBuilder.AppendFormat(" {0}.{1}|{2}.Build.0 = {1}{2}|Any CPU", project.ProjectId, configuration.Name, platform.Name); 159 | stringBuilder.AppendLine(); 160 | } 161 | } 162 | } 163 | } 164 | } 165 | } 166 | } -------------------------------------------------------------------------------- /Scripts/CsProjPostProcessor.cs: -------------------------------------------------------------------------------- 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 platformDefines = new HashSet(); 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 conditions = new List(); 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 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 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 | /// 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 | /// 113 | private static void CopyDefinesToReleaseGroup(List 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 GeneratePropertyGroups(List sources, XNamespace nSpace) 152 | { 153 | List newElements = new List(); 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 | /// 179 | /// Creates duplicate PropertyGroups nodes for all platforms, replacing the platform name, and defines for that platform. 180 | /// 181 | private static void GenerateProjectConfigurations(XAttribute condition, XElement sourcePropGroup, ref List 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 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 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 | } --------------------------------------------------------------------------------