Skip navigation
Cloudy Forest 5 posts
Apr 6, 2012
Currently Being Moderated

Problem with Flash Builder 4.6 when debugging native extension for OSX

Apr 7, 2012 9:23 AM

Tags: #flash_builder #osx #bug #ane #adt #4.6 #native_extension #air_3.1

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

 
Replies
  • Currently Being Moderated
    May 8, 2012 3:10 PM   in reply to Cloudy Forest

    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!

     
    |
    Mark as:
  • Currently Being Moderated
    May 9, 2012 1:31 AM   in reply to Cloudy Forest

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

     
    |
    Mark as:
  • Currently Being Moderated
    May 9, 2012 10:44 AM   in reply to sbaldizz

    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);
    
     
    |
    Mark as:
  • Currently Being Moderated
    May 12, 2012 9:19 AM   in reply to sbaldizz

    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.

     
    |
    Mark as:
  • Currently Being Moderated
    May 13, 2012 5:12 AM   in reply to fluidllc

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

     
    |
    Mark as:
  • Mallika Yelandur
    64 posts
    Jul 13, 2009
    Currently Being Moderated
    May 14, 2012 10:00 PM   in reply to sbaldizz

    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-4aefe03 513238d8a1b8-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

     
    |
    Mark as:
  • Currently Being Moderated
    May 17, 2012 9:56 AM   in reply to Mallika Yelandur

    Holy smokes!!

     

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

     

    Sbaldizz, thank you for the script!

     
    |
    Mark as:
  • Currently Being Moderated
    Aug 1, 2012 8:43 AM   in reply to meekgeek

    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.

     
    |
    Mark as:
  • Currently Being Moderated
    Oct 29, 2012 5:32 AM   in reply to Mike Bresnahan

    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.

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 23, 2012 9:46 AM   in reply to Cloudy Forest

    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.

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 18, 2012 12:16 AM   in reply to YNABteam

    On FB 4.7 and AIR 3.5 the bug still exists!

     
    |
    Mark as:
  • Currently Being Moderated
    Dec 18, 2012 12:18 AM   in reply to EllisElkins

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

     
    |
    Mark as:
  • Currently Being Moderated
    Aug 2, 2013 2:39 AM   in reply to sbaldizz

    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!

     
    |
    Mark as:
  • Currently Being Moderated
    Aug 4, 2013 11:51 PM   in reply to keldonrush

    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

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 1, 2013 4:11 AM   in reply to sbaldizz

    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.

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 2, 2013 12:54 PM   in reply to ThijsInteractionDesign

    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.

     
    |
    Mark as:
  • Currently Being Moderated
    Nov 11, 2013 5:07 AM   in reply to keldonrush

    Thank you!

     
    |
    Mark as:

More Like This

  • Retrieving data ...

Bookmarked By (0)

Answers + Points = Status

  • 10 points awarded for Correct Answers
  • 5 points awarded for Helpful Answers
  • 10,000+ points
  • 1,001-10,000 points
  • 501-1,000 points
  • 5-500 points