Skip to content
elemoine edited this page Mar 10, 2013 · 13 revisions

Alongside js files the src dir includes "exports files" (e.g. src/ol/map.exports). An export file declares symbols and properties that are to be exported when creating builds of the library. For example the src/ol/map.exports file includes this:

@exportClass ol.Map ol.MapOptions
@exportProperty ol.Map.prototype.addLayer

This declares that the ol.Map symbol, and that the ol.Map.prototype.addLayer property, should be exported, i.e. made available to lib users.

Exports files are processed by the bin/generate-exports script, using its --exports switch. This script receives a list of exports files as its input, and outputs @exportSymbol and @exportProperty statements. The output of generate-exports --exports goes into build/src/external/src/exports.js, which is then used as an input file for the ol.js build (see build/ol.json).

Note: the src/google.exports file just makes the goog.require function be exported as a null function. This is just to be able to execute the examples both with Plovr in raw mode (?mode=RAW) – where goog.require statements are required – and against the ol.js build.

The src/objectliterals.exports file is specific. This file declares the config objects and properties for the lib's single-arg constructors. These declarations are done with the @exportObjectLiteral and @exportObjectLiteralProperty directives. This file is also processed with the bin/generate-exports script, but using the --externs and --typedef switches.

The generate-exports --externs generates Closure externs from the @exportObjectLiteral and @exportObjectLiteralProperty directives. The build.py script places these externs in the build/src/external/externs/types.js file, which is used as a regular externs file by the Closure compiler (see build/ol.json).

The generate-exports --typedef command generate typedef's for the object literals declared with @exportObjectLiteral and @exportObjectLiteralProperty. The build.py script places the typedef's in the build/src/external/src/types.js, which, like exports.js, is used as an input file for the ol.js build (see build/ol.json).

Externs define types for config objects created by lib users, while typedef's define types created internally by the lib.

OL3 devs and contributors need to create new exports files when adding new API elements to the lib. They also need to declare single-arg constructors' config objects and properties in src/objectliterals.exports.

Under the hood

build/src/internal/src/requireall.js is used in the build.py build-all target to ensure that all source files are passed to the compiler.

Before reading on, you should have a thorough understand of how the Closure Compiler handles exports and externs.

The exports/externs/typedefs system is required so that the same source files can be used in several modes:

  1. Uncompiled (i.e. mode=RAW)
  2. Application and ol3 library compiled together
  3. ol3 library compiled, application uncompiled

Cases 1 are 2 similar: no exports are needed (except for ol.Object property get and set methods whose names must be preserved), and object literals passed to single argument constructors are defined as @typedefs so that they can be type checked and the compiler can rename their properties. Everything is "internal".

Case 3 is more complicated: we need to ensure that the appropriate library functions are exported so they can be used by application code. However, anything that is exported will be included in the built library and so increase the library size, hence the need for custom builds. Coarse-grained control over the custom build contents is achieved by deciding which .exports files to pass to the compiler. Object literals to single argument constructors are more complicated: these are supplied by the application, so the compiled library must not rename their properties. So, in "external" mode, in addition to the @typedefs, each object literals must be shadowed by an @extern declaration that declares an @interface in the olx namespace. A separate namespace is needed to avoid conflicts with the ol namespace used internally. Furthermore, a shadow derived class (e.g. ol.MapExport) is defined for each exported class that transparently translates the un-renamed external object literal into a possibly renamed internal object literal. This derived class is exported as its parent class by a cunning rename when it's exported. Easy, huh?

Luckily, all this complexity is handled by the generate-exports script. The ol3 developer need only add his declarations to the .exports files and everything is auto-generated from this single source.

Approaches that don't work

Using the @export annotation

Using @export in the source code means that all exported symbols would always be exported, this prevents custom builds.

Using the @expose annotation

This also prevents custom builds, as well as increasing the generated code size.