Production pipeline


#1

This is a great idea, which should be continued. I watched most of them. I would like to participate and show the way I use as3.

Nothing discovering, but I think I have a nice pipeline setup, which allows me to build apps/games as solo developer for all these platforms supported by AdobeAIR.


Poll: Do you want a Flash Online Conference
#2

would love to watch you use said pipeline!


#3

I don’t have the time to go in full details but I have the following pipeline that works quite well

in shorts

  • it is based on Flash Builder (eg. Eclipse)
  • I create a workspace for a specific “global project”
  • in the workspace, each project is a publishing target
  • I use “linked resources”
  • an ant build with multiple targets

I use a common project for everything shared in Flash and AIR

where I have a main class Application followed by specialized classes like WebApplication, DesktopApplication, MobileApplication wher I will just customise the “onResize” logic for example

and depending on the publish target I create a new project for that target, for example “ios” with a main class IOSApplication that extends MobileApplication.

In that “ios” project I use “assets”, “common” and “common/lib-swc” as a linked resource
eg.

	<linkedResources>
		<link>
			<name>assets</name>
			<type>2</type>
			<locationURI>WORKSPACE_LOC/assets</locationURI>
		</link>
		<link>
			<name>common</name>
			<type>2</type>
			<locationURI>WORKSPACE_LOC/common/src</locationURI>
		</link>
		<link>
			<name>lib-swc</name>
			<type>2</type>
			<locationURI>WORKSPACE_LOC/common/lib-swc</locationURI>
		</link>
	</linkedResources>

in term of structure it gives you that

.
├── android
│   ├── bin-debug
│   ├── config-local.xml
│   └── src
├── assets
│   └── ...
├── bin-deploy
├── bin-release
├── build
│   ├── ant
│   ├── common.properties
│   ├── config.xml
│   ├── tasks
│   └── version.properties
├── build.xml
├── common
│   ├── bin-debug
│   ├── config-local.xml
│   ├── lib-swc
│   └── src
├── ios
│   ├── bin-debug
│   ├── config-local.xml
│   └── src
├── linux
│   ├── bin-debug
│   ├── config-local.xml
│   └── src
├── macosx
│   ├── bin-debug
│   ├── config-local.xml
│   └── src
├── readme.txt
├── web
│   ├── bin-debug
│   ├── config-local.xml
│   ├── html-template
│   └── src
└── windows
    ├── bin-debug
    ├── config-local.xml
    └── src

It seems overkill, but with that I can handle a lot of publishing targets in parallel with a minimal effort :smile:

90% of the source code is in the common project

a “publishing target” is merely a mini-project where I use a wrapper to mainly configure the app

it works like that, let’s say I need to publish an online swf
to me that’s a new project “web”
with a main class SWFApplication that extends WebApplication

in details

web/
├── bin-debug
│   └── ...
├── config-local.xml
├── html-template
│   ├── history
│   ├── index.template.html
│   ├── playerProductInstall.swf
│   └── swfobject.js
└── src
    └── SWFApplication.as

see the project is super light, it’s merely 1 main class

the class look like that

package
{
    import corsaair.demoapp.defaultConfiguration;
    import corsaair.demoapp.defaultLayout;
    import corsaair.demoapp.modules.screenshots.ScreenshotGeneral;
    import corsaair.demoapp.system.Configuration;
    import corsaair.demoapp.system.Layout;
    
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;

    /**
     * SWF Applicatiom.
     * By default, the app occupy a fixed area in the browser window.
     * The area width and height are defined by the SWF published size,
     * here we used the metadata to have a 1024x768.
     */
    [SWF(width="1024", height="768", frameRate="24", backgroundColor="#ffffff")]
    public class SWFApplication extends WebApplication
    {
        public function SWFApplication()
        {
            LOG::P{ trace( "SWFApplication ctor" ); }
            var config:Configuration = defaultConfiguration();
            var layout:Layout        = defaultLayout();
            
            //you can still override the viewport to use a fixed area
            config.viewport   = new Rectangle( 0, 0, 800, 600 );
            config.fullscreen = false;
            
            //or override the locale
            config.locale = "ja-JP";
            
            config.catchGlobalError = true;
            config.showGlobalError = true;
            
            super( config, layout );
        }
        
        private function onClick( event:MouseEvent ):void
        {
            trace( "SWFApplication.click()" );
            
            takeScreenshot();
        }
        
        public override function main():void
        {
            LOG::P{ trace( "SWFApplication.main()" ); }
            //always call Application.main()
            super.main();
            
            //modules registry
            register( "screenshot", ScreenshotGeneral );
            
            LOG::P{ trace( "---------------------------" ); }
            LOG::P{ trace( "config:" + config.toString() ); }
            LOG::P{ trace( "---------------------------" ); }
            
            appbackground.addEventListener( MouseEvent.CLICK, onClick );
            
            LOG::P{ trace( "---------------------------" ); }
            LOG::P{ trace( strings.toLineFormat() ); }
            LOG::P{ trace( "---------------------------" ); }
        }
        
    }
}

2 tricks

  • use the constructor to override the config and layout
    for example:
var config:Configuration = defaultConfiguration();
    config.viewport   = new Rectangle( 0, 0, 800, 600 );
    config.fullscreen = false;

super( config );
  • override the main function to use custom modules
    I basically defines interfaces for shared functionalities
    and I use a global registry to register an implementation
    register( "screenshot", ScreenshotGeneral );

which lead to this “hack”

A registry

package corsaair.demoapp.system
{
    import corsaair.demoapp.modules.screenshots.Screenshot;
    
    public class Registry
    {
        public function Registry()
        {
            
        }
        
        public var screenshot:Screenshot;
    }
    
}

Screenshot is an interface

package corsaair.demoapp.modules.screenshots
{
    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.geom.Rectangle;

    public interface Screenshot
    {
        function take( source:DisplayObject, rect:Rectangle = null, smoothing:Boolean = false, quality:String = "" ):BitmapData;
        function save( data:BitmapData, filename:String = null, fastCompression:Boolean = false ):void;
    }
}

and so when I call
register( "screenshot", ScreenshotGeneral );

I basically tell the system to instantiate the Registry.screenshot property with the class ScreenshotGeneral which is an implementation of the interface.

If my app wrapper is an AIR app I can then use a different implementation ScreenshotAIR, or if I want to null out the implementation I instantiate a mock or null object instead eg. ScreenshotNull class

It is the principle of the dependency injection but done by wrapper class and manual configuration, I use that almost everywhere to build custom object but also to build the UI and the layout.

This main wrapper class can also be customised, for example “oh i need to publish a SWF but this one need to be full screen instead of a fixed size”

I just create another wrapper with a different config and/or layout
eg. public class DynamicSWFApplication extends WebApplication

Same for mobile apps, let’s say I use by default
public class AndroidApplication extends MobileApplication
well if I need special stuff for the google play store
and other stuff for the amazon store

I use 2 classes instead
public class GoogleAndroidApplication extends AndroidApplication
public class AmazonAndroidApplication extends AndroidApplication

other tricks is to also have shared assets
where I can define languages and locales for string resources
or simply icons and other images and Ui elements

and this work in sync with the ant build
where for each type of project I have a publishing target
eg. /build/target/android.xml

and from the main build I simply do something like that

    <target name="compile">
    	<antcall target="compile-android" if:true="${build.android}"/>
    </target>

I know it is very specialised, but with that system I can build apps for either SWF, AIR desktop or AIR mobile or all at the same time.

it also have the advantage to be able to start on only 1 target like Android, and later on add a new one like iOS or Desktop, etc.

It’s not magic, if you wrote code mainly for Android, most of the build will break for iOS but then you replace all your custom implementations by null or mock object and step by step you replace or update implementations to make it work for your new target.

I just wish I had the time to automate this pipeline so I could have a small command-line tool based on templates that setup the whole thing for me instead of doing it manually :smile:


#4

Dude, so would you ever consider presenting your pipeline at Sergey’s Flash Online Conference #13?

For ~6 years (2007-2012) I was the lead developer of many simulations and online science labs for distant learning students but I gotta tell ya, it’s been so long since I’ve touched the stuff! Your pipeline sounds intriguing but just reading it is a little difficult to digest, given that in 2011/12, we were just beginning to use AIR for mobile and our pipeline consisted mainly of I make a SWC and hand it to 2 other developers in the cube next to me so they could program their assets/code against that black-box library :slight_smile:

It would be great to watch a video of you dissecting the pipeline via your tool chain/etc. It’s great to see that you use Flash Builder. I am biased towards that because, as you mention, it’s Eclipse under the hood. Is there any way to take a vanilla Eclipse and add Flash Builder functionality via community plugins? Getting folks to pay for and use a product with the word “Flash” in it unfortunately won’t fly in the corporate environment!


#5

no time for that

I wanted at one moment to publish and explain the very details about how this is organised but it’s not just dropping a template and changing a couple of things, it’s long and complicated to explain.

It’s a bit like eXtreme Programming, f you just do Test Driven Development, it can be good, it can help, but XP is bigger than just TDD and unit tests, it’s a system with different parts, if you don’t do all the parts the system does not work completely.

I’m afraid a short preso on Flash Online COnf would not help much as it would require at least a full “demo project” with documentation and really I have no time to produce that just yet.


#6

I hear you man. I’ve got many projects that I’m trying to pair down on over here as well! Looking forward to this Friday’s conference either way. See ya there!