public Job(DistroBase distro) { this.distro = distro; this.profile = null; this.target = BuildTarget.NoTarget; this.outputPath = null; }
public Job(BuildProfile profile, BuildTarget target, string outputPath = null) { this.profile = profile; this.target = target; this.outputPath = outputPath; this.distro = null; }
/// <summary> /// Construct a result that represents a Trimmer error. /// </summary> public static ProfileBuildResult Error(BuildProfile profile, string error) { return(new ProfileBuildResult() { profile = profile, error = error, report = null, }); }
protected void OnEnable() { profile = (EditableProfile)target; editorProfile = target as EditorProfile; buildProfile = target as BuildProfile; options = SortOptionsByCategoryAndName(profile.EditProfile); // Invalidate source profile dropdown sourceProfiles = null; sourceProfilesNames = null; }
/// <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> /// For builds where BuildPlayerOptions is not available, apply the /// required scripting define symbols to the the player settings. /// </summary> static void ApplyScriptingDefineSymbolsToPlayerSettings(BuildProfile buildProfile, BuildTarget target) { // Run options' PreprocessBuild and collect scripting define symbols var targetGroup = BuildPipeline.GetBuildTargetGroup(target); previousScriptingDefineSymbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(targetGroup); var symbols = GetCurrentScriptingDefineSymbols(targetGroup); // Remove all symbols previously added by Trimmer symbols.RemoveWhere(d => d.StartsWith(Option.DEFINE_PREFIX)); symbols.Remove(NO_TRIMMER); // Add Trimmer's symbols GetScriptingDefineSymbols(buildProfile, target, symbols); // Apply scripting define symbols PlayerSettings.SetScriptingDefineSymbolsForGroup(targetGroup, string.Join(";", symbols.ToArray())); }
/// <summary> /// Build a profile for its default targets and with the default build options. /// </summary> public static void Build(BuildProfile profile, IBuildsCompleteListener onComplete = null) { var targets = profile.BuildTargets.ToArray(); var jobs = new BuildRunner.Job[targets.Length]; for (int i = 0; i < targets.Length; i++) { jobs[i] = new BuildRunner.Job() { profile = profile, target = targets[i], }; } var runner = ScriptableObject.CreateInstance <BuildRunner>(); runner.Run(jobs, onComplete, TrimmerPrefs.RestoreActiveBuildTarget); }
/// <summary> /// Add Trimmer's scripting define symbols to the build player option's extraScriptingDefines. /// </summary> static void AddScriptingDefineSymbols(BuildProfile buildProfile, ref BuildPlayerOptions options) { #if !UNITY_2020_1_OR_NEWER // Before Unity 2020.1 BuildPlayerOptions.extraScriptingDefines didn't exist, // fall back to changing player settings ApplyScriptingDefineSymbolsToPlayerSettings(buildProfile, options.target); #else var symbols = new HashSet <string>(); if (options.extraScriptingDefines != null) { symbols.AddRange(options.extraScriptingDefines); } GetScriptingDefineSymbols(buildProfile, options.target, symbols); options.extraScriptingDefines = symbols.ToArray(); #endif }
/// <summary> /// Build a specific target of a profile with the default build options. /// </summary> /// <param name="profile">Profile to build</param> /// <param name="target">Target to build, needs to be part of profile</param> public static void Build(BuildProfile profile, BuildTarget target, IBuildsCompleteListener onComplete = null) { if (profile.BuildTargets != null && profile.BuildTargets.Any() && !profile.BuildTargets.Contains(target)) { var err = $"Build target {target} is not part of the build profile {profile.name}"; if (onComplete != null) { onComplete.OnComplete(false, new[] { ProfileBuildResult.Error(profile, err) }); } else { Debug.LogError(err); } return; } var runner = ScriptableObject.CreateInstance <BuildRunner>(); runner.Run(new[] { new BuildRunner.Job(profile, target) }, onComplete, TrimmerPrefs.RestoreActiveBuildTarget); }
void SourceProfileGUI() { if (editorProfile != null) { EditorGUILayout.BeginHorizontal(); { if (sourceProfiles == null) { sourceProfiles = BuildProfile.AllBuildProfiles.Prepend(null).ToArray(); sourceProfilesNames = sourceProfiles.Select(p => p == null ? "Editor" : p.name).ToArray(); } var sourceProfile = editorProfile.EditorSourceProfile; int selected = Array.IndexOf(sourceProfiles, sourceProfile); var newSelected = EditorGUILayout.Popup("Source Profile", selected, sourceProfilesNames); if (selected != newSelected) { BuildProfile newProfile = null; if (newSelected > 0) { newProfile = sourceProfiles[newSelected]; } editorProfile.EditorSourceProfile = newProfile; } GUI.enabled = (sourceProfile != null); if (GUILayout.Button("Edit", EditorStyles.miniButton, GUILayout.Width(40))) { Selection.activeObject = sourceProfile; EditorApplication.ExecuteMenuItem("Window/Inspector"); } GUI.enabled = true; } EditorGUILayout.EndHorizontal(); } }
/// <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); }
public static void Build(IBuildsCompleteListener onComplete = null) { string commandLineBuildPath = null; string profileName = null; var buildActiveTarget = false; if (Application.isBatchMode) { string[] args = Environment.GetCommandLineArgs(); for (int i = 0; i < args.Length; i++) { if (args[i].EqualsIgnoringCase("-profileName")) { if (i + 1 == args.Length || args[i + 1].StartsWith("-")) { throw new Exception("-profileName needs to be followed by a profile name."); } profileName = args[++i]; } else if (args[i].EqualsIgnoringCase("-output")) { if (i + 1 == args.Length || args[i + 1].StartsWith("-")) { throw new Exception("-output needs to be followed by a path."); } commandLineBuildPath = args[++i]; } else if (args[i].EqualsIgnoringCase("-buildTarget")) { // Unity will validate the value of the -buildTarget option buildActiveTarget = true; } } } BuildProfile profile = null; if (Application.isBatchMode && profileName != null) { profile = BuildProfile.Find(profileName); if (profile == null) { var err = "Build profile named '" + profileName + "' cloud not be found."; if (onComplete != null) { onComplete.OnComplete(false, new[] { ProfileBuildResult.Error(null, err) }); } else { Debug.LogError(err); } return; } Debug.Log("Building " + profile.name + ", selected from command line."); } if (profile == null) { if (EditorProfile.Instance.ActiveProfile == null) { var err = "No profile specified and not active profile set: Nothing to build"; if (onComplete != null) { onComplete.OnComplete(false, new[] { ProfileBuildResult.Error(null, err) }); } else { Debug.LogError(err); } return; } profile = EditorProfile.Instance.ActiveProfile; Debug.Log("Building active profile."); } // Throw if command line build failed to cause non-zero exit code if (Application.isBatchMode && onComplete == null) { onComplete = ScriptableObject.CreateInstance <CommandLineBuildsCompleteListener>(); } BuildRunner.Job[] jobs; if (buildActiveTarget) { jobs = new[] { new BuildRunner.Job(profile, EditorUserBuildSettings.activeBuildTarget, commandLineBuildPath) }; } else { var targets = profile.BuildTargets.ToArray(); jobs = new BuildRunner.Job[targets.Length]; for (int i = 0; i < targets.Length; i++) { jobs[i] = new BuildRunner.Job() { profile = profile, target = targets[i], outputPath = commandLineBuildPath, }; } } var runner = ScriptableObject.CreateInstance <BuildRunner>(); runner.Run(jobs, onComplete, TrimmerPrefs.RestoreActiveBuildTarget && !Application.isBatchMode); }
/// <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!"); }
public BuildPath(BuildProfile profile, BuildTarget target, string path) { this.profile = profile; this.target = target; this.path = path; }