Search Unity

Get build number from a script

Discussion in 'Editor & General Support' started by protopop, Mar 9, 2019.

  1. protopop

    protopop

    Joined:
    May 19, 2009
    Posts:
    1,561
    Every time i make a new player build i have to go into my splash screen and manually change the UI text to match my build number.

    Can i just get the build number from a script?
     
  2. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
  3. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,620
  4. protopop

    protopop

    Joined:
    May 19, 2009
    Posts:
    1,561
    Thank you. I have that, and it helps a lot. but im looking for the build number - see the attached

    This is what im using, i get the application and unity version via script, but i have to type in the build number by hand into my script every export.
    Code (CSharp):
    1. bn = Application.version+"b"+buildNumber+"e"+Application.unityVersion;
    2.         versionText.text = bn;
    Screen Shot 2019-03-10 at 6.25.21 PM.png
     
  5. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
  6. protopop

    protopop

    Joined:
    May 19, 2009
    Posts:
    1,561
  7. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
    Copy the build number from the playersettings into a file as a pre or post build step.
     
    shieldgenerator7 and protopop like this.
  8. protopop

    protopop

    Joined:
    May 19, 2009
    Posts:
    1,561
    Thank you:)
     
  9. nick_unity640

    nick_unity640

    Joined:
    Oct 17, 2019
    Posts:
    3
    That is basically the same thing as just typing it into the script. OP was just being nice but truthfully this isn't any help at all.
     
  10. fffMalzbier

    fffMalzbier

    Joined:
    Jun 14, 2011
    Posts:
    3,276
    My solution for this was having a custome build script that did write the build version into a public variable in a scene before building the application.
    Another way might be using a build post process to wirte a version / build file into the streaming assets folder.
     
    Joe-Censored and kevinsang like this.
  11. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
    How so? One is a manual step, one is a script that does it for you, which is what he was asking for.

    Something like the example on this docs page: https://docs.unity3d.com/ScriptReference/BuildPipeline.BuildPlayer.html
     
  12. CmdrZin

    CmdrZin

    Joined:
    Sep 15, 2017
    Posts:
    2
    2019.3.06f allows that. Add

    using UnityEditor; to gain access to PlayerSettings.

    Add public Text boxes to your class and add UI elements for display.

    public Text bundleText;
    public Text versionText;

    Then in Start() use
    versionText.text = "Version: " + Application.version.ToString();
    bundleText.text = "Build: " + PlayerSettings.Android.bundleVersionCode.ToString();


    NOTE: Works for Windows. Didn't work for Android.
     
    Last edited: Jan 31, 2020
  13. Dreamback

    Dreamback

    Joined:
    Jul 29, 2016
    Posts:
    220
    p0w1nd and protopop like this.
  14. AppMaMa

    AppMaMa

    Joined:
    Mar 8, 2019
    Posts:
    1
    Doesn't work for iOS either... this is really unfortunate...
     
  15. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
    UnityEditor doesn't get included in builds, so this won't work as-is.
     
  16. xLeo

    xLeo

    Joined:
    Sep 21, 2010
    Posts:
    194
    1) Create a scriptable object or text file that is referenced by a script. (I'd rather just use a scriptable object)
    2) On a pre-build step save the data that you'd like to access in runtime (check
    IPreprocessBuildWithReport)
    3) Use the data in runtime.
     
  17. CPUCPU

    CPUCPU

    Joined:
    Nov 15, 2019
    Posts:
    10
    +1 to just using a scriptable object to store the app version / build number in a pre-build step. That was super helpful and solved this problem quickly for me.
     
    jonathanglitchers and xLeo like this.
  18. Kolichikov

    Kolichikov

    Joined:
    Jul 21, 2014
    Posts:
    16
    @xLeo can you elaborate on h ow to create the scriptable object?
    I've never worked with them, so I did something like this:

    Code (CSharp):
    1. public void OnPreprocessBuild(BuildReport report)
    2.     {
    3.         Debug.Log("MyCustomBuildProcessor.OnPreprocessBuild for target ");
    4.         IncrementBuild();
    5.         BuildState.BuildVersion = PlayerSettings.bundleVersion;
    6.     }
    where BuildState is

    Code (CSharp):
    1. public class BuildState : ScriptableObject
    2. {
    3.     public static string BuildVersion;
    4.     public string Build2;
    5. }
    I think I have to get a reference to the scriptableobject I created in the assets folder, but I'm not sure how to do that from the editor (Resources.Load returns null, and I think I expect it to)
     
  19. xLeo

    xLeo

    Joined:
    Sep 21, 2010
    Posts:
    194
    I have attached 2 stripped-down classes that we use.
    Hope it helps!
     

    Attached Files:

  20. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    360
    I get the error "The type or namespace name 'Build' does not exist in the namespace 'UnityEditor' (are you missing an assembly reference?)"

    It's complaining about this line "using UnityEditor.Build;"

    It only happens when building and I don't know why.

    Target = Android, Unity 2020.2.1f1
     
  21. xLeo

    xLeo

    Joined:
    Sep 21, 2010
    Posts:
    194
    Is the script that implements this code under an "Editor" scripts directory? If not, any build compilation will fail.
     
  22. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    360
    I found the issue, even if the script was in a folder called "Editor" that is in the root asset folder it breaks.
    I had to wrap everything in #if UNITY_EDITOR... not sure why.

    Works now, thanks. I have auto-incrementing build numbers that are all in sync now.
    One change I did make is to remove the Asset finding code as it was misbehaving and just linked the build settings straight in.
     
    xLeo likes this.
  23. ltomov

    ltomov

    Joined:
    Aug 3, 2017
    Posts:
    96
    The way I did it was to place a file runtime-config.json in Resources folder and populate it by the build script.

    Code (CSharp):
    1.     public static bool PreProcessBuild(BuildTarget buildTarget, string pathToBuiltProject)
    2.     {
    3.         // add the build version code in /Resources/runtime-config.json
    4.         TextAsset targetFile = Resources.Load<TextAsset>("runtime-config");
    5.         var json = JSON.Parse(targetFile.text);
    6.         json["appVersionCode"] = Builder.BUILD_NUMBER;
    7.  
    8.         // save the file
    9.         File.WriteAllText(AssetDatabase.GetAssetPath(targetFile), json.ToString());
    10.         EditorUtility.SetDirty(targetFile);
    11. }

    And then you read it when the app starts like this:

    Code (CSharp):
    1. TextAsset targetFile = Resources.Load<TextAsset>("runtime-config");
    2. var json = JSON.Parse(targetFile.text);
    3. Game.APP_VERSION_CODE = json["appVersionCode"];
    4.  
    The JSON parser is this one: https://wiki.unity3d.com/index.php/SimpleJSON
     
    protopop and KBaker46 like this.
  24. Rachan

    Rachan

    Joined:
    Dec 3, 2012
    Posts:
    783
    we cannot use "using UnityEditor;" to building app on xcode project
     
  25. xLeo

    xLeo

    Joined:
    Sep 21, 2010
    Posts:
    194
    Yes, @jihadkhawaja 's solution won't work in runtime.

    This works.
     
  26. xLeo

    xLeo

    Joined:
    Sep 21, 2010
    Posts:
    194
    @jihadkhawaja There is also another issue with your implementation: if you build without opening in the Editor the scene which contains `VersionText`component or don't save the scene; the buildID might get outdated.

    The same is valid for builds on Unity Cloud Build.
     
  27. votagus

    votagus

    Joined:
    Dec 13, 2016
    Posts:
    15
    The easiest way I found around it is to inject it during build time.
    First you need to have a scriptable object, let's say Assets/Config/Config.asset
    Code (CSharp):
    1. public class Config : ScriptableObject
    2. {
    3.     public string buildVersion;
    4. }
    And then during build modify the asset to add the build number.
    Code (CSharp):
    1. public class BuildProcess : IPreprocessBuildWithReport
    2. {
    3.    public int callbackOrder => 0;
    4.  
    5.    public void OnPreprocessBuild(BuildReport report)
    6.    {
    7.        string[] result = AssetDatabase.FindAssets("Config", new string[] { "Assets/Config/" });
    8.  
    9.        string path = AssetDatabase.GUIDToAssetPath(result[0]);
    10.        var config = (Config)AssetDatabase.LoadAssetAtPath(path, typeof(Config));
    11.  
    12.        config.buildVersion = Application.platform == RuntimePlatform.IPhonePlayer
    13.            ? PlayerSettings.iOS.buildNumber
    14.            : PlayerSettings.Android.bundleVersionCode.ToString();
    15.  
    16.        EditorUtility.SetDirty(config);
    17.        AssetDatabase.SaveAssets();
    18.        AssetDatabase.Refresh();
    19.    }
    20. }
    Of course during development this will not work, so this is the way I do it when trying to access it.
    Code (CSharp):
    1. string buildVersion = config.buildVersion;
    2. #if UNITY_EDITOR
    3. buildVersion = Application.platform == RuntimePlatform.IPhonePlayer
    4.     ? PlayerSettings.iOS.buildNumber
    5.     : PlayerSettings.Android.bundleVersionCode.ToString();
    6. #endif
     
  28. cwnuk_lfl

    cwnuk_lfl

    Joined:
    Feb 22, 2019
    Posts:
    2
  29. krupps

    krupps

    Joined:
    Oct 17, 2017
    Posts:
    159
    It's much easier to just call it in native using a plugin https://docs.unity3d.com/Manual/PluginsForIOS.html

    Code (CSharp):
    1. var BuildNumber = NSBundle.MainBundle.InfoDictionary.ValueForKey(new NSString("CFBundleVersion")).ToString();
    2.  
    3. var BuildNumber = context.PackageManager.GetPackageInfo(context.PackageName, PackageInfoFlags.MetaData).VersionCode.ToString();
     
    Nith666 likes this.
  30. tessellation

    tessellation

    Joined:
    Aug 11, 2015
    Posts:
    390
    Our preference is to store the build number in the code instead of having to load a file at runtime. See attached script. Our game is multi-platform and runs on desktop and consoles which don't support runtime APIs to read the build number. Also not having to create dependencies on ScriptableObjects means the build number is available to scripts at any time, not just after scenes are loaded.

    BuildInfo.UpdateBuildNumberInCode() is called at player build-time and from our editor UI whenever the build number is updated so we don't need to get the build number from PlayerSettings like @votagus but you could add that if you need an accurate build number prior to building the Unity Player.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. #if UNITY_EDITOR
    4. using System.Text.RegularExpressions;
    5. using System.IO;
    6. using UnityEditor;
    7. using UnityEditor.Build;
    8. using UnityEditor.Build.Reporting;
    9. #endif
    10.  
    11.  
    12. public static class BuildInfo
    13. {
    14.     const uint BUILD_NUMBER = 1234;
    15.  
    16.     public static uint number => BUILD_NUMBER;
    17.  
    18.     public static string version => Application.version;
    19.  
    20.     public static string versionFull
    21.     {
    22.         get
    23.         {
    24.             if (string.IsNullOrEmpty(_cachedFull))
    25.                 _cachedFull = $"{version}({number})";
    26.             return _cachedFull;
    27.         }
    28.     }
    29.  
    30.     static string _cachedFull = null;
    31.  
    32. #if UNITY_EDITOR
    33.  
    34.     public static void UpdateBuildNumberInCode(uint buildNumber)
    35.     {
    36.         // Write the change back into this source file
    37.         string currentFile = new System.Diagnostics.StackTrace(true).GetFrame(0).GetFileName();
    38.         if (File.Exists(currentFile))
    39.         {
    40.             string text = File.ReadAllText(currentFile);
    41.             text = Regex.Replace(text, "( BUILD_NUMBER =) (\\d+);", $"$1 {buildNumber};", RegexOptions.CultureInvariant);
    42.             File.WriteAllText(currentFile, text);
    43.         }
    44.     }
    45.  
    46.  
    47.     class BuildPreprocessor : IPreprocessBuildWithReport
    48.     {
    49.         public int callbackOrder => 0;
    50.         public void OnPreprocessBuild(BuildReport report)
    51.         {
    52.             // Get the build number from iOS settings
    53.             uint buildNumber = 0;
    54.             if (uint.TryParse(PlayerSettings.iOS.buildNumber, out buildNumber))
    55.                 UpdateBuildNumberInCode(buildNumber);
    56.             Debug.Log($"[BuildInfo] Recorded build {buildNumber} in code before building v{Application.version}.");
    57.         }
    58.     }
    59.  
    60. #endif
    61. }
     

    Attached Files:

    Last edited: Mar 7, 2023
    mauricioweber, Madgvox and protopop like this.
  31. sdpgames

    sdpgames

    Joined:
    Sep 2, 2014
    Posts:
    17
    From Chat GPT 4/modified a bit (spoiler: it works), without the need to do any post processing or storage iOS only

    GetInfoValueForKey.h (in a Plugins/iOS folder):

    Code (CSharp):
    1. #include <stddef.h>
    2. extern "C" const char * GetInfoValueForKey(const char * key);
    GetInfoValueForKey.mm (in a Plugins/iOS folder):
    Code (CSharp):
    1. #import <Foundation/Foundation.h>
    2. #import "GetInfoValueForKey.h"
    3.  
    4. const char * GetInfoValueForKey(const char * key)
    5. {
    6.     if (key == NULL)
    7.     {
    8.         return NULL;
    9.     }
    10.  
    11.     NSString *nsKey = [NSString stringWithUTF8String:key];
    12.     NSString *value = [[NSBundle mainBundle].infoDictionary objectForKey:nsKey];
    13.     if (value == nil)
    14.     {
    15.         return NULL;
    16.     }
    17.  
    18.     const char *utf8String = [value UTF8String];
    19.     return strdup(utf8String); // Make a copy for return
    20. }
    Then in Unity, just call:
    Code (CSharp):
    1. using System;
    2. using System.Runtime.InteropServices;
    3.  
    4. ...
    5.  
    6. [DllImport("__Internal")] static extern string GetInfoValueForKey(string key);
    7. static string GetBuildNumber()
    8. {
    9.    return GetInfoValueForKey("CFBundleVersion");
    10. }
     
    Nith666 and protopop like this.
  32. tessellation

    tessellation

    Joined:
    Aug 11, 2015
    Posts:
    390
    Thanks @sdpgames. This is what we used to do and there's an equivalent API call on Android where you get the PackageInfo and then get the versionCode property. However, these solutions only work for mobile devices and don't work across any platform you might ever want to target in Unity (switch, PC/Mac/Linux, Xbox, etc.). That's why we now use the BuildInfo.cs script above.
     
    protopop and sdpgames like this.
  33. nobluff67

    nobluff67

    Joined:
    Nov 3, 2016
    Posts:
    338
    For everyone that prefers a walkthough....

     
  34. protopop

    protopop

    Joined:
    May 19, 2009
    Posts:
    1,561
    OP here. Thanks for posting this.

    When I bring the script into Unity I get these error:

    Assets/_Core/Scripts/BuildInfoNumber.cs(30,3): error CS0246: The type or namespace name 'ClearOnReloadAttribute' could not be found (are you missing a using directive or an assembly reference?)

    Assets/_Core/Scripts/BuildInfoNumber.cs(30,3): error CS0246: The type or namespace name 'ClearOnReload' could not be found (are you missing a using directive or an assembly reference?)

    (I renamed the script because I had another script the same name.)

    Does anyone have any ideas?

    Screen Shot 2023-05-09 at 7.16.17 PM.png
     
  35. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
  36. tessellation

    tessellation

    Joined:
    Aug 11, 2015
    Posts:
    390
    Sorry about that. Yes, you can remove those attributes if you don't use Unity's "disable domain reload" feature. Those are already removed from the code that's pasted directly into the post. You downloaded the attachment and I forgot to remove those from it.
     
    protopop likes this.
  37. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
    Well, it made me aware of a handy plugin I hadn't known about previously, so I call that a win!
     
    protopop likes this.
  38. tessellation

    tessellation

    Joined:
    Aug 11, 2015
    Posts:
    390
    Yes, it's a fantastic plugin. I recommend you install it via Package Manager + "Add package from git URL..."
     
    Madgvox and protopop like this.
  39. protopop

    protopop

    Joined:
    May 19, 2009
    Posts:
    1,561
    Thank you. I used the code from the inline comment and it compiles perfectly.

    I tried adding the BuildInfo.cs to a gameopbject in my scene and I get this error:

    Can't add script behaviour BuildInfo. The script class can't be abstract!

    At runtime I have a script in my scene that writes the game version info to a text field. How could I get the build number from the build info script? Because it is a static class, does that mean I could call a function from any script anytime? Im just not sure what to call. Any help is appreciated.

    UPDATE: Got it.

    Calling the build number from another script

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class OtherScript : MonoBehaviour
    4. {
    5.     void Start()
    6.     {
    7.         uint buildNumber = BuildInfo.BUILD_NUMBER;
    8.         Debug.Log($"Build number is {buildNumber}");
    9.     }
    10. }
    and changed the constant from private to public so I can access it from other scripts

    Code (CSharp):
    1. public static class BuildInfo
    2. {
    3.     public const uint BUILD_NUMBER = 1234;
    4.  
    5.     // rest of the BuildInfo script...
    6. }
    I tried it on my iPad and it works!

    This has haunted me for almost a decade. And four years after my OP this is a great solution. Small but one of those time savers, and every moment counts in game dev.

    Thank you so much.
     
    Last edited: May 10, 2023
  40. tessellation

    tessellation

    Joined:
    Aug 11, 2015
    Posts:
    390
    Actually @protopop, you access the build number with BuildInfo.number property, which is public (read only). You should not access BUILD_NUMBER directly as a public field. Similarly, there's BuildInfo.version to get the application version, so the idea is that everything related is in one place.
     
    protopop likes this.
  41. protopop

    protopop

    Joined:
    May 19, 2009
    Posts:
    1,561
    Good to know - thank you for the details. I'll revert the code to private and use the number instead.
     
  42. janbromberger

    janbromberger

    Joined:
    Aug 7, 2023
    Posts:
    6
    If you use major.minor.build (e.g. 0.1.22) as the format of your Version in Project Settings > Player, you could simply create the following script in Asssets/Editor:

    Code (CSharp):
    1.  
    2. using System;
    3. using UnityEditor.Build;
    4. using UnityEditor.Build.Reporting;
    5. using UnityEditor;
    6. public class BuildIncrementer : IPreprocessBuildWithReport
    7. {
    8.     public int callbackOrder => 1;
    9.    
    10.     public void OnPreprocessBuild(BuildReport report)
    11.     {
    12.         Version version = Version.Parse(PlayerSettings.bundleVersion);
    13.         Version nextVersion = new Version(version.Major, version.Minor, version.Build + 1);
    14.         PlayerSettings.bundleVersion = nextVersion.ToString();
    15.     }
    16. }
    17.  
    And then set it to a Text from Application.version.
     
  43. tessellation

    tessellation

    Joined:
    Aug 11, 2015
    Posts:
    390
    Someone already proposed a similar solution above. Unfortunately bundleVersion is only for iOS and Android, whereas the system I proposed here works for all platforms that Unity supports.