Sdk: Conditional code preprocessing

Created on 27 May 2018  路  13Comments  路  Source: dart-lang/sdk

I'm primarily concerned about this for Flutter, where I can't use dart:mirrors.

As a library author, I would like to be able to publish code that conditionally uses new functionality if it's available in a downstream consumer's SDK version.

I could imagine implementing this by checking:

  1. Whether a compile time variable is set.
  2. Whether a method/property is defined/exists in the Flutter SDK API that my library consumes.

For these examples, assume I want to decode a UTF8 string and use data in that string to draw a circle with a particular type of gradient; in the latest SDK, I can draw exactly that gradient, whereas in older SDKs I want to log a warning and draw something close to the desired gradient (perhaps in a more computationally expensive manner or in a manner that is lower fidelity compared with the expected output now possible in the latest SDK).

For example, using C-style preprocessor notation

#if FLUTTER_SDK >= 0.4.5
// Use new and better methods, use alternatives to deprecated methods, e.g. `utf8.decode`
// or new gradient shading methods that are just getting added.
#else
// Fall back or simply don't provide functionality, or use a method
// that isn't deprecated in older version, e.g. `UTF8.decode`, or using a close match 
// on the gradient or perhaps just skipping the rendering for the unavailable 
// gradient method(s).
#endif

or:

#if IS_DEFINED(sdkClass.newCoolMethod)
// code that uses `sdkClass.newCoolMethod()`
#else
// code that uses some work around, or perhaps does nothing at all
#endif

or, using something like JavaScript:

if (sdkClass.newCoolMethod) { // but no reflection.
  sdkClass.newCoolMethod();
}

Basically, I'd like to avoid needing to maintain multiple versions of my library to support Flutter beta, dev, and/or master channels. I'm OK with users on Beta not getting cutting edge functionality, and I'm even OK with maintaining a build configuration file (or section of pubspec.yaml) to drive this; but I'd like to avoid a very confusing scenario where I have to keep going back and checking if I can migrate functionality to my "beta" tracking package from my "dev" tracking package - just let the compiler include that functionality if it's available in the SDK, and skip it (or use a specified alternative) if it's not.

Although I'm using C-style preprocessing to illustrate here, I'm not looking for a full preprocessor. I'd be very open to other ideas or suggestions that don't involve reflection at runtime.

area-language core-n type-enhancement

Most helpful comment

This is coming up as something that would help Flutter framework implement cross-platform features in ways that are especially challenging today - such as supporting both dart:html and dart:io, or supporting Fuchsia specific things that we don't expect will be available for other platforms.

All 13 comments

some mechanism like this would also be useful for libraries that
come in two flavors: one depending on 'dart:html' and one that does not.

/cc @leafpetersen @lrhn

In the Dart 2 time frame, we will have some (but not all of what you are asking for here). You will be able to conditionally import/export code controlled by a fixed set of platform defined flags. We will not have finer granularity (e.g. platform version) in that time frame (and no current plans for adding it later). Follow https://github.com/dart-lang/sdk/issues/32960 for more details.

So it certainly could be achieved as a preprocessing step before passing to Dart if it's not targetted for the language. My only fear with that is fragmentation (there's the coffeedart preprocessor, and the typedart preprocessor, and the danfielddart preprocessor, etc). That, and the fact that it wouldn't be recognized by the analyzer in any meaningful way.

I do want a feature like this. It enables gradual migration of new, breaking, language features.

BTW: this is the best documentation on conditional imports I could find:

https://medium.com/@dvargahali/dart-2-conditional-imports-update-16147a776aa8

This is coming up as something that would help Flutter framework implement cross-platform features in ways that are especially challenging today - such as supporting both dart:html and dart:io, or supporting Fuchsia specific things that we don't expect will be available for other platforms.

/cc @amirh

After having used conditional imports on a few occasions I find them to be rather clumsy.
Typically I use them to redirect code to a stub implementation, which usually affects many files.

A much cleaner mechanism in my opinion would allow me to specify this redirection in only place.
For example, I would like to say
"for this build target, redirect all uses of dart:html to stubbed_out_html.dart"

If you just want to implement an entire API twice, without forwarding from stubs, you can use conditional exports.

library platform_dependent_html;
export "stubbed_out_html.dart"
    if (dart.library.html) "html.dart";

Then you can import this library, and get real HTML if it's there, or stubbed_out_html.dart if not.

We do not provide a way for a build to replace an existing library that someone else has asked for explicitly.

If you just want to implement an entire API twice, without forwarding from stubs, you can use conditional exports.

I don't think conditional exports are working everywhere currently. #34179

Much needed feature!
And you can find inspiration in Haxe Conditional Compilation it helps us a lot https://haxe.org/manual/lf-condition-compilation.html

I'm definetly missing C# pre-processor features, different build configurations defining own symbols and condition compilation with #if statements

Was this page helpful?
0 / 5 - 0 ratings