Skip to content

Commit e9f14eb

Browse files
committed
[flutter_hook_config] Add the flutter_hook_config package
A package:hooks protocol extension that lets the Flutter SDK expose engine host-tool paths (impellerc, libtessellator) to build and link hooks in a typed way, replacing the per-package SDK-cache-walking that packages like flutter_gpu_shaders do today and making the --local-engine override propagate to hooks for free. Towards flutter/flutter#186299
1 parent 2ec2236 commit e9f14eb

13 files changed

Lines changed: 396 additions & 0 deletions

File tree

.github/labeler.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@
3838
- any-glob-to-any-file:
3939
- packages/file_selector/**/*
4040

41+
'p: flutter_hook_config':
42+
- changed-files:
43+
- any-glob-to-any-file:
44+
- packages/flutter_hook_config/**/*
45+
4146
'p: flutter_lints':
4247
- changed-files:
4348
- any-glob-to-any-file:

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ These are the packages hosted in this repository:
4444
| [espresso](./packages/espresso/) | [![pub package](https://img.shields.io/pub/v/espresso.svg)](https://pub.dev/packages/espresso) | [![pub points](https://img.shields.io/pub/points/espresso)](https://pub.dev/packages/espresso/score) | [![downloads](https://img.shields.io/pub/dm/espresso)](https://pub.dev/packages/espresso/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20espresso?label=)](https://github.com/flutter/flutter/labels/p%3A%20espresso) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20espresso?label=)](https://github.com/flutter/packages/labels/p%3A%20espresso) |
4545
| [extension\_google\_sign\_in\_as\_googleapis\_auth](./packages/extension_google_sign_in_as_googleapis_auth/) | [![pub package](https://img.shields.io/pub/v/extension_google_sign_in_as_googleapis_auth.svg)](https://pub.dev/packages/extension_google_sign_in_as_googleapis_auth) | [![pub points](https://img.shields.io/pub/points/extension_google_sign_in_as_googleapis_auth)](https://pub.dev/packages/extension_google_sign_in_as_googleapis_auth/score) | [![downloads](https://img.shields.io/pub/dm/extension_google_sign_in_as_googleapis_auth)](https://pub.dev/packages/extension_google_sign_in_as_googleapis_auth/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20extension_google_sign_in_as_googleapis_auth?label=)](https://github.com/flutter/flutter/labels/p%3A%20extension_google_sign_in_as_googleapis_auth) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20extension_google_sign_in_as_googleapis_auth?label=)](https://github.com/flutter/packages/labels/p%3A%20extension_google_sign_in_as_googleapis_auth) |
4646
| [file\_selector](./packages/file_selector/) | [![pub package](https://img.shields.io/pub/v/file_selector.svg)](https://pub.dev/packages/file_selector) | [![pub points](https://img.shields.io/pub/points/file_selector)](https://pub.dev/packages/file_selector/score) | [![downloads](https://img.shields.io/pub/dm/file_selector)](https://pub.dev/packages/file_selector/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20file_selector?label=)](https://github.com/flutter/flutter/labels/p%3A%20file_selector) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20file_selector?label=)](https://github.com/flutter/packages/labels/p%3A%20file_selector) |
47+
| [flutter\_hook\_config](./packages/flutter_hook_config/) | [![pub package](https://img.shields.io/pub/v/flutter_hook_config.svg)](https://pub.dev/packages/flutter_hook_config) | [![pub points](https://img.shields.io/pub/points/flutter_hook_config)](https://pub.dev/packages/flutter_hook_config/score) | [![downloads](https://img.shields.io/pub/dm/flutter_hook_config)](https://pub.dev/packages/flutter_hook_config/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20flutter_hook_config?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_hook_config) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20flutter_hook_config?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_hook_config) |
4748
| [flutter\_lints](./packages/flutter_lints/) | [![pub package](https://img.shields.io/pub/v/flutter_lints.svg)](https://pub.dev/packages/flutter_lints) | [![pub points](https://img.shields.io/pub/points/flutter_lints)](https://pub.dev/packages/flutter_lints/score) | [![downloads](https://img.shields.io/pub/dm/flutter_lints)](https://pub.dev/packages/flutter_lints/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20flutter_lints?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_lints) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20flutter_lints?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_lints) |
4849
| [flutter\_plugin\_android\_lifecycle](./packages/flutter_plugin_android_lifecycle/) | [![pub package](https://img.shields.io/pub/v/flutter_plugin_android_lifecycle.svg)](https://pub.dev/packages/flutter_plugin_android_lifecycle) | [![pub points](https://img.shields.io/pub/points/flutter_plugin_android_lifecycle)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![downloads](https://img.shields.io/pub/dm/flutter_plugin_android_lifecycle)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20flutter_plugin_android_lifecycle?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_plugin_android_lifecycle) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20flutter_plugin_android_lifecycle?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_plugin_android_lifecycle) |
4950
| [flutter\_svg](./third_party/packages/flutter_svg/) | [![pub package](https://img.shields.io/pub/v/flutter_svg.svg)](https://pub.dev/packages/flutter_svg) | [![pub points](https://img.shields.io/pub/points/flutter_svg)](https://pub.dev/packages/flutter_svg/score) | [![downloads](https://img.shields.io/pub/dm/flutter_svg)](https://pub.dev/packages/flutter_svg/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20flutter_svg?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_svg) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20flutter_svg?label=)](https://github.com/flutter/packages/labels/p%3A%20flutter_svg) |

SUGGESTED_REVIEWERS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ reviewed by someone else.
3838
- **Web**: @mdebbar
3939
- **Windows**: @stuartmorgan-g
4040

41+
`flutter_hook_config`:
42+
- @bdero
43+
- @dcharkes
44+
4145
`flutter_lints`:
4246
- @chunhtai
4347

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
2+
/pubspec.lock
3+
**/doc/api/
4+
.dart_tool/
5+
.packages
6+
build/
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Below is a list of people and organizations that have contributed
2+
# to the Flutter project. Names should be added to the list like so:
3+
#
4+
# Name/Organization <email address>
5+
6+
Google Inc.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
## 0.1.0
2+
3+
* Initial release of `flutter_hook_config`: a `package:hooks` protocol extension
4+
that lets the Flutter SDK expose engine host-tool paths (`impellerc`,
5+
`libtessellator`) to build and link hooks, including the `--local-engine`
6+
override.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Copyright 2013 The Flutter Authors
2+
3+
Redistribution and use in source and binary forms, with or without modification,
4+
are permitted provided that the following conditions are met:
5+
6+
* Redistributions of source code must retain the above copyright
7+
notice, this list of conditions and the following disclaimer.
8+
* Redistributions in binary form must reproduce the above
9+
copyright notice, this list of conditions and the following
10+
disclaimer in the documentation and/or other materials provided
11+
with the distribution.
12+
* Neither the name of Google Inc. nor the names of its
13+
contributors may be used to endorse or promote products derived
14+
from this software without specific prior written permission.
15+
16+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# flutter_hook_config
2+
3+
A [`package:hooks`](https://pub.dev/packages/hooks) protocol extension that lets
4+
the Flutter SDK pass Flutter-specific configuration to build and link hooks in a
5+
typed way.
6+
7+
Today it exposes the absolute paths of the engine host tools a hook might need:
8+
9+
* `impellerc` — the offline shader compiler (used, for example, by
10+
`flutter_gpu_shaders` to compile `.shaderbundle.json` files).
11+
* `libtessellator` — the tessellation library.
12+
13+
The Flutter SDK (`flutter_tools`) resolves these through the same artifact
14+
lookup it uses for its own build steps, so under `--local-engine` a hook gets
15+
the locally built tools, matching the engine the app is being built against,
16+
instead of the ones in the SDK cache.
17+
18+
## For hook authors
19+
20+
Add `flutter_hook_config` to your hook's dependencies and read the config from
21+
the `BuildInput` / `LinkInput`. Always check `config.buildForFlutter` first: a
22+
hook may also be invoked by a plain `dart` build, or by a Flutter SDK that
23+
predates this extension, in which case no Flutter-specific config is available
24+
and you should fall back to your own tool discovery.
25+
26+
```dart
27+
import 'package:flutter_hook_config/flutter_hook_config.dart';
28+
import 'package:hooks/hooks.dart';
29+
30+
void main(List<String> args) async {
31+
await build(args, (input, output) async {
32+
final Uri impellerc;
33+
if (input.config.buildForFlutter) {
34+
impellerc = input.config.flutter.impellerc;
35+
} else {
36+
impellerc = await locateImpellercSomeOtherWay();
37+
}
38+
// ... invoke impellerc ...
39+
});
40+
}
41+
```
42+
43+
## For the Flutter SDK
44+
45+
`flutter_tools` constructs a `FlutterExtension` and passes it to the hook runner
46+
alongside the other protocol extensions (`CodeAssetExtension`,
47+
`DataAssetsExtension`):
48+
49+
```dart
50+
FlutterExtension(
51+
impellerc: artifacts.getHostArtifact(HostArtifact.impellerc).absolute.uri,
52+
libtessellator: artifacts.getHostArtifact(HostArtifact.libtessellator).absolute.uri,
53+
)
54+
```
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2013 The Flutter Authors
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
/// A `package:hooks` protocol extension that exposes Flutter engine host-tool
6+
/// paths (such as `impellerc` and `libtessellator`) to build and link hooks.
7+
///
8+
/// Hook authors read the injected configuration via [HookConfigFlutterConfig],
9+
/// checking [HookConfigFlutterConfig.buildForFlutter] first:
10+
///
11+
/// ```dart
12+
/// import 'package:flutter_hook_config/flutter_hook_config.dart';
13+
/// import 'package:hooks/hooks.dart';
14+
///
15+
/// void main(List<String> args) async {
16+
/// await build(args, (input, output) async {
17+
/// if (input.config.buildForFlutter) {
18+
/// final impellerc = input.config.flutter.impellerc;
19+
/// // ... invoke impellerc ...
20+
/// }
21+
/// });
22+
/// }
23+
/// ```
24+
///
25+
/// The Flutter SDK (`flutter_tools`) populates the configuration by passing a
26+
/// [FlutterExtension] to the hook runner.
27+
library;
28+
29+
export 'src/config.dart'
30+
show FlutterConfig, HookConfigBuilderFlutterConfig, HookConfigFlutterConfig;
31+
export 'src/extension.dart' show FlutterExtension;
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright 2013 The Flutter Authors
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:hooks/hooks.dart';
6+
7+
/// The key under which Flutter-specific configuration is stored inside the hook
8+
/// config's `extensions` map.
9+
const String _flutterExtensionKey = 'flutter';
10+
11+
const String _impellercKey = 'impellerc';
12+
const String _libtessellatorKey = 'libtessellator';
13+
14+
/// Flutter-specific configuration supplied to a build or link hook.
15+
///
16+
/// Obtained via [HookConfigFlutterConfig.flutter]. Only available when the hook
17+
/// is invoked by a Flutter SDK that supports this extension; check
18+
/// [HookConfigFlutterConfig.buildForFlutter] before accessing it.
19+
final class FlutterConfig {
20+
FlutterConfig._(this._json);
21+
22+
final Map<String, Object?> _json;
23+
24+
/// The absolute path to the `impellerc` offline shader compiler that ships
25+
/// with the engine artifacts of the invoking Flutter SDK.
26+
///
27+
/// When the SDK is invoked with `--local-engine`, this points at the locally
28+
/// built `impellerc`, matching the engine the app is being built against.
29+
Uri get impellerc => _filePath(_impellercKey);
30+
31+
/// The absolute path to the `libtessellator` dynamic library that ships with
32+
/// the engine artifacts of the invoking Flutter SDK.
33+
///
34+
/// When the SDK is invoked with `--local-engine`, this points at the locally
35+
/// built `libtessellator`.
36+
Uri get libtessellator => _filePath(_libtessellatorKey);
37+
38+
Uri _filePath(String key) {
39+
final Object? value = _json[key];
40+
if (value is! String) {
41+
throw FormatException(
42+
"Expected a String at 'config.extensions.$_flutterExtensionKey.$key' "
43+
'in the hook input, got: $value',
44+
);
45+
}
46+
return Uri.file(value);
47+
}
48+
}
49+
50+
/// Extension on [HookConfig] providing access to Flutter-specific
51+
/// configuration.
52+
extension HookConfigFlutterConfig on HookConfig {
53+
/// Whether this hook is being invoked as part of a Flutter build.
54+
///
55+
/// When `false`, the hook is being invoked by a plain `dart` build, or by a
56+
/// Flutter SDK that predates the `flutter_hook_config` extension, and
57+
/// [flutter] must not be accessed. Hooks that need Flutter-supplied
58+
/// configuration should fall back to their own discovery logic in that case.
59+
bool get buildForFlutter => _flutterExtensionJson != null;
60+
61+
/// The Flutter-specific configuration for this hook invocation.
62+
///
63+
/// Only valid when [buildForFlutter] is `true`; throws a [StateError]
64+
/// otherwise.
65+
FlutterConfig get flutter {
66+
final Map<String, Object?>? extensionJson = _flutterExtensionJson;
67+
if (extensionJson == null) {
68+
throw StateError(
69+
'No Flutter-specific hook configuration is available. The hook is not '
70+
'being invoked by a Flutter SDK, or the Flutter SDK predates '
71+
'flutter_hook_config support. Check `config.buildForFlutter` first.',
72+
);
73+
}
74+
return FlutterConfig._(extensionJson);
75+
}
76+
77+
Map<String, Object?>? get _flutterExtensionJson {
78+
final Object? extensions = json['extensions'];
79+
if (extensions is! Map<String, Object?>) {
80+
return null;
81+
}
82+
final Object? flutterJson = extensions[_flutterExtensionKey];
83+
return flutterJson is Map<String, Object?> ? flutterJson : null;
84+
}
85+
}
86+
87+
/// Extension on [HookConfigBuilder] for writing Flutter-specific configuration
88+
/// onto a hook input.
89+
///
90+
/// This is intended for use by the Flutter SDK (`flutter_tools`) via
91+
/// [FlutterExtension]; ordinary hook authors do not call this directly.
92+
extension HookConfigBuilderFlutterConfig on HookConfigBuilder {
93+
/// Records the Flutter-specific configuration on a build or link hook input.
94+
///
95+
/// [impellerc] and [libtessellator] must be absolute file URIs.
96+
void setupFlutter({required Uri impellerc, required Uri libtessellator}) {
97+
assert(impellerc.isAbsolute, 'impellerc must be an absolute URI');
98+
assert(libtessellator.isAbsolute, 'libtessellator must be an absolute URI');
99+
final extensions =
100+
(json['extensions'] ??= <String, Object?>{}) as Map<String, Object?>;
101+
extensions[_flutterExtensionKey] = <String, Object?>{
102+
_impellercKey: impellerc.toFilePath(),
103+
_libtessellatorKey: libtessellator.toFilePath(),
104+
};
105+
}
106+
}

0 commit comments

Comments
 (0)