20 Replies Latest reply: Nov 5, 2014 12:14 PM by ddyer00 RSS

    Problem with Flash Builder 4.6 when debugging native extension for OSX

    Cloudy Forest Community Member

      I think I stumbled upon a bug in Flash Builder 4.6. I'm running FB on OSX 10.7 and I'm building native extension for OSX (AIR 3.1).

       

      After successfull creation of my extension's native code .framework file in Xcode, writing Action Script wrapper around it and packaging everything together in ANE file (using adt) I discovered that I couldn't start a debug session from Flash Builder (4.6 and AIR 3.1). Since I was able to test my extension from terminal (using adl), I am pretty sure that I connected all the dots correctly.

       

      I discovered that Flash Builder only before starting debug session unpacks the extension's .ane file into some temporary folder, beneath the workspace. Unfortunately, it messes the attributes of unpacked files so aliases are missing (they look like regular files) and main binary inside OSX's .framework package is missing executable attribute. Since those aliases and attributes are essential for things to work on OSX, FB simply fails to find extension and reports error like "ArgumentError: Error #3500: The extension context does not have a method with the name". Somebody else stumbled upon this in following post http://forums.adobe.com/message/3974973 (it seems it's specific to Linux and OSX, but not Windows - no aliasing there, huh?).

       

      I would appreciate if somebody from Adobe could check that and verify if that's up to Flash Builder or not.

       

      Thanks

        • 1. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
          sbaldizz Community Member

          I think I found the problem (but not the solution)

           

           

          When Flash builder launch adl, first extract the ane extension in a hidden folder:

          ~/Documents/Adobe Flash Builder 4.6/.metadata/.plugins/com.adobe.flexbuilder.project.ui/[folder for the project/macosx

           

          Inside this folder there is a directory with the name of the ane (like myext.ane) and inside many files (catalog.xml, library.swf, META-INF, ...)

          Inside META-INF/ANE/MacOS-x86/ there are the files of the OSX Framework with native code.

           

          The problem is that the .framework folder is corrupter, the symlinks inside it are normal file (the contents is the path of linked file) but they are not real symlink!

           

          So the problem is that the symlink inside the frameworks are not correctly generated.

           

          If you start from flashbuilder the app without call code inside an ane, and (with the app opened) replace the broken file with the correct symlink the app works correctly also for the ane code!

          • 2. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
            Cloudy Forest Community Member

            Thanks for reproducing Flash Builder's bug. Workaround that you are mentioning is too awkward for me. I ended up  manually extracting content of the .ane file, starting fake debugging session from Flash Builder (using web project) and finally running my app using adl from command line.

            • 3. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
              sbaldizz Community Member

              I don't decompress manually the ane file for extract the correct symlinks, but replace the wrong file with the same from the framework folder make by xcode

              Launching the debugger from flex and replace (before call any ane code) the symlinks allow to debug without any more problems.

               

              I'm developing a little function that fix the framework symlinks to call when the app start (maybe using the conditional compilation so this code is use only in debugger mode and not in final release). The problem is that flex don't allow to create a symlink and for this function I need to call an external process (like a bash script)...

              Late today I post the code...

              • 4. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
                sbaldizz Community Member

                this is the code that I develop to temporally fix the bug:

                 

                 

                private static var 
                    callback_error:Function = null, 
                    callback_ioerror:Function = null, 
                    callback_output:Function = null;
                
                /** @param extensionID:String id of the ane extension to fix
                  * @param onExit:Function callback function with first arguments the value of extensionID and for the second the boolean state of the fix operation
                  **/
                private function fixANE(extensionID:String, onExit:Function=null):Boolean {
                    if (!NativeProcess.isSupported) {
                        if (onExit!=null)
                            onExit(extensionID, false);
                        return false;
                    }
                    // init event listners 
                    if (callback_output==null)
                        callback_output = 
                            function(event:ProgressEvent):void {
                                var process:NativeProcess = event.target as NativeProcess;
                                trace("OUT -", process.standardOutput.readUTFBytes(process.standardError.bytesAvailable)); 
                            };
                    if (callback_error==null)
                        callback_error = 
                            function(event:ProgressEvent):void {
                                var process:NativeProcess = event.target as NativeProcess;
                                trace("ERROR -", process.standardError.readUTFBytes(process.standardError.bytesAvailable)); 
                            };
                    if (callback_ioerror==null)
                        callback_ioerror = 
                            function(event:IOErrorEvent):void {
                                trace(event.toString());
                            };
                
                    var ext_dir:File;
                    try {
                        ext_dir = ExtensionContext.getExtensionDirectory(extensionID);
                    } catch (e:*) {
                        if (onExit!=null)
                            onExit(extensionID, false);
                        return false;
                    }
                    if (!ext_dir.isDirectory) {
                        if (onExit!=null)
                            onExit(extensionID, false);
                        return false;
                    }
                    var ane_dir:File = ext_dir.resolvePath("META-INF/ANE/");
                    var ext_stream:FileStream = new FileStream();
                    ext_stream.open(ane_dir.resolvePath("extension.xml"), FileMode.READ);
                    var ext_xml:XML = XML(ext_stream.readUTFBytes(ext_stream.bytesAvailable));
                    ext_stream.close();
                
                    var defaultNS:Namespace = ext_xml.namespace("");
                    var framework:String = ext_xml.defaultNS::platforms.defaultNS::platform.(@name=="MacOS-x86").defaultNS::applicationDeployment.defaultNS::nativeLibrary.text();
                    if (!framework) {
                        if (onExit!=null)
                            onExit(extensionID, false);
                        return false;
                    }
                
                    var framework_dir:File = ane_dir.resolvePath('MacOS-x86/'+framework);
                    // list of symlink files
                    var symlink:Vector.<String> = new Vector.<String>(3, true);
                    symlink[0] = 'Resources';
                    symlink[1] = framework_dir.name.substr(0, framework_dir.name.length-framework_dir.extension.length-1);
                    symlink[2] = 'Versions/Current';
                    var fileToFix:int = symlink.length,
                        fileFixed:int = 0,
                        fileFailed:int = 0;
                    symlink.every(
                        function(item:String, index:int, a:Vector.<String>):Boolean {
                            var f:File = framework_dir.resolvePath(item);
                            if (!f.isSymbolicLink) {
                                var fs:FileStream = new FileStream();
                                fs.open(f, FileMode.READ);
                                var lnk:String = fs.readUTFBytes(fs.bytesAvailable);
                                fs.close();                        
                                var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
                                nativeProcessStartupInfo.executable = new File('/bin/ln');
                                nativeProcessStartupInfo.workingDirectory = f.parent;
                                nativeProcessStartupInfo.arguments = new Vector.<String>(3, true);
                                nativeProcessStartupInfo.arguments[0] = "-Fs";
                                nativeProcessStartupInfo.arguments[1] = lnk;
                                nativeProcessStartupInfo.arguments[2] = f.name;
                
                
                                var process:NativeProcess = new NativeProcess();    
                                process.start(nativeProcessStartupInfo);
                                //process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, callback_output);
                                process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, callback_error);
                                process.addEventListener(IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, callback_ioerror);
                                process.addEventListener(IOErrorEvent.STANDARD_ERROR_IO_ERROR, callback_ioerror);
                                process.addEventListener(
                                    NativeProcessExitEvent.EXIT, 
                                    function (event:NativeProcessExitEvent):void {
                                        if (event.exitCode==0)
                                            fileFixed++;
                                        else
                                            fileFailed++
                
                                        if (fileFixed+fileFailed==fileToFix) {
                                            if (fileFailed==0) 
                                                trace('ANE '+extensionID+' fixed.');
                                            else
                                                trace('Unable to fix ANE '+extensionID+'!');
                                            if (onExit!=null)
                                                onExit(extensionID, fileFailed==0);
                                        }
                                    }
                                );
                            } else
                                fileFixed++;
                            return true;
                        }
                    );
                    return true;
                }
                

                 

                call this function passing the id of the extension and optionally a callback function:

                 

                 

                function onFix(extensionID:String, success:Boolean):void {
                     if (success) {
                          // do the ane code...
                     }
                }
                fixANE("package.ANETest", onFix);
                
                • 5. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
                  fluidllc Community Member

                  well done, sir.  i'm surprised this hasn't bubbled as a higher bug since it's so critical.  i can only assume not many people are working on mac, lion, and air for desktop.

                  • 6. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
                    sbaldizz Community Member

                    probably because native extensions are a feature introduced recently and, for me, not effectively documented.

                    • 7. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
                      Mallika Yelandur Employee Hosts

                      sbaldizz,

                      Just wanted to follow-up on your comment re: documentation for native extensions -

                       

                      If you haven't already, you may want to check out: http://help.adobe.com/en_US/air/extensions/index.html. There's some deep-dive documentation out there about native extensions.

                       

                      And for using native extensions with Flash Builder, you can see http://help.adobe.com/en_US/flex/mobileapps/WSe4e4b720da9dedb5-4aefe03513238d8a1b8-8000.ht ml.There are a few links in this article that can lead you to more information about using native extensions.

                       

                      Hope this helps,

                      Mallika Yelandur

                      Adobe Community Help & Learning

                      • 8. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
                        meekgeek

                        Holy smokes!!

                         

                        Slaving at this since Saturday just to find out the tools are broken!

                         

                        Sbaldizz, thank you for the script!

                        • 9. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
                          Mike Bresnahan Community Member

                          I ran into this problem as well. One interesting thing that I discovered is that if you create a Flex Mobile project and add a iPhoine-ARM/MacOS-x86 dual platform extension  everything works just fine. The problem appears to be isolated to AIR desktop apps.

                          • 10. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
                            YNABteam Community Member

                            I just ran into this as well. Thank you for the post. This isn't fixed in FB 4.7 beta 1 either. Not sure about Beta 2 (download link is currently broken). I am looking for other workarounds.

                            • 11. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
                              EllisElkins Community Member

                              There's another problem with the symlinks to watch out for. I built the framework then copied the directory over to the directory where I would build the ane from. I knew that by doing this the symlink files would still be pointing to the folders and files of the original framework folder that I copied, so I deleted the copied symlink files and remade them in Finder. Turns out that making these symlink files in Finder caused the files to not work. So instead of copying the framework folder I dragged it to where I would build the ane. I believe another way to do this would be to create the symlink files in terminal, though I haven't tried this. Hope this helps.

                              • 12. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
                                sbaldizz Community Member

                                On FB 4.7 and AIR 3.5 the bug still exists!

                                • 13. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
                                  sbaldizz Community Member

                                  You can't create symblink with finder that make an alias. With terminal the command for create symblink is `ln -s `.

                                  • 14. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
                                    keldonrush Community Member

                                    Sbaldizz, thank you for this runtime debug workaround - this has been a painful journey and it is over thanks to you.

                                    Fixing ADL's shortcomings with AIR itself - very well done and much appreciated.

                                     

                                    Thank you!

                                    • 15. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
                                      keldonrush Community Member

                                      I have found a better workflow for me in the form of a bash script that I use to build my ANE.

                                      I am running OSX Lion.

                                       

                                      I like this better because then I am not executing code during debug that I won't be executing at runtime.

                                      This bash script 'make file' solves the problem of the symbolic links not getting treated correctly by ADL by beating it to the punch and replacing the symlinks with the files they point to.

                                      The super key line is :

                                      cp -R -L ../xcode/Build/Products/Debug/ ../bin/ane/tmp
                                      

                                       

                                      The -R makes the copy procedure recursive and the -L flag replaces all symlinks found anywhere in the recursive directory structure with the files they reference.

                                      In my Xcode project's directory structure the 'Debug' directory contains the .framework folder that is the C part of my ANE.

                                       

                                      #!/bin/sh

                                      # set the path to the installed SDK to a handy variable for the script AIR_SDK=/SDKs/flex_sdk_4.6_w_air_sdk_3.8/ # clear the contents of the target dir rm -rf ../bin/ane/* # create tmp dir mkdir -p ../bin/ane/tmp # copy Mac framework while replacing symlinks with actual files cp -R -L ../xcode/Build/Products/Debug/ ../bin/ane/tmp # copy the SWC while renaming it to ZIP cp ../bin/ANE_DEVICE.swc ../bin/ane/tmp/ANE_DEVICE.swc # unzip SWC to a dir named 'swc' unzip ../bin/ane/tmp/ANE_DEVICE.swc -d ../bin/ane/tmp/swc/ # copy the library.swf into tmp cp ../bin/ane/tmp/swc/library.swf ../bin/ane/tmp/library.swf # package ANE "$AIR_SDK"/bin/adt -package \      -target ane ../bin/ane/ANE_DEVICE.ane ../bin/device_extension.xml \      -swc ../bin/ANE_DEVICE.swc \      -platform MacOS-x86 -C ../bin/ane/tmp/ . # delete the tmp dir rm -r ../bin/ane/tmp

                                      • 16. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
                                        ThijsInteractionDesign

                                        It seems I also have this problem, but I am not sure whether I can fix it with this. Where and how do I compile your code? Does it have to be in the final flash as3 code of the app that I try to make? Or is it a separate piece of coding? Or do I need to include this when exporting the library? I hope it still works anyhow, we are over a year later and both Xcode, Flash, Flash Builder and AIR have been updated several times ever since, so I am curious if somebody can help me here.

                                        • 17. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
                                          keldonrush Community Member

                                          The bash script above is executed to package the ANE - it is run from the command line to create a new build of the ANE.

                                          Obviously paths and values will need to be changed to match your project.

                                           

                                          After you are packaging the ANE with this bash file then everything will work at runtime.

                                           

                                          The original fix is ActionScript based - therefore is used at runtime -  and needs to be run once before you ever try and call the ANE.

                                          • 19. Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
                                            HUGO1SF

                                            sbaldizz Thanks! been wondering this still exists, but your fix rocks.i ever meet you i'll buy you beer

                                            • 20. Re: Re: Problem with Flash Builder 4.6 when debugging native extension for OSX
                                              ddyer00 Community Member

                                              This works for debugging, but fails for release builds, ironically because there is nothing to fix.

                                              This version works for both debugging and release.

                                               

                                                   /** @param extensionID:String id of the ane extension to fix  
                                                         * @param onExit:Function callback function with first arguments the value of extensionID and for the second the boolean state of the fix operation  
                                                         **/  
                                                        private function fixANE(extensionID:String, onExit:Function=null):Boolean
                                                        {      var error : Error = null;
                                                             var fileFailed : int = 0;
                                                             var fileFixed : int = 0;
                                                             
                                                             try 
                                                             {
                                                             if (!NativeProcess.isSupported) {  
                                                                  trace("Unsupported exit");
                                                                  if (onExit!=null)  
                                                                       onExit(extensionID, false);  
                                                                  return false;  
                                                             }  
                                                             // init event listners   
                                                             if (callback_output==null)  
                                                                  callback_output =   
                                                                       function(event:ProgressEvent):void {  
                                                                            var process:NativeProcess = event.target as NativeProcess;  
                                                                            trace("OUT -", process.standardOutput.readUTFBytes(process.standardError.bytesAvailable));   
                                                                       };  
                                                             if (callback_error==null)  
                                                                  callback_error =   
                                                                       function(event:ProgressEvent):void {  
                                                                            var process:NativeProcess = event.target as NativeProcess;  
                                                                            trace("ERROR -", process.standardError.readUTFBytes(process.standardError.bytesAvailable));   
                                                                       };  
                                                             if (callback_ioerror==null)  
                                                                  callback_ioerror =   
                                                                       function(event:IOErrorEvent):void {  
                                                                            trace(event.toString());  
                                                                       };  
                                                             
                                                             var ext_dir:File;  
                                                             try {  
                                                                  ext_dir = ExtensionContext.getExtensionDirectory(extensionID);  
                                                             } catch (e:*) {  
                                                                  trace("GetExtensionDirectory error "+e);
                                                                  if (onExit!=null)  
                                                                       onExit(extensionID, false);  
                                                                  return false;  
                                                             }  
                                                             if (!ext_dir.isDirectory) {  
                                                                  trace(ext_dir + "not isDirectory ")
                                                                  if (onExit!=null)  
                                                                       onExit(extensionID, false);  
                                                                  return false;  
                                                             }  
                                                             var ane_dir:File = ext_dir.resolvePath("META-INF/ANE/");  
                                                             var ext_stream:FileStream = new FileStream();  
                                                             ext_stream.open(ane_dir.resolvePath("extension.xml"), FileMode.READ);  
                                                             var ext_xml:XML = XML(ext_stream.readUTFBytes(ext_stream.bytesAvailable));  
                                                             ext_stream.close();  
                                                             var defaultNS:Namespace = ext_xml.namespace("");  
                                                             var framework:String = ext_xml.defaultNS::platforms.defaultNS::platform.(@name=="MacOS-x86").defaultNS::applicationDeployment.defaultNS::nativeLibrary.text();  
                                              
                                                             if (!framework) {  
                                                                  trace("No MacOS framework "+framework);
                                                                  if (onExit!=null)  
                                                                       onExit(extensionID, false);  
                                                                  return false;  
                                                             }  
                                                             
                                                             var framework_dir:File = ane_dir.resolvePath('MacOS-x86/'+framework);  
                                                             // list of symlink files  
                                                             var symlink:Vector. = new Vector.(3, true);  
                                                             symlink[0] = 'Resources';  
                                                             symlink[1] = framework_dir.name.substr(0, framework_dir.name.length-framework_dir.extension.length-1);  
                                                             symlink[2] = 'Versions/Current';  
                                                             var fileToFix:int = symlink.length;  
                                                             symlink.every(  
                                                                  function(item:String, index:int, a:Vector.):Boolean {  
                                                                       var f:File = framework_dir.resolvePath(item);  
                                                                       if (!f.isSymbolicLink) {  
                                                                            var fs:FileStream = new FileStream();  
                                                                            fs.open(f, FileMode.READ);  
                                                                            var lnk:String = fs.readUTFBytes(fs.bytesAvailable);  
                                                                            fs.close();                          
                                                                            var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();  
                                                                            nativeProcessStartupInfo.executable = new File('/bin/ln');  
                                                                            nativeProcessStartupInfo.workingDirectory = f.parent;  
                                                                            nativeProcessStartupInfo.arguments = new Vector.(3, true);  
                                                                            nativeProcessStartupInfo.arguments[0] = "-Fs";  
                                                                            nativeProcessStartupInfo.arguments[1] = lnk;  
                                                                            nativeProcessStartupInfo.arguments[2] = f.name;  
                                                                            
                                                                            
                                                                            var process:NativeProcess = new NativeProcess();      
                                                                            process.start(nativeProcessStartupInfo);  
                                                                            process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, callback_output);  
                                                                            process.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, callback_error);  
                                                                            process.addEventListener(IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, callback_ioerror);  
                                                                            process.addEventListener(IOErrorEvent.STANDARD_ERROR_IO_ERROR, callback_ioerror);  
                                                                            process.addEventListener(  
                                                                                 NativeProcessExitEvent.EXIT,   
                                                                                 function (event:NativeProcessExitEvent):void {  
                                                                                      if (event.exitCode==0)  
                                                                                      {     fileFixed++;  
                                                                                      }
                                                                                      else  
                                                                                      {     fileFailed++  
                                                                                      }
                                                                                      
                                                                                 }  
                                                                            );  
                                                                       } else  
                                                                            fileFixed++;  // count as fixed if it doesn't appear to be in need of fixing
                                                                       return true;  
                                                                  }  
                                                             );  
                                                        } // end of outer try 
                                                        catch (err : *)
                                                        {     error = err;
                                                             trace("fixANE error "+err);          
                                                        }
                                                        
                                                        if (onExit!=null)  
                                                             {onExit(extensionID, fileFailed==0 && (error==null));  
                                                             }
                                                        return(true);
                                                        }
                                                   }