Search Unity

Postprocessbuildplayer - build options frameworks

Discussion in 'iOS and tvOS' started by BazLevelUp, Jun 28, 2012.

  1. BazLevelUp

    BazLevelUp

    Joined:
    Jun 27, 2012
    Posts:
    36
    Hi everyone !

    I'm in charge of the iOS version of our game, but we have other programmers working on the others versions of the game in the same repo, so my app is updating on a daily basis. So I need a post process that will configure my generated Xcode automatically.

    We have already plugins that do automatically (prime31's ones), but we're also using some plugins that don't :(

    I know I must use Postprocessbuildplayer :)
    But I don't know how :(
    That is, I know all the theory around how to use it, but I just don't know how to code the behaviour I want.

    Inside the Postprocessbuildplayer provided by Prime31, we read :
    Code (csharp):
    1.  
    2. [...]
    3. # Searches for other PostprocessBuildPlayer scripts and executes them. Make sure the other script
    4. # have a name suffix with an underscore "_" like "PostprocessBuildPlayer_AnotherBuild" or whatever.
    5. [...]
    6. functions that do what described above
    7.  
    So I know that all I have to do is create another PostprocessBuildPlayer, name it "PostprocessBuildPlayer_OthersPlugins" and put my code in it :)


    But what code ? I have searched for help but I don't have found any :(

    What I need is : change some values in my Project/Target(Unity-iPhone)/Build Settings include some frameworks.


    What I have find :
    - Create my own plugin
    - copying stuff and info.plist
    - Auto-signing for App Store
    - post-process INSIDE Unity

    Can someone please help me ? ;)
     
  2. BazLevelUp

    BazLevelUp

    Joined:
    Jun 27, 2012
    Posts:
    36
    I have found something rather interesting ! :)

    Some Python code that do what I want : PostProcessBuildPlayer - hohohmm

    I have made the changes I needed for my situation, but just like the poor hohohmm, I can't get my script to be executed, for now just Prime31 script is passing. The best I can do is having Prime31 finding my script as said on console
    Code (csharp):
    1.  
    2. 28/6/12 3:55:39.519 PM Prime31: Processing all folders
    3. 28/6/12 3:55:39.522 PM Prime31: file has postprocess in it: OthersPlugins
    4. 28/6/12 3:55:39.523 PM Prime31: --- About to kick off the Runner for Localytics ---
    5. [...]
    6.  
    I'll try using just my script without the one from Prime31.
    -> EDIT : If I remove the "PostProcessBuildPlayer" from Prime31 and rename mine to "PostProcessBuildPlayer" (so that it became the one and only to be executed), the build of the project is a success but my script is not executed.

    I'm gonna write to Prime31 to ask him how he manages to do it ;)
     
    Last edited: Jun 28, 2012
  3. opsive

    opsive

    Joined:
    Mar 15, 2010
    Posts:
    5,127
    If you figure out how I'd love to hear. I have been trying with AppleScript but I haven't been able to add the framework to the link binary with libraries build phase, and from what I've read it looks like it is a problem with the links between Xcode and AppleScript
     
  4. BazLevelUp

    BazLevelUp

    Joined:
    Jun 27, 2012
    Posts:
    36
    Hi !
    I'm sorry, I should have replied here days ago, in case someone like you would come here.

    So I have ask the question here to prime31. His answer was :
    @baz, there is no magic involved. Simply naming the file PostProcessBuildPlayer_Something will most definitely get the script called.

    So I was doing everything nice, but it still didn't work ! :(
    I forfeit, and tried to do the postProcessing INSIDE Unity, as described in the last link above.
    And it worked !


    Basically, what I'm doing is opening the Xcode project, reading it and caching it, and then rewrite it line by line, adding or changing lines when special cases happens.

    I have append the code here. I know it will look kinda hardcore at first glance, but do copy it in your fav editor, indent it as you want, and iterate through it like 1 or 2 time, and you will understand. In fac it's pretty straighforward. If I have helped anybody then I'm glad :)

    Code (csharp):
    1.  
    2. public static class PostBuildTrigger
    3. {
    4.     // Frameworks Ids  -  These ids have been generated by creating a project using Xcode then
    5.     // extracting the values from the generated project.pbxproj.  The format of this
    6.     // file is not documented by Apple so the correct algorithm for generating these
    7.     // ids is unknown
    8.     const string CORETELEPHONY_ID = "919BD0C3159C677000C931BE" ;
    9.     const string CORETELEPHONY_FILEREFID = "919BD0C2159C677000C931BE" ;
    10.        
    11.     // List of all the frameworks to be added to the project
    12.     public struct framework
    13.     {
    14.         public string sName ;
    15.         public string sId ;
    16.         public string sFileId ;
    17.        
    18.         public framework(string name, string myId, string fileid)
    19.         {
    20.             sName = name ;
    21.             sId = myId ;
    22.             sFileId = fileid ;
    23.         }
    24.     }
    25.  
    26.    
    27.     /// Processbuild Function
    28.     [PostProcessBuild] // <- this is where the magic happens
    29.     public static void OnPostProcessBuild(BuildTarget target, string path)
    30.     {
    31.             // 1: Check this is an iOS build before running
    32. #if UNITY_IPHONE
    33.         {
    34.             // 2: We init our tab and process our project
    35.             framework[] myFrameworks = { new framework("CoreTelephony.framework", CORETELEPHONY_ID, CORETELEPHONY_FILEREFID) } ;
    36.        
    37.             string xcodeprojPath = Application.dataPath ;
    38.             xcodeprojPath = xcodeprojPath.Substring(0, xcodeprojPath.Length - 16) ;
    39.             string device = "iOSandIpad" ;
    40.             if (PlayerSettings.iOS.targetDevice == iOSTargetDevice.iPadOnly)
    41.                 device = "iPad" ;
    42.             else if (PlayerSettings.iOS.targetDevice == iOSTargetDevice.iPhoneOnly)
    43.                 device = "iPhone" ;
    44.             xcodeprojPath = xcodeprojPath + "buildios/"+device+"/Unity-iPhone.xcodeproj" ;
    45. //          Debug.Log("We found xcodeprojPath to be : "+xcodeprojPath) ;
    46.                
    47.             Debug.Log("OnPostProcessBuild - START on: "+device) ;
    48.             updateXcodeProject(xcodeprojPath, myFrameworks) ;                      
    49.         }
    50. #else
    51.         // 3: We do nothing if not iPhone
    52.         Debug.Log("OnPostProcessBuild - Warning: This is not an iOS build") ;
    53. #endif     
    54.         Debug.Log("OnPostProcessBuild - STOP") ;
    55.     }
    56.  
    57.    
    58.    
    59.    
    60.     // MAIN FUNCTION
    61.     // xcodeproj_filename - filename of the Xcode project to change
    62.     // frameworks - list of Apple standard frameworks to add to the project
    63.     public static void updateXcodeProject(string xcodeprojPath, framework[] listeFrameworks)
    64.     {
    65.         // STEP 1 : We open up the file generated by Unity and read into memory as
    66.         // a list of lines for processing
    67.         string project = xcodeprojPath + "/project.pbxproj" ;
    68.         string[] lines = System.IO.File.ReadAllLines(project);
    69.        
    70.         // STEP 2 : We check if file has already been processed and only proceed if it hasn't,
    71.         // we'll do this by looping through the build files and see if CoreTelephony.framework
    72.         // is there
    73.         int i = 0 ;
    74.         bool bFound = false ;
    75.         bool bEnd = false ;
    76.         while ( !bFound  !bEnd)
    77.         {
    78.             if (lines[i].Length > 5  (String.Compare(lines[i].Substring(3, 3), "End") == 0) )
    79.                 bEnd = true ;
    80.            
    81.             bFound = lines[i].Contains("CoreTelephony.framework") ;
    82.             ++i ;
    83.         }
    84.         if (bFound)
    85.             Debug.Log("OnPostProcessBuild - ERROR: Frameworks have already been added to XCode project") ;
    86.         else
    87.         {
    88.             // STEP 3 : We'll open/replace project.pbxproj for writing and iterate over the old
    89.             // file in memory, copying the original file and inserting every extra we need
    90.             FileStream filestr = new FileStream(project, FileMode.Create); //Create new file and open it for read and write, if the file exists overwrite it.
    91.             filestr.Close() ;
    92.             StreamWriter fCurrentXcodeProjFile = new StreamWriter(project) ; // will be used for writing
    93.            
    94.             // As we iterate through the list we'll record which section of the
    95.             // project.pbxproj we are currently in
    96.             string section = "" ;
    97.  
    98.             // We use this boolean to decide whether we have already added the list of
    99.             // build files to the link line.  This is needed because there could be multiple
    100.             // build targets and they are not named in the project.pbxproj
    101.             bool bFrameworks_build_added = false ;
    102.             int iNbBuildConfigSet = 0 ; // can't be > 2
    103.        
    104.             i = 0 ;
    105.             foreach (string line in lines)
    106.             {
    107.                 if (line.StartsWith("\t\t\t\tGCC_ENABLE_CPP_EXCEPTIONS") ||
    108.                     line.StartsWith("\t\t\t\tGCC_ENABLE_CPP_RTTI") ||
    109.                     line.StartsWith("\t\t\t\tGCC_ENABLE_OBJC_EXCEPTIONS") )
    110.                 {
    111.                     // apparently, we don't copy those lines in our new project
    112.                 }
    113.                 else
    114.                 {                          
    115. //////////////////////////////
    116. //  STEP 1 : Build Options  //
    117. //////////////////////////////
    118. //
    119. // TapJoy needs "Enable Object-C Exceptions" to be set to "YES"
    120. //
    121. //////////////////////////////
    122.  
    123. // This one is special, we have to replace a line and not write after
    124.         //          if ( section == "XCBuildConfiguration"  line.Trim().StartsWith("\t\t\t\tGCC_ENABLE_OBJC_EXCEPTIONS") )
    125.         //              fCurrentXcodeProjFile.Write("\t\t\t\tGCC_ENABLE_OBJC_EXCEPTIONS = YES;\n") ;
    126.  
    127. // in any other situation, we'll first copy the line in our new project, then we might do something special regarding that line
    128.         //              else
    129.                     fCurrentXcodeProjFile.WriteLine(line) ;
    130.                    
    131.                    
    132.  
    133. //////////////////////////////////
    134. //  STEP 2 : Include Framewoks  //
    135. //////////////////////////////////
    136. //
    137. // TapJoy needs CoreTelephony (in weak-link = "optional")
    138. //
    139. //////////////////////////////////
    140.  
    141. // Each section starts with a comment such as : /* Begin PBXBuildFile section */'
    142.                     if ( lines[i].Length > 7  String.Compare(lines[i].Substring(3, 5), "Begin") == 0  )
    143.                     {
    144.                         section = line.Split(' ')[2] ;
    145.                         //Debug.Log("NEW_SECTION: "+section) ;
    146.                         if (section == "PBXBuildFile")
    147.                         {
    148.                             foreach (framework fr in listeFrameworks)
    149.                                 add_build_file(fCurrentXcodeProjFile, fr.sId, fr.sName, fr.sFileId) ;
    150.                         }
    151.                        
    152.                         if (section == "PBXFileReference")
    153.                         {
    154.                             foreach (framework fr in listeFrameworks)
    155.                                 add_framework_file_reference(fCurrentXcodeProjFile, fr.sFileId, fr.sName) ;
    156.                         }
    157.                        
    158.                         if (line.Length > 5  String.Compare(line.Substring(3, 3), "End") == 0)
    159.                             section = "" ;
    160.                     }
    161. // The PBXResourcesBuildPhase section is what appears in XCode as 'Link
    162. // Binary With Libraries'.  As with the frameworks we make the assumption the
    163. // first target is always 'Unity-iPhone' as the name of the target itself is
    164. // not listed in project.pbxproj               
    165.                     if (section == "PBXFrameworksBuildPhase"
    166.                         line.Trim().Length > 4
    167.                         String.Compare(line.Trim().Substring(0, 5) , "files") == 0
    168.                         !bFrameworks_build_added)
    169.                     {
    170.                         foreach (framework fr in listeFrameworks)
    171.                             add_frameworks_build_phase(fCurrentXcodeProjFile, fr.sId, fr.sName) ;
    172.                         bFrameworks_build_added = true ;
    173.                     }
    174.                    
    175. // The PBXGroup is the section that appears in XCode as 'Copy Bundle Resources'.           
    176.                     if (section == "PBXGroup"
    177.                         line.Trim().Length > 7  
    178.                         String.Compare(line.Trim().Substring(0, 8) , "children") == 0
    179.                         lines[i-2].Trim().Split(' ').Length > 0
    180.                         String.Compare(lines[i-2].Trim().Split(' ')[2] , "CustomTemplate" ) == 0 )
    181.                     {
    182.                         foreach (framework fr in listeFrameworks)
    183.                             add_group(fCurrentXcodeProjFile, fr.sFileId, fr.sName) ;
    184.                     }
    185.  
    186. //////////////////////////////
    187. //  STEP 3 : Build Options  //
    188. //////////////////////////////
    189. //
    190. // AdColony needs "Other Linker Flags" to have "-all_load -ObjC" added to its value
    191. //
    192. //////////////////////////////
    193.                     if (section == "XCBuildConfiguration"
    194.                         line.StartsWith("\t\t\t\tOTHER_LDFLAGS")
    195.                         iNbBuildConfigSet < 2)
    196.                     {
    197.                         //fCurrentXcodeProjFile.Write("\t\t\t\t\t\"-all_load\",\n") ;
    198.                         fCurrentXcodeProjFile.Write("\t\t\t\t\t\"-ObjC\",\n") ;
    199.                         Debug.Log("OnPostProcessBuild - Adding \"-ObjC\" flag to build options") ; // \"-all_load\" and
    200.                         ++iNbBuildConfigSet ;
    201.                     }
    202.                 }
    203.                 ++i ;
    204.             }
    205.             fCurrentXcodeProjFile.Close() ;
    206.         }
    207.     }
    208.    
    209.        
    210.     /////////////////
    211.     ///////////
    212.     // ROUTINES
    213.     ///////////
    214.     /////////////////
    215.  
    216.    
    217.     // Adds a line into the PBXBuildFile section
    218.     private static void add_build_file(StreamWriter file, string id, string name, string fileref)
    219.     {
    220.         Debug.Log("OnPostProcessBuild - Adding build file " + name) ;
    221.         string subsection = "Frameworks" ;
    222.        
    223.         if (name == "CoreTelephony.framework")  // CoreTelephony.framework should be weak-linked
    224.             file.Write("\t\t"+id+" /* "+name+" in "+subsection+" */ = {isa = PBXBuildFile; fileRef = "+fileref+" /* "+name+" */; settings = {ATTRIBUTES = (Weak, ); }; };") ;
    225.         else // Others framework are normal
    226.             file.Write("\t\t"+id+" /* "+name+" in "+subsection+" */ = {isa = PBXBuildFile; fileRef = "+fileref+" /* "+name+" */; };\n") ;
    227.     }
    228.    
    229.     // Adds a line into the PBXBuildFile section
    230.     private static void add_framework_file_reference(StreamWriter file, string id, string name)
    231.     {
    232.         Debug.Log("OnPostProcessBuild - Adding framework file reference " + name) ;
    233.        
    234.         string path = "System/Library/Frameworks" ; // all the frameworks come from here
    235.         if (name == "libsqlite3.0.dylib")           // except for lidsqlite
    236.             path = "usr/lib" ;
    237.        
    238.         file.Write("\t\t"+id+" /* "+name+" */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = "+name+"; path = "+path+"/"+name+"; sourceTree = SDKROOT; };\n") ;
    239.     }
    240.    
    241.     // Adds a line into the PBXFrameworksBuildPhase section
    242.     private static void add_frameworks_build_phase(StreamWriter file, string id, string name)
    243.     {
    244.         Debug.Log("OnPostProcessBuild - Adding build phase " + name) ;
    245.        
    246.         file.Write("\t\t\t\t"+id+" /* "+name+" in Frameworks */,\n") ;
    247.     }
    248.    
    249.     // Adds a line into the PBXGroup section
    250.     private static void add_group(StreamWriter file, string id, string name)
    251.     {
    252.         Debug.Log("OnPostProcessBuild - Add group " + name) ;
    253.        
    254.         file.Write("\t\t\t\t"+id+" /* "+name+" */,\n") ;
    255.     }
    256. }
    257.  
    Courage !
     
  5. nan

    nan

    Joined:
    May 19, 2009
    Posts:
    10
    Really good stuff BazLevelUp,
    how do you get framework ids ?
    do you have a sample ?

    thank you
     
  6. Borluse

    Borluse

    Joined:
    Aug 16, 2012
    Posts:
    3
    HI!
    You could do something like this:
    1. Follow prime31's PostprocessBuildPlayer's rule, duplicate one of its script and rename it to your own like PostprocessBuildPlayer_xxx
    2. I have no idea how prime31 added files to xcode through applescript. I tried but failed. So i found another way, that is use a python script.
    3. Comment the last line like 'osascript xxxxxxx'
    4. Added a line to call your own python scrpit like:
    `Python \'$currDir/Assets/Editor/xxx/update.py\' `
    5. In your update.py, folllow this line : https://bitbucket.org/icalderon/mod-pbxproj/overview
    6. END.

    This script allows you to generate unique id for anything you wanted to add to XCode, like framework, file, bundles, etc.

    Some link useful:
    http://danwright.info/blog/2010/10/xcode-pbxproject-files/
    http://the.darktable.com/post/17700178147/modifying-an-xcode-project-with-applescript-is

    About applescript, it does do some works, like add file, create group. But if you wish to add some framework, it sucks.
    Best wishes :)
     
  7. BazLevelUp

    BazLevelUp

    Joined:
    Jun 27, 2012
    Posts:
    36
    As it reads in the comments, it's taken from Xcode.
    What the script does in overall in rewriting the projet from a point without modifications to a point with.
    So basically you generate the Xcode solution using Unity, get a back-up copy of the project.pbxproj, open the original one with Xcode, make the modifications you want using the interface, and save.
    THEN, you can open the back-up file and the one with the modifications you want, and compare what is different
     
  8. RMGK

    RMGK

    Joined:
    Sep 30, 2011
    Posts:
    75
    Thanks a ton mate. Was bashing my head with bits of script, having it all in editor in magnificent!
     
  9. arkon

    arkon

    Joined:
    Jun 27, 2011
    Posts:
    1,122
    I had problems with the whole post process build thing, I followed what Prime31 had done and after days of banging my head I discovered a major problem. IF your post process code has a syntax error or any kind of error it just silently fails, none of the code seems to be executed. Made it impossible even to add debugging code to it. In the end I had to do it one line at a time, add a line, run it, see if it worked and repeat for all lines in the script.
     
  10. vivalavladislav

    vivalavladislav

    Joined:
    Sep 12, 2013
    Posts:
    16
    thank's, that helped me a lot)
     
  11. rsb_jeremy

    rsb_jeremy

    Joined:
    Jan 8, 2014
    Posts:
    5
    note that the 'path' variable gives you a path to the folder containing the xcodeproj, so you should be able to avoid building that yourself
     
  12. standardcombo

    standardcombo

    Joined:
    Jun 28, 2012
    Posts:
    19
    There's a syntax error on line 76 above. I'm not sure if it's supposed to be an && or ||, but I'm taking a stab at it and say it's &&