From BlenderWiki

Jump to: navigation, search
Note: This is an archived version of the Blender Developer Wiki. The current and active wiki is available on wiki.blender.org.

This document explains how to setup an online addons system similar to the firefox one, going into details about versioning.

WORK IN PROGRESS

I still need to add:

  • binaries checksums with checksum on b.org
  • svn hooks for doing a svn checkout from download.blender.org, in order to download scripts from d.b.o instead of from svn


How do we enable online extensions?

When user opens the userpref panel:

  • Blender takes bl_addon_info dictionary from all scripts in:
  • Userpref panel shows these info as if the scripts came in the official Blender distribution.
  • If the user tries to enable a certain addon
    • Blender asks permission to download it
    • if the script needs a binary to work, then Blender also asks if the user wants to download the bin itself exposing the size in MB
    • if the user accepts, downloads the addon package (eventually including the binary)
  • Once this happened, Blender enables the addon.

How do we setup python scripts which depend on binaries to work?

If the script needs a binary to work, then:

  • it is required that the addon is a python package (dir + __init__.py)
  • the binary has to go in a subfolder of the package

In bf-extensions svn we have:

extern
└── py
    └── scripts
        └── addons
            ├── <addon package>
            │   ├── __init__.py   <-- bl_addon_info goes here
            │   ├── module/
            │   │   └── <script.py>
            │   │   └── <script.py>
            │   │   └── ...
            │   └── module/
            │       └── <script.py>
            │       └── <script.py>
            │       └── ...
            └── <dict>.py

The file <dict>.py contains a python dictionary where we store urls of the needed binaries. We'll see it in details in the example below.

In our home dir we have:

.blender
└── 2.53
    └── scripts
        └── addons
            └── <addon package>
                ├── __init__.py   <-- bl_addon_info goes here
                ├── module/
                │   └── <script.py>
                │   └── <script.py>
                │   └── ...
                ├── module/
                │   └── <script.py>
                │   └── <script.py>
                │   └── ... 
                └── bin/
                    └── <exe>
                    └── <exe>
                    └── ...


Why do we need an extern/ folder in svn?

If a script is developed in an external project, developing it also in bf-extensions is not an option because this would cause confusion, possible forking and difficulties in merging separate versions.

Also, if the script pretends to download a binary compiled from a source code not developed on blender.org servers, then blender.org won't distribute the addon, neither in official releases, nor from blender.org servers. In this case it will be asked the addon developer to develop outside of blender.org svn and use extern/ directory as described below.


Practical example

This example is based on a discussion with dougal2 (LuxRender), Kerbox (YafaRay) and xat (GameKit).

As a practical example, let's use LuxRender on Linux 32 bit.

Legenda:

  • LuxRender = name of the addon
  • LuxBlend = python exporter Blender -> LuxRender, it can:
    • export ascii file to be read, or
    • use pylux to show Lux renders inside Blender render view
  • pylux = name of the LuxRender python bindings


Initial situation

We start with:

  • Blender 2.53 (revision 30596)
  • LuxRender 0.7 (here we implicitly assume 0.7.0)

We put the addon in bf-extensions in the extern/ directory, at revision 887.

In bf-extensions we have:

bf-extensions
└── extern
    └── py
        └── scripts
            └── addons
                └── luxrender
                    ├── 0.7
                    │   └── luxblend
                    │       ├── __init__.py  <-- bl_addon_info['version'] = (0,7,0)
                    │       │                    bl_addon_info['api'] = 30596
                    │       └── ...
                    └── data.py

The file pylux.py contains a dictionary to retrieve:

Executables urls
For every Blender version we report the urls where we can get a certain version of the needed binary.
URLs can be missing if a build for a certain platform isn't available.
Compatibility with blender api versions
Every time the Blender API changes, we note down which revision of the addon was valid for that Blender API change revision.
If an addon is valid until the current Blender SVN, we use -1.


bl_addon_data =
    {
    (2,5,3):
        {
        (0,7):
            {
            'binary_name':'pylux',
            'api_compatibility':
                {
                30596:{
                    (0,7,0):(887,-1)
                    }
                },
            'binary_urls':
                {
                'linux-32':'<pylux 0.7.0 executable URL>',
                'linux-64':'<pylux 0.7.0 executable URL>',
                'windows-32':'<pylux 0.7.0 executable URL>',
                'windows-64':'<pylux 0.7.0 executable URL>',
                'osx-intel':'<pylux 0.7.0 executable URL>',
                'osx-ppc':'<pylux 0.7.0 executable URL>'
                }
            }
        }
    }
IMPORTANT: URLs
These URLs must have a baseurl permitted by Blender, so that we know that the source is somewhat trusted.

We shouldn't permit personal builds neither from individuals sites neither from graphicall.org or similar sites.

It is desirable that developers of a big project accept the responsability to give secure and working binaries.

In this case only http://www.luxrender.net/ or its subdomains and/or sub pages would be accepted for the script to be distributed from blender.org.


Locally we have:

.blender
└── 2.53
    └── scripts
        └── addons
            └── luxrender
                └── 0.7
                    ├── luxblend
                    │   ├── __init__.py   <-- bl_addon_info['version'] = (0,7,0)
                    │   │                     bl_addon_info['api'] = 30596
                    │   └── ...
                    └── pylux
                        └── <pylux 0.7.0 executable>


Blender API fixes

When Blender API changes, Blender API developers can do fixes to python files in extern/py/scripts/addons/.

When this happens:

  • we change bl_addon_info['version'] in the addon,
  • we update bl_addon_data in pylux.py
  • the addon developers merge the fixes back in the external repository.

Suppose that the API changes at Blender revision 32000, when bf-extensions is at revision 900.

We make the fix at bf-extensions revision 910. We have:

bf-extensions
└── extern
    └── py
        └── scripts
            └── addons
                └── luxrender
                    ├── 0.7
                    │   └── luxblend
                    │       ├── __init__.py  <-- bl_addon_info['version'] = (0,7,0)
                    │       │                    bl_addon_info['api'] = 32000
                    │       └── ...
                    └── data.py

and

bl_addon_data =
    {
    (2,5,3):
        {
        (0,7):
            {
            'binary_name':'pylux',
            'api_compatibility':
                {
                32000:{
                    (0,7,0):(910,-1)
                    },
                30596:{
                    (0,7,0):(887,899)
                    }
                },
            'binary_urls':
                {
                'linux-32':'<pylux 0.7.0 executable URL>',
                'linux-64':'<pylux 0.7.0 executable URL>',
                'windows-32':'<pylux 0.7.0 executable URL>',
                'windows-64':'<pylux 0.7.0 executable URL>',
                'osx-intel':'<pylux 0.7.0 executable URL>',
                'osx-ppc':'<pylux 0.7.0 executable URL>'
                }
            }
        }
    }

This means that LuxBlend 0.7.0 might not work if downloaded from bf-extensions at a revision within 900 and 909.

The strategy to manage this is to explain this problem in bl_addon_info['warning'].

This is especially good for the pre-release, when we can check if any warning sign shows up, so to fix any problem before the release.

Note
Note that the API might have changed in parts not used by the addon, so in our example LuxBlend (0,7,0,30596) might work in Blender rev.32000, when API has changed. We note down the API change revisions and put a warning anyway to avoid to verify in greater detail, hence making this process simpler.


What we have locally depends on the Blender revision:

Used Blender revision fix available in bf-extensions bl_addon_info['version'] warning
31000 Yes (0,7,0,30596) No
32050 No (0,7,0,30596) Yes
32050 Yes (0,7,0,32000) No


Addon bug fixes/enhancements

When the addon developers fix a bug in the addon itself, they should bump the least significant number in the addon version.

For example:

  • LuxRender has a 2 numbers in its version (ex: 0.7), hence the bugfixed addon should have version = 0.7.1 (still in sync with pylux 0.7)
  • YafaRay has 3 numbers in its version (ex: 0.1.2), hence the bugfixed addon should have version = 0.1.2.1 (still in sync with yafapy 0.1.2)

Suppose that the addon update happens at bf-extensions revision 975 (same Blender API = rev.32000).

bf-extensions
└── extern
    └── py
        └── scripts
            └── addons
                └── luxrender
                    ├── 0.7
                    │   └── luxblend
                    │       ├── __init__.py  <-- bl_addon_info['version'] = (0,7,1)
                    │       │                    bl_addon_info['api'] = 32000
                    │       └── ...
                    └── data.py

and

bl_addon_data =
    {
    (2,5,3):
        {
        (0,7):
            {
            'binary_name':'pylux',
            'api_compatibility':
                {
                32000:{
                    (0,7,0):(910,974), (0,7,1):(975,-1)
                    },
                30596:{
                    (0,7,0):(887,899)
                    }
                },
            'binary_urls':
                {
                'linux-32':'<pylux 0.7.0 executable URL>',
                'linux-64':'<pylux 0.7.0 executable URL>',
                'windows-32':'<pylux 0.7.0 executable URL>',
                'windows-64':'<pylux 0.7.0 executable URL>',
                'osx-intel':'<pylux 0.7.0 executable URL>',
                'osx-ppc':'<pylux 0.7.0 executable URL>'
                }
            }
        }
    }

When the addon is copied in bf-extensions, users with a Blender revision higher than 974 should get notified at Blender startup, in a non-intrusive or blocking way. Using alert in the info header is a good way to do this. The option of being notified when addons changes should be a userpreference, so that we avoid annoying users if they don't like this feature.

Executable bug fixes/enhancements

The same applies when the binary is a python binding of the executable (in our example, pylux has changed, not LuxRender).

Let's suppose that addon has changed another time and we're at LuxBlend 0.7.2 now.

If pylux 0.7 gets a bugfix, it should be called "pylux 0.7.1" so that eventually LuxBlend 0.7.2 can work with pylux 0.7.1 because they both belong to the 0.7 series.

bf-extensions
└── extern
    └── py
        └── scripts
            └── addons
                └── luxrender
                    ├── 0.7
                    │   └── luxblend
                    │       ├── __init__.py  <-- bl_addon_info['version'] = (0,7,2)
                    │       │                    bl_addon_info['api'] = 32000
                    │       └── ...
                    └── data.py

and

bl_addon_data =
    {
    (2,5,3):
        {
        (0,7):
            {
            'binary_name':'pylux',
            'api_compatibility':
                {
                32000:{
                    (0,7,0):(910,974), (0,7,1):(975,1249), (0,7,2):(1250,-1)
                    },
                30596:{
                    (0,7,0):(887,899)
                    }
                },
            'binary_urls':
                {
                'linux-32':'<pylux 0.7.1 executable URL>',  # !different version!
                'linux-64':'<pylux 0.7.1 executable URL>',
                'windows-32':'<pylux 0.7.1 executable URL>',
                'windows-64':'<pylux 0.7.1 executable URL>',
                'osx-intel':'<pylux 0.7.1 executable URL>',
                'osx-ppc':'<pylux 0.7.1 executable URL>',
                }
            }
        }
    }

As for the addon, the user should be notified by this and a new version of the binary should be downloaded after user acceptance.

Check the Download policy below to see in which cases when the binary gets overwritten or not.

Locally we will have:

.blender
└── 2.53
    └── scripts
        └── addons
            └── luxrender
                └── 0.7
                    ├── luxblend
                    │   ├── __init__.py   <-- bl_addon_info['version'] = (0,7,2)
                    │   │                     bl_addon_info['api'] = 32000
                    │   └── ...
                    └── pylux
                        └── <pylux 0.7.1 executable>  <-- different version!


Download policy

The user should have the chance to keep major versions of the addons and of the executable, while minor changes should be overwritten:

  • LuxBlend 0.7.11 should overwrite 0.7.10
  • pylux 0.7.3 should overwrite 0.7.2

When it comes to the next major version (in our example LuxBlend 0.8/pylux 0.8, see next section) we should implicitly assume a "0.8.0", and in this case

  • LuxBlend 0.8.0 should NOT overwrite 0.7.11
  • pylux 0.8.0 should NOT overwrite pylux 0.7.3


New LuxRender release, same Blender series

Later on, Lux devs release a stable LuxRender called "0.8".

Let's suppose that the new LuxBlend goes in bf-extensions at release 1500, while the Blender API is still "32000".

In bf-extensions we will have:

bf-extensions
└── extern
    └── py
        └── scripts
            └── addons
                └── luxrender
                    ├── 0.7
                    │   └── luxblend
                    │       ├── __init__.py  <-- bl_addon_info['version'] = (0,7,2)
                    │       │                    bl_addon_info['api'] = 32000
                    │       └── ...
                    ├── 0.8
                    │   └── luxblend
                    │       ├── __init__.py  <-- bl_addon_info['version'] = (0,8,0)
                    │       │                    bl_addon_info['api'] = 32000
                    │       └── ...
                    └── data.py

...while pylux.py is:

bl_addon_data =
    {
    (2,5,3):
        {
        (0,8):
            {
            'binary_name':'pylux',
            'api_compatibility':
                {
                32000:{
                    (0,8,0):(1500,-1)
                    }
                },
            'binary_urls':
                {
                'linux-32':'<pylux 0.8.0 executable URL>',
                'linux-64':'<pylux 0.8.0 executable URL>',
                'windows-32':'<pylux 0.8.0 executable URL>',
                'windows-64':'<pylux 0.8.0 executable URL>',
                'osx-intel':'<pylux 0.8.0 executable URL>',
                'osx-ppc':'<pylux 0.8.0 executable URL>'
                }
            },
        (0,7):
            {
            ...
            }
        }
    }

Locally we will have:

.blender
└── 2.53
    └── scripts
        └── addons
            └── luxrender
                ├── 0.7
                │   ├── luxblend
                │   │   ├── __init__.py   <-- bl_addon_info['version'] = (0,7,2)
                │   │   │                     bl_addon_info['api'] = 32000
                │   │   └── ...
                │   └── pylux
                │       └── <pylux 0.7.1 executable>
                └── 0.8
                    ├── luxblend
                    │   ├── __init__.py   <-- bl_addon_info['version'] = (0,8,0)
                    │   │                     bl_addon_info['api'] = 32000
                    │   └── ...
                    └── pylux
                        └── <pylux 0.8.0 executable>


New Blender release, same Lux series

Later on, Blender 2.54 gets released, while LuxRender is still at version 0.8.

Due to bug fixes to LuxBlend and to pylux, we could be at:

  • LuxBlend 0.8.3
  • pylux 0.8.2
bf-extensions
└── extern
    └── py
        └── scripts
            └── addons
                └── luxrender
                    ├── 0.7
                    │   └── luxblend
                    │       ├── __init__.py  <-- bl_addon_info['version'] = (0,7,2)
                    │       │                    bl_addon_info['api'] = 32000
                    │       └── ...
                    ├── 0.8
                    │   └── luxblend
                    │       ├── __init__.py  <-- bl_addon_info['version'] = (0,8,3)
                    │       │                    bl_addon_info['api'] = 32000
                    │       └── ...
                    └── data.py

and

bl_addon_data =
    {
    (2,5,3):
        {
        (0,8):
            {
            'binary_name':'pylux',
            'api_compatibility':
                {
                32000:{
                    (0,8,0):(1500,1600), (0,8,1):(1601,1650),
                    (0,8,2):(1651,1800), (0,8,3):(1801,-1)
                    }
                },
            'binary_urls':
                {
                'linux-32':'<pylux 0.8.2 executable URL>',  # !different version!
                'linux-64':'<pylux 0.8.2 executable URL>',
                'windows-32':'<pylux 0.8.2 executable URL>',
                'windows-64':'<pylux 0.8.2 executable URL>',
                'osx-intel':'<pylux 0.8.2 executable URL>',
                'osx-ppc':'<pylux 0.8.2 executable URL>'
                }
            },
        (0,7):
            {
            ...
            }
        }
    }

Blender's API might be unchanged, LuxRender could be the same, hence we could use the same addon (LuxBlend 0.8.3) and binding (pylux 0.8.2).

More generally, though, the API might be stable for old features, but it might have new functionalities, and the addon for 2.54 could use them.

So, I'm assuming here that a new Blender release also means:

  • a new API version (release revision, let's say it is rev.35000)
  • a new exporter, in this case LuxBlend 0.8.4.

Let's also say we are at bf-extensions revision 2000.

bf-extensions
└── extern
    └── py
        └── scripts
            └── addons
                └── luxrender
                    ├── 0.7
                    │   └── luxblend
                    │       ├── __init__.py  <-- bl_addon_info['version'] = (0,7,2)
                    │       │                    bl_addon_info['api'] = 32000
                    │       └── ...
                    ├── 0.8
                    │   └── luxblend
                    │       ├── __init__.py  <-- bl_addon_info['version'] = (0,8,4)
                    │       │                    bl_addon_info['api'] = 35000
                    │       └── ...
                    └── data.py

In pylux.py, if the binaries have the same urls we simply duplicate for 2.54, or we update them.

bl_addon_data =
    {
    (2,5,4):
        {
        (0,8):
            {
            'binary_name':'pylux',
            'api_compatibility':
                {
                35000:{
                    (0,8,4):(2000,-1)
                    }
                },
            'binary_urls':
                {
                'linux-32':'<pylux 0.8.2 executable URL>',
                'linux-64':'<pylux 0.8.2 executable URL>',
                'windows-32':'<pylux 0.8.2 executable URL>',
                'windows-64':'<pylux 0.8.2 executable URL>',
                'osx-intel':'<pylux 0.8.2 executable URL>',
                'osx-ppc':'<pylux 0.8.2 executable URL>'
                }
            }
        },
    (2,5,3):
        {
        (0,8):
            {
            'binary_name':'pylux',
            'api_compatibility':
                {
                32000:{
                    (0,8,0):(1500,1600), (0,8,1):(1601,1650),
                    (0,8,2):(1651,1800), (0,8,3):(1801,1999)
                    }
                },
            'binary_urls':
                {
                'linux-32':'<pylux 0.8.2 executable URL>',
                'linux-64':'<pylux 0.8.2 executable URL>',
                'windows-32':'<pylux 0.8.2 executable URL>',
                'windows-64':'<pylux 0.8.2 executable URL>',
                'osx-intel':'<pylux 0.8.2 executable URL>',
                'osx-ppc':'<pylux 0.8.2 executable URL>'
                }
            },
        (0,7):
            {
            ...
            }
        }
    }

Locally we have:

.blender
├── 2.53
│   └── scripts
│       └── addons
│           └── luxrender
│               ├── 0.7
│               │   ├── luxblend
│               │   │   ├── __init__.py   <-- bl_addon_info['version'] = (0,7,2)
│               │   │   │                     bl_addon_info['api'] = 32000
│               │   │   └── ...
│               │   └── pylux
│               │       └── <pylux 0.7.1 executable>
│               └── 0.8
│                   ├── luxblend
│                   │   ├── __init__.py   <-- bl_addon_info['version'] = (0,8,3)
│                   │   │                     bl_addon_info['api'] = 32000
│                   │   └── ...
│                   └── pylux
│                       └── <pylux 0.8.2 executable>
└── 2.54
    └── scripts
        └── addons
            └── luxrender
                └── 0.8
                    ├── luxblend
                    │   ├── __init__.py   <-- bl_addon_info['version'] = (0,8,4)
                    │   │                     bl_addon_info['api'] = 35000
                    │   └── ...
                    └── pylux
                        └── <pylux 0.8.2 executable>


New LuxRender release, same Blender series

When later on LuxRender 0.9 is released, we have:

bf-extensions
└── extern
    └── py
        └── scripts
            └── addons
                └── luxrender
                    ├── 0.7
                    │   └── luxblend
                    │       ├── __init__.py  <-- bl_addon_info['version'] = (0,7,2)
                    │       │                    bl_addon_info['api'] = 32000
                    │       └── ...
                    ├── 0.8
                    │   └── luxblend
                    │       ├── __init__.py  <-- bl_addon_info['version'] = (0,8,4)
                    │       │                    bl_addon_info['api'] = 35000
                    │       └── ...
                    ├── 0.9
                    │   └── luxblend
                    │       ├── __init__.py  <-- bl_addon_info['version'] = (0,9,0)
                    │       │                    bl_addon_info['api'] = 35000
                    │       └── ...
                    └── data.py

and

bl_addon_data =
    {
    (2,5,4):
        {
        (0,9):
            {
            'binary_name':'pylux',
            'api_compatibility':
                {
                35000:{
                    (0,9,0):(2501,-1)
                    }
                },
            'binary_urls':
                {
                'linux-32':'<pylux 0.9.0 executable URL>',
                'linux-64':'<pylux 0.9.0 executable URL>',
                'windows-32':'<pylux 0.9.0 executable URL>',
                'windows-64':'<pylux 0.9.0 executable URL>',
                'osx-intel':'<pylux 0.9.0 executable URL>',
                'osx-ppc':'<pylux 0.9.0 executable URL>'
                }
            },
        (0,8):
            {
            'binary_name':'pylux',
            'api_compatibility':
                {
                35000:{
                    (0,8,4):(2000,2500)
                    }
                },
            'binary_urls':
                {
                'linux-32':'<pylux 0.8.2 executable URL>',
                'linux-64':'<pylux 0.8.2 executable URL>',
                'windows-32':'<pylux 0.8.2 executable URL>',
                'windows-64':'<pylux 0.8.2 executable URL>',
                'osx-intel':'<pylux 0.8.2 executable URL>',
                'osx-ppc':'<pylux 0.8.2 executable URL>'
                }
            }
        },
    (2,5,3):
        {
        ...
        }
    }

and

.blender
├── 2.53
│   └── scripts
│       └── addons
│           └── luxrender
│               ├── 0.7
│               │   ├── luxblend
│               │   │   ├── __init__.py   <-- bl_addon_info['version'] = (0,7,2)
│               │   │   │                     bl_addon_info['api'] = 32000
│               │   │   └── ...
│               │   └── pylux
│               │       └── <pylux 0.7.1 executable>
│               └── 0.8
│                   ├── luxblend
│                   │   ├── __init__.py   <-- bl_addon_info['version'] = (0,8,3)
│                   │   │                     bl_addon_info['api'] = 32000
│                   │   └── ...
│                   └── pylux
│                       └── <pylux 0.8.2 executable>
└── 2.54
    └── scripts
        └── addons
            └── luxrender
                ├── 0.8
                │   ├── luxblend
                │   │   ├── __init__.py   <-- bl_addon_info['version'] = (0,8,4)
                │   │   │                     bl_addon_info['api'] = 35000
                │   │   └── ...
                │   └── pylux
                │       └── <pylux 0.8.0 executable>
                └── 0.9
                    ├── luxblend
                    │   ├── __init__.py   <-- bl_addon_info['version'] = (0,9,0)
                    │   │                     bl_addon_info['api'] = 35000
                    │   └── ...
                    └── pylux
                        └── <pylux 0.9.0 executable>

And so on...