Search Unity

  1. Get all the Unite Berlin 2018 news on the blog.
    Dismiss Notice
  2. Unity 2018.2 has arrived! Read about it here.
    Dismiss Notice
  3. Improve your Unity skills with a certified instructor in a private, interactive classroom. Learn more.
    Dismiss Notice
  4. ARCore is out of developer preview! Read about it here.
    Dismiss Notice
  5. Magic Leap’s Lumin SDK Technical Preview for Unity lets you get started creating content for Magic Leap One™. Find more information on our blog!
    Dismiss Notice
  6. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice

Build Error: TextMesh Pro compilation failure

Discussion in 'Unity Cloud Build' started by Hosnkobf, Jul 10, 2018 at 6:06 PM.

  1. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    279
    When I make a build for Android on my machine, everything works fine.
    But when I let Unity Cloud Build do the build it fails.

    There are two error messages. The first is early in the build:
    the second are actually three errors related to another and are outputted several times (for each location in the TMP code where it is used):
    I do not understand why the compilation fails and why there are ambiguous calls. It looks like there is another TextMesh Pro package loaded by the Unity Cloud which contains the same script I also have in the DLL.

    How can I tell Unity Cloud build that it should just use the things inside my project and not load any other things additionally? (or do I understand something wrong?)

    Thanks for your help.
     
  2. technicat

    technicat

    Joined:
    Nov 22, 2006
    Posts:
    1,220
    Do you have TextMesh Pro imported from the Asset Store? This is just a guess, but with Unity 2018 TextMesh Pro has been available via the built-in Package Manager, and Unity 2018.2 was just released, so if you have your cloud config set to build with the latest version of Unity, perhaps it is by default including the Package Manager version of TextMesh Pro.
     
    musickgm and Hosnkobf like this.
  3. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    279
    Hi technicat,
    thank you very much for your suggestion. It is true: I recently updated to Unity 2018 but kept the TextMesh Pro version from the asset store.

    I deleted the TextMesh Pro folder and imported the asset again through the package manager. Now I ran into a big problem: My Font Assets where broken because they referenced a script which is not in the project structure anymore. So I had to re-create the fonts. Unfortunately, all my prefabs lost the references to the fonts (probably also scene objects would lose references, but I have almost nothing in the single scene for my app, because I create everything on the fly with prefabs).

    To overcome this problem I rolled back my changes (and then had to reimport the prefabs) and created a little helper tool which saves the assigned font for each relevant component in all prefabs. After switching to the package manager version I loaded the saved data and mapped the new fonts to the old fonts and applied them to all prefabs.

    I share my tool with you guys, in case you run into the same problem (or some similar problem... it actually works with any UnityEngine.Object variable in any component).
    And here is the code. Please note that you will have to do some work yourself. I use a Table class which I created but cannot share because of legal reasons.
    So you need to write the data to a file yourself and read it again.
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using UnityEngine;
    6. using UnityEditor;
    7. using System.IO;
    8. using TheraBytes.Utilities.Io;
    9. using UnityEditorInternal;
    10. using System.Reflection;
    11.  
    12. namespace TheraBytes.Utilities.Editor
    13. {
    14.  
    15.     [Serializable]
    16.     public class PrefabValueStore : EditorWindow
    17.     {
    18.         [MenuItem("Window/Thera Bytes/Prefab Value Store", false, 30)]
    19.         public static void ShowWindow()
    20.         {
    21.             var win = EditorWindow.GetWindow<PrefabValueStore>("Prefab Value Store") as PrefabValueStore;
    22.  
    23.         }
    24.  
    25.         #region Nested Class
    26.         public class PrefabInfo
    27.         {
    28.             public string Name { get; private set; }
    29.             public string Path { get; private set; }
    30.             public GameObject Object { get; private set; }
    31.  
    32.             public PrefabInfo(string name, string path, GameObject obj)
    33.             {
    34.                 this.Name = name;
    35.                 this.Path = path;
    36.                 this.Object = obj;
    37.             }
    38.         }
    39.         #endregion
    40.  
    41.         const string COL_PATH = "Path";
    42.         const string COL_HIERARCHY = "Hierarchy";
    43.         const string COL_NAME = "Name";
    44.         const string COL_VALUE = "Value";
    45.  
    46.         [SerializeField] string _filePath;
    47.         [SerializeField] List<UnityEngine.Object> _values = new List<UnityEngine.Object>();
    48.         [SerializeField] string _componentTypeName;
    49.         [SerializeField] string _variableName;
    50.  
    51.         ReorderableList _listDrawer;
    52.  
    53.         private void OnGUI()
    54.         {
    55.             InitListDrawer();
    56.  
    57.             _filePath = EditorGUILayout.TextField("File Path", _filePath);
    58.             _componentTypeName = EditorGUILayout.TextField("Component Type Name", _componentTypeName);
    59.             _variableName = EditorGUILayout.TextField("Variable Name", _variableName);
    60.  
    61.             _listDrawer.DoLayoutList();
    62.             GUILayout.FlexibleSpace();
    63.  
    64.             EditorGUILayout.BeginHorizontal();
    65.             {
    66.                 if(GUILayout.Button("Scan & Save"))
    67.                 {
    68.                     SaveData();
    69.                 }
    70.  
    71.                 if(GUILayout.Button("Load & Apply"))
    72.                 {
    73.                     LoadData();
    74.                 }
    75.             }
    76.             EditorGUILayout.EndHorizontal();
    77.         }
    78.  
    79.  
    80.         void InitListDrawer()
    81.         {
    82.             if (_listDrawer != null)
    83.                 return;
    84.  
    85.             _listDrawer = new ReorderableList(_values, typeof(UnityEngine.Object));
    86.             _listDrawer.drawElementCallback +=
    87.                 (r, idx, active, focus) =>
    88.                 {
    89.                     Rect rect = new Rect(r.x, r.y + 2, r.width, r.height - 4);
    90.                     _values[idx] = EditorGUI.ObjectField(rect, _values[idx], typeof(UnityEngine.Object), false);
    91.                 };
    92.         }
    93.  
    94.  
    95.         void SaveData()
    96.         {
    97.             Table table = new Table(_filePath, 4, 1, CsvSetting.UniqueColumnHeaders);
    98.             table[0, 0] = COL_PATH;
    99.             table[1, 0] = COL_HIERARCHY;
    100.             table[2, 0] = COL_NAME;
    101.             table[3, 0] = COL_VALUE;
    102.          
    103.             foreach(PrefabInfo prefabInfo in IteratePrefabs())
    104.             {
    105.                 PrefabInfo parent = new PrefabInfo(prefabInfo.Name, "", prefabInfo.Object);
    106.                 foreach(PrefabInfo childInfo in IteratePrefabHierarchy(parent))
    107.                 {
    108.                     var comp = childInfo.Object.GetComponent(_componentTypeName);
    109.                     if (comp == null)
    110.                         continue;
    111.                  
    112.                     var field = comp.GetType()
    113.                         .GetField(_variableName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
    114.  
    115.                     if (field == null)
    116.                         continue;
    117.                  
    118.                     object val = field.GetValue(comp);
    119.  
    120.                     int idx = _values.FindIndex((o) => o.Equals(val));
    121.  
    122.                     if (idx < 0)
    123.                         continue;
    124.  
    125.                     table.AppendRow(prefabInfo.Path, childInfo.Path, childInfo.Name, idx.ToString());
    126.                 }
    127.             }
    128.  
    129.             table.Save();
    130.  
    131.             this.LogInfo("Data has been saved!");
    132.         }
    133.  
    134.         void LoadData()
    135.         {
    136.             Table table = new Table(_filePath, CsvSetting.UniqueColumnHeaders);
    137.             foreach (PrefabInfo prefabInfo in IteratePrefabs())
    138.             {
    139.                 List<int> rows = table.GetRowIndices(prefabInfo.Path).ToList();
    140.  
    141.                 if (rows.Count == 0)
    142.                     continue;
    143.  
    144.                 PrefabInfo parent = new PrefabInfo(prefabInfo.Name, "", prefabInfo.Object);
    145.                 foreach (PrefabInfo childInfo in IteratePrefabHierarchy(parent))
    146.                 {
    147.                     foreach(int row in rows)
    148.                     {
    149.                         if (table[COL_HIERARCHY, row] != childInfo.Path)
    150.                             continue;
    151.  
    152.                         if (table[COL_NAME, row] != childInfo.Name)
    153.                             continue;
    154.  
    155.                         var comp = childInfo.Object.GetComponent(_componentTypeName);
    156.                         if(comp == null)
    157.                         {
    158.                             this.LogErrorFormat("Could not find component of type '{0}' in {1}/{2}",
    159.                                 _componentTypeName, childInfo.Path, childInfo.Name);
    160.                             continue;
    161.                         }
    162.  
    163.                         var field = comp.GetType()
    164.                             .GetField(_variableName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
    165.  
    166.                         if (field == null)
    167.                         {
    168.                             this.LogErrorFormat("Could not find field '{0}' in {1}", _variableName, _componentTypeName);
    169.                             continue;
    170.                         }
    171.  
    172.                         int idx = table.Get<int>(COL_VALUE, row);
    173.                         field.SetValue(comp, _values[idx]);
    174.                     }
    175.                 }
    176.             }
    177.  
    178.             this.LogInfo("Data has been loaded!");
    179.         }
    180.  
    181.         IEnumerable<PrefabInfo> IteratePrefabs()
    182.         {
    183.             string[] prefabPaths = Directory.GetFiles(Application.dataPath, "*.prefab", SearchOption.AllDirectories);
    184.  
    185.             foreach (string p in prefabPaths)
    186.             {
    187.                 int start = Application.dataPath.Length - "Assets".Length;
    188.                 string internalPath = p.Substring(start, p.Length - start).Replace('\\', '/');
    189.  
    190.                 string name = Path.GetFileNameWithoutExtension(internalPath);
    191.                 string path = Path.GetDirectoryName(internalPath);
    192.                 GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(internalPath);
    193.  
    194.                 yield return new PrefabInfo(name, path, prefab);
    195.             }
    196.         }
    197.  
    198.         IEnumerable<PrefabInfo> IteratePrefabHierarchy(PrefabInfo parent)
    199.         {
    200.             yield return parent;
    201.  
    202.             string path = parent.Path + "/" + parent.Name;
    203.             foreach(Transform child in parent.Object.transform)
    204.             {
    205.                 var childInfo = new PrefabInfo(child.name, path, child.gameObject);
    206.  
    207.                 foreach(PrefabInfo info in IteratePrefabHierarchy(childInfo))
    208.                 {
    209.                     yield return info;
    210.                 }
    211.             }
    212.         }
    213.     }
    214. }
    215.  
    This is how it works:
    Open the window (Windows -> Thera Bytes -> Prefab Value Store [if you didn't change]).

    Save:
    In the field "File Path" enter an existing folder followed by the file you want to create (e.g. "D:/tmp.txt").
    In the field "Component Type Name" enter the Component where the problematic field is (e.g. "TextMeshProUGUI").
    In the field "Variable Name" enter the variable which you need to update (e.g. "m_fontAsset").

    Then add Items to the list and assign the objects which will be lost / replaced later (e.g. all your TextMesh Pro Font Assets). It is important to remember the order!

    Then click "Scan & Save".

    Load:
    When your component fields have missing value (so, after you deleted TextMesh Pro and then imported it through the package manager) recreate your font assets (or whatever) and open the Prefab Value Store window.
    Make sure that all the fields have the same values as they had during save.
    Add the newly created font assets (or whatever) to the list. Ensure that you have the same number of items in the list as during save and make also sure that the order of the items maps to the order of items during save.

    Then click "Load & Apply".

    Check your prefabs. They should have the fonts (or whatever) assigned again :)

    PS: Nice side-feature: The tool can also be used to easily replace all references to one object with references to another object in all prefabs. For example if you need to replace a font with another one or a certain sprite with another one everywhere.


    PS: right now the cloud is building. It takes longer than the previous builds with the error, so I guess this is a good sign.

    Edit: The build error is gone after using TextMesh Pro from the Package Manager! Thanks for the help.
     
    Last edited: Jul 11, 2018 at 2:01 PM
    technicat likes this.
  4. technicat

    technicat

    Joined:
    Nov 22, 2006
    Posts:
    1,220
    Hosnkobf likes this.
  5. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    279
    Oh yeah, that could have saved me some time...
    Anyways... Maybe I can re-use my tool in the future. I am almost sure that I will need it again some day :D
     
    technicat likes this.
  6. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    279
    hmm, interesting:
    I am actually working with Unity 2018.1.6f1 right now. When the error described here occured I accidentally had Cloud Build set to 2018.2.
    Because I thought, it is probably a good idea to have the same version as on my machine, I switched to 2018.1 in the cloud build settings.

    Funny thing: With that version Unity Cloud has build errors because it cannot find the TextMesh Pro code files.

    So, there are two bugs with Unity Cloud:
    - If Unity Cloud Build is set to 2018.1: It does not load the TextMesh Pro package, even if I defined it in the package manager on my machine.
    - If Unity Cloud build is set to 2018.2: It always loads TextMesh Pro as package, even if it has been included via asset store package.

    looks like the packages are not well integrated yet in Unity Cloud build. I hope they will sort it out soon... It is hard to believe that they messed around with such an important feature.