summaryrefslogtreecommitdiff
path: root/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs
blob: 08a22475a42afd12becd11773f4af375ef485675 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
//-----------------------------------------------------------------------
// <copyright file="AssemblyImportSettingsUtilities.cs" company="Sirenix IVS">
// Copyright (c) 2018 Sirenix IVS
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------

#if UNITY_EDITOR

namespace VRC.Udon.Serialization.OdinSerializer.Utilities.Editor
{
    using System;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using UnityEditor;

    /// <summary>
    /// Defines how an assembly's import settings should be configured.
    /// </summary>
    public enum OdinAssemblyImportSettings
    {
        /// <summary>
        /// Include the assembly in the build, but not in the editor.
        /// </summary>
        IncludeInBuildOnly,
        /// <summary>
        /// Include the assembly in the editor, but not in the build.
        /// </summary>
        IncludeInEditorOnly,
        /// <summary>
        /// Include the assembly in both the build and in the editor.
        /// </summary>
        IncludeInAll,
        /// <summary>
        /// Exclude the assembly from both the build and from the editor.
        /// </summary>
        ExcludeFromAll,
    }

    /// <summary>
    /// Utility for correctly setting import on OdinSerializer assemblies based on platform and scripting backend.
    /// </summary>
    public static class AssemblyImportSettingsUtilities
    {
        private static MethodInfo getPropertyIntMethod;
        private static MethodInfo getScriptingBackendMethod;
        private static MethodInfo getApiCompatibilityLevelMethod;
        private static MethodInfo apiCompatibilityLevelProperty;

        /// <summary>
        /// All valid Unity BuildTarget platforms.
        /// </summary>
        public static readonly ImmutableList<BuildTarget> Platforms; 

        /// <summary>
        /// All valid Unity BuildTarget platforms that support Just In Time compilation.
        /// </summary>
        public static readonly ImmutableList<BuildTarget> JITPlatforms;

        /// <summary>
        /// All scripting backends that support JIT.
        /// </summary>
        public static readonly ImmutableList<ScriptingImplementation> JITScriptingBackends;

        /// <summary>
        /// All API compatibility levels that support JIT.
        /// </summary>
        public static readonly ImmutableList<ApiCompatibilityLevel> JITApiCompatibilityLevels;

        static AssemblyImportSettingsUtilities()
        {
            // Different methods required for getting the current scripting backend from different versions of the Unity Editor.
            getPropertyIntMethod = typeof(PlayerSettings).GetMethod("GetPropertyInt", Flags.StaticPublic, null, new Type[] { typeof(string), typeof(BuildTargetGroup) }, null);
            getScriptingBackendMethod = typeof(PlayerSettings).GetMethod("GetScriptingBackend", Flags.StaticPublic, null, new Type[] { typeof(BuildTargetGroup) }, null);

            // Diffferent methods required for getting the current api level from different versions of the Unity Editor.
            getApiCompatibilityLevelMethod = typeof(PlayerSettings).GetMethod("GetApiCompatibilityLevel", Flags.StaticPublic, null, new Type[] { typeof(BuildTargetGroup) }, null);
            var apiLevelProperty = typeof(PlayerSettings).GetProperty("apiCompatibilityLevel", Flags.StaticPublic);
            apiCompatibilityLevelProperty = apiLevelProperty != null ? apiLevelProperty.GetGetMethod() : null;

            // All valid BuildTarget values.
            Platforms = new ImmutableList<BuildTarget>(Enum.GetValues(typeof(BuildTarget))
                .Cast<BuildTarget>()
                .Where(t => t >= 0 && typeof(BuildTarget).GetMember(t.ToString())[0].IsDefined(typeof(ObsoleteAttribute), false) == false)
                .ToArray());

            // All BuildTarget values that support JIT.
            JITPlatforms = new ImmutableList<BuildTarget>(Platforms
                .Where(i => i.ToString().StartsWith("StandaloneOSX")) // Unity 2017.3 replaced StandaloneOSXIntel, StandaloneOSXIntel64 and StandaloneOSXUniversal with StandaloneOSX.
                .Append(new BuildTarget[]
                {
                    BuildTarget.StandaloneWindows,
                    BuildTarget.StandaloneWindows64,
                    BuildTarget.StandaloneLinux,
                    BuildTarget.StandaloneLinux64,
                    BuildTarget.StandaloneLinuxUniversal,
                    BuildTarget.Android
                })
                .ToArray());

            // All scripting backends that support JIT.
            JITScriptingBackends = new ImmutableList<ScriptingImplementation>(new ScriptingImplementation[]
            {
                ScriptingImplementation.Mono2x,
            });

            // Names of all api levels that support JIT.
            string[] jitApiNames = new string[]
            {
                "NET_2_0",
                "NET_2_0_Subset",
                "NET_4_6",
                "NET_Web",  // TODO: Does NET_Web support JIT stuff?
                "NET_Micro" // TODO: Does NET_Micro support JIT stuff?
            };

            var apiLevelNames = Enum.GetNames(typeof(ApiCompatibilityLevel));

            JITApiCompatibilityLevels = new ImmutableList<ApiCompatibilityLevel>(jitApiNames
                .Where(x => apiLevelNames.Contains(x))
                .Select(x => (ApiCompatibilityLevel)Enum.Parse(typeof(ApiCompatibilityLevel), x))
                .ToArray());
        }

        /// <summary>
        /// Set the import settings on the assembly.
        /// </summary>
        /// <param name="assemblyFilePath">The path to the assembly to configure import settings from.</param>
        /// <param name="importSettings">The import settings to configure for the assembly at the path.</param>
        public static void SetAssemblyImportSettings(BuildTarget platform, string assemblyFilePath, OdinAssemblyImportSettings importSettings)
        {
            bool includeInBuild = false;
            bool includeInEditor = false;

            switch (importSettings)
            {
                case OdinAssemblyImportSettings.IncludeInAll:
                    includeInBuild = true;
                    includeInEditor = true;
                    break;

                case OdinAssemblyImportSettings.IncludeInBuildOnly:
                    includeInBuild = true;
                    break;

                case OdinAssemblyImportSettings.IncludeInEditorOnly:
                    includeInEditor = true;
                    break;

                case OdinAssemblyImportSettings.ExcludeFromAll:
                    break;
            }

            SetAssemblyImportSettings(platform, assemblyFilePath, includeInBuild, includeInEditor);
        }

        /// <summary>
        /// Set the import settings on the assembly.
        /// </summary>
        /// <param name="assemblyFilePath">The path to the assembly to configure import settings from.</param>
        /// <param name="includeInBuild">Indicates if the assembly should be included in the build.</param>
        /// <param name="includeInEditor">Indicates if the assembly should be included in the Unity editor.</param>
        public static void SetAssemblyImportSettings(BuildTarget platform, string assemblyFilePath, bool includeInBuild, bool includeInEditor)
        {
            if (File.Exists(assemblyFilePath) == false)
            {
                throw new FileNotFoundException(assemblyFilePath);
            }

            var importer = (PluginImporter)AssetImporter.GetAtPath(assemblyFilePath);
            if (importer == null)
            {
                throw new InvalidOperationException("Failed to get PluginImporter for " + assemblyFilePath);
            }

            bool updateImportSettings = 
                importer.GetCompatibleWithAnyPlatform() // If the 'any platform' flag is true, then reapply settings no matter what to ensure that everything is correct.
                //|| Platforms.Any(p => importer.GetCompatibleWithPlatform(p) != includeInBuild)
                || importer.GetCompatibleWithPlatform(platform) != includeInBuild 
                || importer.GetCompatibleWithEditor() != includeInEditor;

            // Apply new import settings if necessary.
            if (updateImportSettings)
            {
                importer.SetCompatibleWithAnyPlatform(false);
                //Platforms.ForEach(p => importer.SetCompatibleWithPlatform(p, includeInBuild));
                importer.SetCompatibleWithPlatform(platform, includeInBuild);
                importer.SetCompatibleWithEditor(includeInEditor);

                importer.SaveAndReimport();
            }
        }

        /// <summary>
        /// Gets the current scripting backend for the build from the Unity editor. This method is Unity version independent.
        /// </summary>
        /// <returns></returns>
        public static ScriptingImplementation GetCurrentScriptingBackend()
        {
            var buildGroup = EditorUserBuildSettings.selectedBuildTargetGroup;

            if (getScriptingBackendMethod != null)
            {
                return (ScriptingImplementation)getScriptingBackendMethod.Invoke(null, new object[] { buildGroup });
            }
            else if (getPropertyIntMethod != null)
            {
                return (ScriptingImplementation)getPropertyIntMethod.Invoke(null, new object[] { "ScriptingBackend", buildGroup });
            }

            throw new InvalidOperationException("Was unable to get the current scripting backend!");
        }

        /// <summary>
        /// Gets the current API compatibility level from the Unity Editor. This method is Unity version independent.
        /// </summary>
        /// <returns></returns>
        public static ApiCompatibilityLevel GetCurrentApiCompatibilityLevel()
        {
            if (getApiCompatibilityLevelMethod != null)
            {
                var buildGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
                return (ApiCompatibilityLevel)getApiCompatibilityLevelMethod.Invoke(null, new object[] { buildGroup });
            }
            else if (apiCompatibilityLevelProperty != null)
            {
                return (ApiCompatibilityLevel)apiCompatibilityLevelProperty.Invoke(null, null);
            }

            throw new InvalidOperationException("Was unable to get the current api compatibility level!");
        }

        /// <summary>
        /// Gets a value that indicates if the specified platform supports JIT.
        /// </summary>
        /// <param name="platform">The platform to test.</param>
        /// <returns><c>true</c> if the platform supports JIT; otherwise <c>false</c>.</returns>
        public static bool PlatformSupportsJIT(BuildTarget platform)
        {
            return JITPlatforms.Contains(platform);
        }

        /// <summary>
        /// Gets a value that indicates if the specified scripting backend supports JIT.
        /// </summary>
        /// <param name="backend">The backend to test.</param>
        /// <returns><c>true</c> if the backend supports JIT; otherwise <c>false</c>.</returns>
        public static bool ScriptingBackendSupportsJIT(ScriptingImplementation backend)
        {
            return JITScriptingBackends.Contains(backend);
        }

        /// <summary>
        /// Gets a value that indicates if the specified api level supports JIT.
        /// </summary>
        /// <param name="apiLevel">The api level to test.</param>
        /// <returns><c>true</c> if the api level supports JIT; otherwise <c>false</c>.</returns>
        public static bool ApiCompatibilityLevelSupportsJIT(ApiCompatibilityLevel apiLevel)
        {
            return JITApiCompatibilityLevels.Contains(apiLevel);
        }

        /// <summary>
        /// Gets a value that indicates if the specified build settings supports JIT.
        /// </summary>
        /// <param name="platform">The platform build setting.</param>
        /// <param name="backend">The scripting backend build settting.</param>
        /// <param name="apiLevel">The api level build setting.</param>
        /// <returns><c>true</c> if the build settings supports JIT; otherwise <c>false</c>.</returns>
        public static bool IsJITSupported(BuildTarget platform, ScriptingImplementation backend, ApiCompatibilityLevel apiLevel)
        {
            return PlatformSupportsJIT(platform) && ScriptingBackendSupportsJIT(backend) && ApiCompatibilityLevelSupportsJIT(apiLevel);
        }
    }
}

#endif