public void OnPreprocessBuild(BuildReport report) { var target = report.summary.platform; var path = report.summary.outputPath; if (BuildType == TrimmerBuildType.None) { NonTrimmerBuild(target); } // Run options' PreprocessBuild includesAnyOption = false; foreach (var option in GetCurrentEditProfile().OrderBy(o => o.PostprocessOrder)) { var inclusion = currentProfile == null ? OptionInclusion.Remove : currentProfile.GetInclusionOf(option, target); includesAnyOption |= ((inclusion & OptionInclusion.Option) != 0); if ((option.Capabilities & OptionCapabilities.ConfiguresBuild) != 0) { option.PreprocessBuild(report, inclusion); } } GenerateBuildInfo(report.summary.guid); string defines = null; #if UNITY_2020_1_OR_NEWER var extraScriptingDefines = OptionHelper.currentBuildOptions.extraScriptingDefines; if (extraScriptingDefines != null && extraScriptingDefines.Length > 0) { defines = extraScriptingDefines.Join(); } #else var targetGroup = BuildPipeline.GetBuildTargetGroup(target); defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(targetGroup); #endif Debug.Log(string.Format( "Trimmer: Building profile '{0}' for '{1}' to '{2}'\nIncluded: {3}\nSymbols: {4}", currentProfile != null ? currentProfile.name : "null", target, path, GetCurrentEditProfile() .Where(o => { if (currentProfile == null) { return(false); } var inclusion = currentProfile.GetInclusionOf(o, target); return(inclusion.HasFlag(OptionInclusion.Feature) || inclusion.HasFlag(OptionInclusion.Option)); }) .Select(o => o.Name) .Join(), defines )); }
/// <summary> /// Determine all the scripting define symbols set by a build profile for the given build target. /// </summary> static void GetScriptingDefineSymbols(BuildProfile buildProfile, BuildTarget target, HashSet <string> symbols) { var includesAnyOption = false; foreach (var option in GetCurrentEditProfile().OrderBy(o => o.PostprocessOrder)) { var inclusion = buildProfile == null ? OptionInclusion.Remove : buildProfile.GetInclusionOf(option, target); includesAnyOption |= ((inclusion & OptionInclusion.Option) != 0); option.GetScriptingDefineSymbols(inclusion, symbols); } if (!includesAnyOption) { symbols.Add(NO_TRIMMER); } }
/// <summary> /// Build a profile with the given build options, synchronously starting the build. /// </summary> /// <remarks> /// The `BuildPlayerOptions` will be passed through the profile's Options' /// <see cref="Option.PrepareBuild"/>, which can modify it before the build is started. /// /// > [!NOTE] /// > If you do not set `options.locationPathName` and no option sets /// > it in the `PrepareBuild` callback, then a save dialog will be shown. /// /// > [!WARNING] /// > With this method it's possible to build for a build target even if this /// > target is not the active build target. This can lead to issues where /// > build code for the given target is not run. /// </remarks> public static BuildReport BuildSync(BuildProfile buildProfile, BuildPlayerOptions options) { // Prepare build BuildType = TrimmerBuildType.Profile; currentProfile = buildProfile; // Add Trimmer scripting define symbols AddScriptingDefineSymbols(buildProfile, ref options); // Run options' PrepareBuild foreach (var option in GetCurrentEditProfile().OrderBy(o => o.PostprocessOrder)) { if ((option.Capabilities & OptionCapabilities.ConfiguresBuild) == 0) { continue; } var inclusion = buildProfile == null ? OptionInclusion.Remove : buildProfile.GetInclusionOf(option, options.target); options = option.PrepareBuild(options, inclusion); } // Ask for location if none has been set if (string.IsNullOrEmpty(options.locationPathName)) { if (Application.isBatchMode) { // Cannot pick path in batch mode throw new Exception($"Trimmer: No build path set by profile or in build player options."); } else { options.locationPathName = PickBuildLocation(options.target); if (string.IsNullOrEmpty(options.locationPathName)) { Debug.Log("Cancelled build location dialog"); return(null); } } } // Make sure the path has the right extension // Call internal method: // string PostprocessBuildPlayer.GetExtensionForBuildTarget(BuildTargetGroup targetGroup, BuildTarget target, BuildOptions options) var PostprocessBuildPlayer = typeof(BuildPipeline).Assembly.GetType("UnityEditor.PostprocessBuildPlayer"); if (PostprocessBuildPlayer == null) { Debug.LogWarning("Could not find PostprocessBuildPlayer to determine build file extension."); } else { var GetExtensionForBuildTarget = PostprocessBuildPlayer.GetMethod("GetExtensionForBuildTarget", BindingFlags.Public | BindingFlags.Static); if (GetExtensionForBuildTarget == null) { Debug.LogWarning("Could not find GetExtensionForBuildTarget to determine build file extension."); } else { var args = new object[] { options.targetGroup, options.target, options.options }; var ext = (string)GetExtensionForBuildTarget.Invoke(null, args); var current = Path.GetExtension(options.locationPathName); if (current.Length > 0) { current = current.Substring(1); // Remove leading dot } if (!string.IsNullOrEmpty(ext) && Path.GetExtension(options.locationPathName).EqualsIgnoringCase(current)) { options.locationPathName += "." + ext; } } } // Run the build OptionHelper.currentBuildOptions = options; var report = BuildPipeline.BuildPlayer(options); if (report.summary.result != BuildResult.Succeeded) { OnBuildError(report); } else { Debug.Log(string.Format("Trimmer: Built {0} to '{1}'", options.target, options.locationPathName)); buildProfile.SetLastBuildPath(options.target, options.locationPathName); } currentProfile = null; OptionHelper.currentBuildOptions = default; return(report); }
/// <summary> /// Entry point for Unity Cloud builds. /// </summary> /// <remarks> /// If you don't configure anything, Unity Cloud Build will build without a /// profile, all Options will use their default value and will be removed. /// /// Add the name of the Build Profile you want to build with to the target name /// in Unity Cloud Build, enclosed in double underscores, e.g. `__Profile Name__`. /// Note that since the target name can contain only alphanumeric characters, /// spaces, dashes and underscores, those characters cannot appear in the profile /// name either. /// /// Also note that the ability for Options to set build options is limited, /// currently only setting a custom scene list is supported. /// </remarks> public static void UnityCloudBuild(BuildManifestObject manifest) { BuildType = TrimmerBuildType.CloudBuild; Debug.Log("UnityCloudBuild: Parsing profile name..."); // Get profile name from could build target name string targetName; if (!manifest.TryGetValue("cloudBuildTargetName", out targetName)) { Debug.LogError("Could not get target name from cloud build manifest."); } else { var match = Regex.Match(targetName, @"__([\w\-. ]+)__"); if (match.Success) { targetName = match.Groups[1].Value; Debug.Log("Parsed build profile name from target name: " + targetName); } } Debug.Log("UnityCloudBuild: Looking for profile..."); BuildProfile buildProfile = null; if (!string.IsNullOrEmpty(targetName)) { buildProfile = BuildProfile.Find(targetName); if (buildProfile == null) { Debug.LogError("Build Profile named '" + targetName + "' could not be found."); return; } } if (buildProfile == null) { Debug.LogWarning("No Build Profile selected. Add the Build Profile enclosed in double underscores (__) to the target name."); return; } Debug.Log("UnityCloudBuild: Running PrepareBuild callbacks..."); // Prepare build var options = GetDefaultOptions(EditorUserBuildSettings.activeBuildTarget); currentProfile = buildProfile; // Run options' PrepareBuild foreach (var option in GetCurrentEditProfile().OrderBy(o => o.PostprocessOrder)) { if ((option.Capabilities & OptionCapabilities.ConfiguresBuild) == 0) { continue; } var inclusion = buildProfile == null ? OptionInclusion.Remove : buildProfile.GetInclusionOf(option, options.target); options = option.PrepareBuild(options, inclusion); } // Cloud Build doesn't allow changing BuildPlayerOptions.extraScriptingDefines, // so we have to apply scripting define symbols to player settings ApplyScriptingDefineSymbolsToPlayerSettings(buildProfile, options.target); Debug.Log("UnityCloudBuild: Apply scenes..."); // Apply scenes if (options.scenes != null && options.scenes.Length > 0) { var scenes = new EditorBuildSettingsScene[options.scenes.Length]; for (int i = 0; i < scenes.Length; i++) { scenes[i] = new EditorBuildSettingsScene( options.scenes[i], true ); } EditorBuildSettings.scenes = scenes; } OptionHelper.currentBuildOptions = options; Debug.Log("UnityCloudBuild: Done!"); }