-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conditional code preprocessing #33249
Comments
some mechanism like this would also be useful for libraries that |
/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 #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 |
/cc @amirh |
After having used conditional imports on a few occasions I find them to be rather clumsy. A much cleaner mechanism in my opinion would allow me to specify this redirection in only place. |
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 We do not provide a way for a build to replace an existing library that someone else has asked for explicitly. |
I don't think conditional exports are working everywhere currently. #34179 |
Much needed feature! |
I'm definetly missing C# pre-processor features, different build configurations defining own symbols and condition compilation with #if statements |
@lrhn has there been any more thought about this recently? I was thinking about this exact proposal in lieu of having a much cleaner way of doing io/html implementations of let's say a service class (with regards to Flutter). Currently what I have to do to get it working is the following.
With pre-processor features like in C++/C# I can get rid of the stub file, (the factory constructor) and the separate implementations, therefore keeping everything in one class. Something like this
|
I'd like to know if the Dart team is at least considering this, especially for package maintainers, as we can't assume people using our packages will upgrade to the most recent version of Flutter or Dart. A few things I have encountered recently that could be easily solved using conditional compilation, but require workarounds (or worse, maintaining multiple versions of the same library).
|
The more I think about this, the more I think what I'd really like here is API availability capabilities. IOW, it's not that I really want preprocessor defines in Dart, it's that I want to know if some API is available (e.g. whether dart:ui as a whole is avialable, or whether dart:ui has a function called someNewAwesomeThing, or whether object X has a const constructor not, etc.). |
I figured out a way to circumvent this issue. /// This allows a value of type T or T? to be treated as a value of type T?.
///
/// We use this so that APIs that have become non-nullable can still be used
/// with `!` and `?` on the stable branch.
// TODO: Remove this, once Flutter 2 support is dropped
// See https://github.com/flutter/flutter/issues/64830
T? _ambiguate<T>(T? value) => value; Now you can do |
In Haxe language conditional compilation looks like this: class Main {
public static function main() {
#if !debug
trace("ok");
#elseif (debug_level > 3)
trace(3);
#else
trace("debug level too low");
#end
}
} |
But by what mechanics would you act on this knowledge? At the runtime all mentioned pieces can be known just by a generated consts – as Flutter installation comes with Dart sources. |
@ohir - there's no sane way currently to check at runtime whether a particular method (or method signature) is defined, and certain things are checked at compile time (like whether a const constructor is available or not). I'm mainly thinking of how availability macros work in Objective-C on iOS/macOS. There I can do things like have code conditionally work if and only if I'm using a particular SDK version. So for example, as a package author, I want to use some new exciting API that Flutter offers, but I have no way to check whether that API exists today (ignoring doing things like casting to dynamic and trying to invoke a method which is horrible for many reasons). I also don't have a way to have a single package offer better behavior depending on which version of the Flutter/Dart SDKs are in use. E.g. if (DartVersion > xxx) {
// use cool new feature that is faster
} else {
// use slower fallback
} You can come close to getting this by having minimum version requirements in your pubspec, but pub has a hard time solving for strange and overlapping constraints - and it ends up requiring package maintainers to maintain multiple versions of their packages that may not even make it to their users correctly because of pub. |
I asked @munificent about this on Twitter and he mentioned that a general case pre-processor likely isn't coming any time soon, because it requires a lot of the tooling support for things like changing targets (think the target / platform selector on Visual Studio), and since we have I feel like Dart could copy swift's So similar to what Dan is suggesting we could have code like:
Or:
In both cases, the compiler should skip any code that doesn't meet the available criteria. Since this mostly affects library maintainers, and we're likely to use fvm to change versions of Flutter, any support needed in tooling other than the analyzer could lag. I'd be interested to hear what the Dart team thinks... |
Similar functionality likely could be provided for Dart/Flutter own inventory, but for a substantial effort. I have no high hopes this will ever happen (ie. maintaining full lifecycles and supporting them thorough the toolchain).
What about library author testing for all possible permutations (of execution path configurations) that are result of the pub solver coming to a given set of dependencies peculiar to the site of use? You can control, and that just to an extent, only your package's direct dependencies (version-wise).
I understand that you have no way to check whether that API exists at your package user's installation, am I correct? If I am, I know of no "contemporary automagic solution", but I think that 'ol pale |
Introduce `fuchsia.unknown/{Cloneable,Closeable,Queryable}`. These protocols together provide the functionality necessary to back file descriptors. They are similar to COM's `IUnknown`[0]. `fuchsia.unknown.Queryable` is marked transitional as the underlying FIDL features are not yet implemented (see https://fxbug.dev/105608). Simplify and improve the type safety of `fuchsia.io/*2`: - `Node2.Reopen` no longer supports protocol renegotiation. It is now identical to `fuchsia.unknown/Cloneable.Clone` except that the new connection's rights can be specified. Protocols which compose `Node2` do not compose `Cloneable`. - `Node2.OnConnectionInfo` is renamed `OnRepresentation` and now contains only the representation. - `Node2.Describe` is renamed `GetConnectionInfo`. It no longer returns the node's representation, as that can be learned using `fuchsia.unknown/Queryable.Query`. It no longer returns `available_operations`, as that is not used. It no longer accepts a query; rights are always returned. - `File2.Describe` is introduced as a means of acquiring sidecar handles previously provided by `Node2.Describe`. - `Directory2.Open`'s `ConnectionOptions` argument is considerably more principled. See code comments for details. `fuchsia.hardware.pty/Device` no longer composes `fuchsia.io/File`; it now composes `fuchsia.io/File2` and `fuchsia.io/Node1` to avoid undesired composition. `Device.Describe` is added to provide sidecar handles previously provided by `Node2.Describe`. `fuchsia.posix.socket/BaseSocket` now composes `fuchsia.unknown/*` as the new way of representing file descriptors. `fuchsia.io/Node` remains composed to avoid changing ABI. `fuchsia.posix.socket{,.raw,.packet}/*Socket.Describe` are added to provide sidecar handles previously provided by `Node2.Describe`. API history rewriting here is regrettable but necessary. Ideally, all not-yet-implemented APIs would be annotated with @available(added=HEAD) but this breaks downstream compilation of VFS libraries (C++ and Dart). In C++ we can use conditional compilation to avoid emitting code for APIs which we know not to exist at the target API level. On the other hand, Dart has a longstanding lack of support for conditional compilation (see dart-lang/sdk#33249) which effectively precludes the use of @available annotations with APIs for which we vend Dart implementations (https://fxbug.dev/105932). [0] https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nn-unknwn-iunknown Change-Id: I37eacb2d25fcbe37c348b3c0f1e8627dc4337769 Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/696652 Reviewed-by: Adam Barth <[email protected]> Reviewed-by: Chris Suter <[email protected]> API-Review: Chris Suter <[email protected]> Commit-Queue: Tamir Duberstein <[email protected]>
Members! please add the conditional compilation feature to Dart just like C or Apple's Swift. It's important! |
I am also missing C# pre-processor directives. Hope Dart will have this feature soon as well. |
We're missing this over here as well: This is a bit downstream, but it would be quite nice to have the flutter versions eventually stored off as compilation consts: #if flutter_3_3_1_or_newer
//
#elif flutter_3_3_0
//
#else
//
#endif So it would be cool if somehow that usecase was enabled (not just dart version). |
It would be nice to have
|
If all uses are hidden behind Do you have an example? |
Yes, exactly this is my problem. Please see the backgrounds for my complaint here: Baseflow/flutter-geolocator#1296 (comment) The package in question that causes this problem though is I only ever need the package in debug mode, so I assumed defining it in Apparently, the package causes a lot of problems in the Android release build that are massive and critical, but I still rely on the package during development, it makes things so much easier for me, because there are a lot of things that can only be tested on a real device. Just for the record though: I switched to Thanks. |
Why will native I see two important-to-fix bugs here, as there is no easy way for developers to work on development features and have the possibility to easily deactivate these features for release builds. |
I also need this to distinguish between different flutter sdk versions. Is there any update here? |
No update that I'm aware of. |
@munificent Is this something macros could be hacked a bit to do? For example, could you write: @dart_minimum('2.17')
@override
set connectionFactory(
Future<ConnectionTask<Socket>> Function(
Uri url, String? proxyHost, int? proxyPort)?
f) =>
innerClient.connectionFactory = f; And have the macro read current Dart version and remove the attributed code block for versions under 2.17? Or are macros only additive? This is not ideal (It'd still love a |
Macros are mainly additive, but they can bypass existing code entirely by not calling it. augment set connectionFactory(Future<ConnectionTask<Socket>> Function(
Uri url, String? proxyHost, int? proxyPort)?
f) {} or maybe a body of The function won't stop existing, but it will stop doing anything. A bigger questions is whether the macro has access to the language version (but I guess you could just have one version of the macro package per SDK release, say one with an SDK constraint of |
Not really. By design, macros don't have access to any configuration information. If they did, then the compilers would have to be very careful to run macros separately for each possible configuration and track their outputs separately. It would definitely be useful of macros could do config-specific stuff, but it has a lot of implications for developer experience, complexity, and compile time performance, so at least for now we are avoiding it by not giving macros access to that information. cc @jakemac53 |
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:
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
or:
or, using something like JavaScript:
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.
The text was updated successfully, but these errors were encountered: