Wednesday, September 2, 2009

Conditional Module Inclusion and Compilation

Inspired by the thread of "conditional module inclusion and compilation" in PLT mailing list, bzlib/os provides a way to conditionally run code depending on the system type.

To start, require bzlib/os:
(require (planet bzlib/os))


Conditional Require

To require modules only within windows, use require/windows.

(require/windows <require-spec> ...)

The <require-spec> takes the same form as regular require statements. In Mac OS or Unix, the above statement evalutes to (void).

To do the same for Mac OS, use require/macosx. And use require/unix for Unix.

To specify multiple platform in one statement - use require/os.

(require/os (:windows <required-spec> ...)
(:macosx <required-spec> ...)
(:unix <required-spec> ...)
(else <required-spec> ...))

Use :windows to specify the window's branch inclusion. Use :macosx for Macs, and :unix for Unix. All are optional.

Use else to specify non-platform specific includes. If the else branch exists, it must be the last branch.

Conditional Provide

There are also os-dependent provide statements, provided to mirror the require statements (even though it's probably less likely to be used).
  • provide/windows evals to provide on Windows
  • provide/macosx evals to provide on Macs
  • provide/unix evals to provide on Unix
  • provide/os has the same structure has require/os - use :windows, :macosx, and :unix to write OS-dependent provide branch, and use else to write OS independent branch. If the else branch exists it must be the last branch


Both the require/* and provide/* behaves the same as regular require and provide in that they can only be called at top level or module level (provide only works at module level).

Conditional Expressions

The most general form is conditional expressions, which can be called at any position.

For windows, use +:windows:

(+:windows <exp> <exp2>) evaluates to <exp>, and <exp2> otherwise.

(+:windows <exp>) evaluates to <exp>, and (void) otherwise. It is equivalent to (+:windows <exp> (void)).

For Macs, use +:macosx in the same way as above. For Unix, use +:unix.

If you need to write more than 2 OS branch, use +:os, which is the foundation of all above macros:

(+:os (:windows exp)
(:macosx exp)
(:unix exp)
(else exp))

Similar to require/os and provide/os, one of the branches with labels of :windows, :macosx and :unix get evaluated for the respective OS. The else branch is evaluated for platform independent expression; and if it exists it must be the last expression.

It is possible to write (+:os), which gets evaluated to (void).

Caveats

Eli has cautioned that one must make sure no .zo files are copied across platforms by using this pattern.

In general one should write platform independent code, but of course that would not be possible in all situations. It is always better to isolate the OS-specific code to their own modules, and make sure all those modules all expose the same signatures; Unit can aid with this pattern.

Example:
  • bar.ss exposes bar and baz
  • bar-windows.ss exposes bar and baz that work specifically for windows
  • foo.ss includes bar.ss & bar-windows.ss via (require/os (:windows "bar-windows.ss") (else "bar.ss"))

No comments:

Post a Comment