Highlighted

Problem with Flash Builder 4.6 when debugging native extension for OSX

Community Beginner ,
Apr 07, 2012

Copy link to clipboard

Copied

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

Views

17.5K

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more

Problem with Flash Builder 4.6 when debugging native extension for OSX

Community Beginner ,
Apr 07, 2012

Copy link to clipboard

Copied

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

Views

17.5K

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Apr 07, 2012 0
New Here ,
May 08, 2012

Copy link to clipboard

Copied

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!

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 08, 2012 0
Community Beginner ,
May 08, 2012

Copy link to clipboard

Copied

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.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 08, 2012 0
New Here ,
May 09, 2012

Copy link to clipboard

Copied

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...

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 09, 2012 0
New Here ,
May 09, 2012

Copy link to clipboard

Copied

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);

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 09, 2012 0
Community Beginner ,
May 12, 2012

Copy link to clipboard

Copied

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.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 12, 2012 0
New Here ,
May 13, 2012

Copy link to clipboard

Copied

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

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 13, 2012 0
Adobe Employee ,
May 14, 2012

Copy link to clipboard

Copied

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.html.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

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 14, 2012 0
New Here ,
May 17, 2012

Copy link to clipboard

Copied

Holy smokes!!

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

Sbaldizz, thank you for the script!

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 17, 2012 0
Explorer ,
Aug 01, 2012

Copy link to clipboard

Copied

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.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Aug 01, 2012 0
Community Beginner ,
Oct 29, 2012

Copy link to clipboard

Copied

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.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Oct 29, 2012 0
New Here ,
Dec 18, 2012

Copy link to clipboard

Copied

On FB 4.7 and AIR 3.5 the bug still exists!

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Dec 18, 2012 0
New Here ,
Nov 01, 2013

Copy link to clipboard

Copied

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.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Nov 01, 2013 0
Explorer ,
Nov 02, 2013

Copy link to clipboard

Copied

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.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Nov 02, 2013 0
New Here ,
Nov 11, 2013

Copy link to clipboard

Copied

Thank you!

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Nov 11, 2013 0
New Here ,
May 03, 2014

Copy link to clipboard

Copied

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

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
May 03, 2014 0
ddyer00 LATEST
New Here ,
Nov 05, 2014

Copy link to clipboard

Copied

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);
          }
     }

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Nov 05, 2014 0
New Here ,
Nov 23, 2012

Copy link to clipboard

Copied

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.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Nov 23, 2012 0
New Here ,
Dec 18, 2012

Copy link to clipboard

Copied

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

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Dec 18, 2012 0
Explorer ,
Aug 02, 2013

Copy link to clipboard

Copied

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!

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Aug 02, 2013 0
Explorer ,
Aug 04, 2013

Copy link to clipboard

Copied

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

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Aug 04, 2013 0