diff --git a/dev/devicelab/bin/tasks/codegen_integration_test.dart b/dev/devicelab/bin/tasks/codegen_integration_test.dart new file mode 100644 index 00000000000..2074b422d4e --- /dev/null +++ b/dev/devicelab/bin/tasks/codegen_integration_test.dart @@ -0,0 +1,20 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:io'; + +import 'package:path/path.dart' as path; + +import 'package:flutter_devicelab/framework/adb.dart'; +import 'package:flutter_devicelab/framework/framework.dart'; +import 'package:flutter_devicelab/framework/utils.dart'; +import 'package:flutter_devicelab/tasks/integration_tests.dart'; + +final Directory codegenAppPath = dir(path.join(flutterDirectory.path, 'dev/integration_tests/codegen')); + +Future main() async { + deviceOperatingSystem = DeviceOperatingSystem.android; + await task(createCodegenerationIntegrationTest()); +} diff --git a/dev/devicelab/lib/tasks/integration_tests.dart b/dev/devicelab/lib/tasks/integration_tests.dart index e38f4a22845..334c2a06f13 100644 --- a/dev/devicelab/lib/tasks/integration_tests.dart +++ b/dev/devicelab/lib/tasks/integration_tests.dart @@ -61,6 +61,16 @@ TaskFunction createAndroidSemanticsIntegrationTest() { ); } +TaskFunction createCodegenerationIntegrationTest() { + return DriverTest( + '${flutterDirectory.path}/dev/integration_tests/codegen', + 'lib/main.dart', + environment: { + 'FLUTTER_EXPERIMENTAL_BUILD': 'true' + }, + ); +} + TaskFunction createFlutterCreateOfflineTest() { return () async { final Directory tempDir = Directory.systemTemp.createTempSync('flutter_create_test.'); @@ -83,12 +93,14 @@ class DriverTest { this.testDirectory, this.testTarget, { this.extraOptions = const [], + this.environment = const {}, } ); final String testDirectory; final String testTarget; final List extraOptions; + final Map environment; Future call() { return inDirectory(testDirectory, () async { @@ -107,7 +119,7 @@ class DriverTest { deviceId, ]; options.addAll(extraOptions); - await flutter('drive', options: options); + await flutter('drive', options: options, environment: Map.from(environment)); return TaskResult.success(null); }); diff --git a/dev/devicelab/manifest.yaml b/dev/devicelab/manifest.yaml index 861d0637a98..0d89a031735 100644 --- a/dev/devicelab/manifest.yaml +++ b/dev/devicelab/manifest.yaml @@ -76,6 +76,14 @@ tasks: stage: devicelab_win required_agent_capabilities: ["windows/android"] + codegen_integration_test: + description: > + Runs codegeneration and verifies that it can execute + correctly. + stage: devicelab_win + required_agent_capabilities: ["windows/android"] + flaky: true + flutter_gallery_android__compile: description: > Collects various performance metrics of compiling the Flutter diff --git a/dev/integration_tests/codegen/README.md b/dev/integration_tests/codegen/README.md new file mode 100644 index 00000000000..494dc2bbccb --- /dev/null +++ b/dev/integration_tests/codegen/README.md @@ -0,0 +1,3 @@ +# codegen + +A Flutter project for testing code generation. diff --git a/dev/integration_tests/codegen/android/.project b/dev/integration_tests/codegen/android/.project new file mode 100644 index 00000000000..8b6e7b429ff --- /dev/null +++ b/dev/integration_tests/codegen/android/.project @@ -0,0 +1,17 @@ + + + android_____ + Project android_____ created by Buildship. + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.buildship.core.gradleprojectnature + + diff --git a/dev/integration_tests/codegen/android/.settings/org.eclipse.buildship.core.prefs b/dev/integration_tests/codegen/android/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 00000000000..e8895216fd3 --- /dev/null +++ b/dev/integration_tests/codegen/android/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,2 @@ +connection.project.dir= +eclipse.preferences.version=1 diff --git a/dev/integration_tests/codegen/android/app/build.gradle b/dev/integration_tests/codegen/android/app/build.gradle new file mode 100644 index 00000000000..9ebebef363b --- /dev/null +++ b/dev/integration_tests/codegen/android/app/build.gradle @@ -0,0 +1,57 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withInputStream { stream -> + localProperties.load(stream) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 28 + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 28 + versionCode 1 + versionName "0.0.1" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } + + aaptOptions { + // TODO(goderbauer): remove when https://github.com/flutter/flutter/issues/8986 is resolved. + if(System.getenv("FLUTTER_CI_WIN")) { + println "AAPT cruncher disabled when running on Win CI." + cruncherEnabled false + } + } +} + +flutter { + source '../..' +} + +dependencies { + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' +} diff --git a/dev/integration_tests/codegen/android/app/src/main/AndroidManifest.xml b/dev/integration_tests/codegen/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..67036414330 --- /dev/null +++ b/dev/integration_tests/codegen/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + diff --git a/dev/integration_tests/codegen/android/app/src/main/java/com/yourcompany/platforminteraction/MainActivity.java b/dev/integration_tests/codegen/android/app/src/main/java/com/yourcompany/platforminteraction/MainActivity.java new file mode 100644 index 00000000000..73b7e51bb9b --- /dev/null +++ b/dev/integration_tests/codegen/android/app/src/main/java/com/yourcompany/platforminteraction/MainActivity.java @@ -0,0 +1,17 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.yourcompany.platforminteraction; + +import android.os.Bundle; +import io.flutter.app.FlutterActivity; +import io.flutter.plugins.GeneratedPluginRegistrant; + +public class MainActivity extends FlutterActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + GeneratedPluginRegistrant.registerWith(this); + } +} diff --git a/dev/integration_tests/codegen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/dev/integration_tests/codegen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000000..db77bb4b7b0 Binary files /dev/null and b/dev/integration_tests/codegen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/dev/integration_tests/codegen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/dev/integration_tests/codegen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000000..17987b79bb8 Binary files /dev/null and b/dev/integration_tests/codegen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/dev/integration_tests/codegen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/dev/integration_tests/codegen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000000..09d4391482b Binary files /dev/null and b/dev/integration_tests/codegen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/dev/integration_tests/codegen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/dev/integration_tests/codegen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000000..d5f1c8d34e7 Binary files /dev/null and b/dev/integration_tests/codegen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/dev/integration_tests/codegen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/dev/integration_tests/codegen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000000..4d6372eebdb Binary files /dev/null and b/dev/integration_tests/codegen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/dev/integration_tests/codegen/android/build.gradle b/dev/integration_tests/codegen/android/build.gradle new file mode 100644 index 00000000000..bb8a303898c --- /dev/null +++ b/dev/integration_tests/codegen/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/dev/integration_tests/codegen/android/gradle.properties b/dev/integration_tests/codegen/android/gradle.properties new file mode 100644 index 00000000000..8bd86f68051 --- /dev/null +++ b/dev/integration_tests/codegen/android/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx1536M diff --git a/dev/integration_tests/codegen/android/gradle/wrapper/gradle-wrapper.properties b/dev/integration_tests/codegen/android/gradle/wrapper/gradle-wrapper.properties new file mode 100755 index 00000000000..2819f022f1f --- /dev/null +++ b/dev/integration_tests/codegen/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip diff --git a/dev/integration_tests/codegen/android/settings.gradle b/dev/integration_tests/codegen/android/settings.gradle new file mode 100644 index 00000000000..115da6cb4f4 --- /dev/null +++ b/dev/integration_tests/codegen/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withInputStream { stream -> plugins.load(stream) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/dev/integration_tests/codegen/lib/coffee_app.dart b/dev/integration_tests/codegen/lib/coffee_app.dart new file mode 100644 index 00000000000..daa5b4225f3 --- /dev/null +++ b/dev/integration_tests/codegen/lib/coffee_app.dart @@ -0,0 +1,58 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:inject/inject.dart'; + +// This is a compile-time generated file and does not exist in source. +import 'coffee_app.inject.dart' as generated; // ignore: uri_does_not_exist +import 'src/coffee.dart'; + +@module +class PourOverCoffeeModule { + @provide + @brandName + String provideBrand() => 'Coffee by Flutter Inc.'; + + @provide + @modelName + String provideModel() => 'PourOverSupremeFiesta'; + + @provide + @asynchronous + Future provideHeater() async => Stove(); + + @provide + Pump providePump(Heater heater) => NoOpPump(); +} + +class NoOpPump extends Pump { + @override + void pump() { + print('nothing to pump...'); + } +} + +class Stove extends Heater { + @override + bool get isHot => _isHot; + bool _isHot = false; + + @override + void off() { + _isHot = true; + } + + @override + void on() { + _isHot = true; + } +} + +@Injector([PourOverCoffeeModule]) +abstract class CoffeeApp { + static final Future Function(PourOverCoffeeModule) create = generated.CoffeeApp$Injector.create; + + @provide + CoffeeMaker getCoffeeMaker(); +} diff --git a/dev/integration_tests/codegen/lib/main.dart b/dev/integration_tests/codegen/lib/main.dart new file mode 100644 index 00000000000..b136fba2201 --- /dev/null +++ b/dev/integration_tests/codegen/lib/main.dart @@ -0,0 +1,49 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter_driver/driver_extension.dart'; + +import 'coffee_app.dart'; +import 'src/coffee.dart'; + +Future main() async { + enableFlutterDriverExtension(); + coffeeApp = await CoffeeApp.create(PourOverCoffeeModule()); + runApp(ExampleWidget()); +} + +CoffeeApp coffeeApp; + +class ExampleWidget extends StatefulWidget { + @override + _ExampleWidgetState createState() => _ExampleWidgetState(); +} + +class _ExampleWidgetState extends State { + String _message = ''; + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + body: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + RaisedButton( + child: const Text('Press Button, Get Coffee'), + onPressed: () async { + final CoffeeMaker coffeeMaker = coffeeApp.getCoffeeMaker(); + setState(() { + _message = coffeeMaker.brew(); + }); + }, + ), + Text(_message), + ], + ), + ), + ); + } +} diff --git a/dev/integration_tests/codegen/lib/src/coffee.dart b/dev/integration_tests/codegen/lib/src/coffee.dart new file mode 100644 index 00000000000..5b33d77ce03 --- /dev/null +++ b/dev/integration_tests/codegen/lib/src/coffee.dart @@ -0,0 +1,41 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:inject/inject.dart'; + +const Qualifier brandName = Qualifier(#brandName); +const Qualifier modelName = Qualifier(#modelName); + +class CoffeeMaker { + @provide + CoffeeMaker(this._heater, this._pump, this._brand, this._model); + + final Heater _heater; + final Pump _pump; + + @modelName + final String _model; + + @brandName + final String _brand; + + String brew() { + _heater.on(); + _pump.pump(); + print(' [_]P coffee! [_]P'); + final String message = 'Thanks for using $_model by $_brand'; + _heater.off(); + return message; + } +} + +abstract class Heater { + void on(); + void off(); + bool get isHot; +} + +abstract class Pump { + void pump(); +} diff --git a/dev/integration_tests/codegen/pubspec.yaml b/dev/integration_tests/codegen/pubspec.yaml new file mode 100644 index 00000000000..c45fed8a73d --- /dev/null +++ b/dev/integration_tests/codegen/pubspec.yaml @@ -0,0 +1,88 @@ +name: codegen +description: A test of Flutter integrating code generation. + +environment: + # The pub client defaults to an <2.0.0 sdk constraint which we need to explicitly overwrite. + sdk: ">=2.0.0-dev.68.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + flutter_driver: + sdk: flutter + # TODO(jonahwilliams): replace with pub version when everything is compatible + inject: + git: + url: https://github.com/jonahwilliams/inject.dart + path: package/inject + + async: 2.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + collection: 1.14.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + crypto: 2.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + file: 5.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + intl: 0.15.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + json_rpc_2: 2.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + meta: 1.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.6.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + pub_semver: 1.4.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + source_span: 1.5.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + stack_trace: 1.9.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + stream_channel: 1.6.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + term_glyph: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + typed_data: 1.1.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + vector_math: 2.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + vm_service_client: 0.2.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + web_socket_channel: 1.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + +dev_dependencies: + test: 1.5.3 + + analyzer: 0.35.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + args: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + csslib: 0.14.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + front_end: 0.1.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + glob: 1.1.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + html: 0.13.3+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + http: 0.12.0+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + http_multi_server: 2.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + http_parser: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + io: 0.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + js: 0.6.1+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + kernel: 0.3.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + logging: 0.11.3+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + matcher: 0.12.3+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 0.9.6+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + multi_server_socket: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + node_preamble: 1.4.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + package_config: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + package_resolver: 1.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + pedantic: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + plugin: 0.2.0+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + pool: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + shelf: 0.7.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + shelf_packages_handler: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + shelf_static: 0.2.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + shelf_web_socket: 0.2.2+4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + source_map_stack_trace: 1.1.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + source_maps: 0.10.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + string_scanner: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + test_api: 0.2.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + test_core: 0.2.1+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + utf: 0.9.0+5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + watcher: 0.9.7+10 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + yaml: 2.1.15 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + +builders: + # TODO(jonahwilliams): replace with pub version when everything is compatible + inject_generator: + git: + url: https://github.com/jonahwilliams/inject.dart + path: package/inject_generator + +flutter: + uses-material-design: true + +# PUBSPEC CHECKSUM: c26c diff --git a/dev/integration_tests/codegen/test_driver/main_test.dart b/dev/integration_tests/codegen/test_driver/main_test.dart new file mode 100644 index 00000000000..33824ab46ab --- /dev/null +++ b/dev/integration_tests/codegen/test_driver/main_test.dart @@ -0,0 +1,23 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_driver/flutter_driver.dart'; +import 'package:test/test.dart' hide TypeMatcher, isInstanceOf; + +void main() { + FlutterDriver driver; + + setUpAll(() async { + driver = await FlutterDriver.connect(); + }); + + test('Can execute generated code', () async { + const String button = 'Press Button, Get Coffee'; + await driver.tap(find.text(button)); + + const String message = 'Thanks for using PourOverSupremeFiesta by Coffee by Flutter Inc.'; + final String fullMessage = await driver.getText(find.text(message)); + expect(fullMessage, message); + }); +} \ No newline at end of file diff --git a/packages/flutter_build/lib/src/kernel_builder.dart b/packages/flutter_build/lib/src/kernel_builder.dart index 65f873dd335..447848817a5 100644 --- a/packages/flutter_build/lib/src/kernel_builder.dart +++ b/packages/flutter_build/lib/src/kernel_builder.dart @@ -84,6 +84,11 @@ class FlutterKernelBuilder implements Builder { @override Future build(BuildStep buildStep) async { + // Do not resolve dependencies if this does not correspond to the main + // entrypoint. + if (!mainPath.contains(buildStep.inputId.path)) { + return; + } final AssetId outputId = buildStep.inputId.changeExtension(_kFlutterDillOutputExtension); final AssetId packagesOutputId = buildStep.inputId.changeExtension(_kPackagesExtension); @@ -97,9 +102,8 @@ class FlutterKernelBuilder implements Builder { return; } - // Do not generate kernel if it has been disabled or if this asset does not - // correspond to the current entrypoint. - if (disabled || !mainPath.contains(buildStep.inputId.path)) { + // Do not generate kernel if it has been disabled. + if (disabled) { return; } @@ -118,8 +122,14 @@ class FlutterKernelBuilder implements Builder { // Note: currently we only replace the root package with a multiroot // scheme. To support codegen on arbitrary packages we will need to do // this for each dependency. - final String newPackagesContents = oldPackagesContents.replaceFirst('$packageName:lib/', '$packageName:$multiRootScheme:///lib/'); + final String newPackagesContents = oldPackagesContents.replaceFirst('$packageName:lib/', '$packageName:$multiRootScheme:/'); await packagesFile.writeAsString(newPackagesContents); + String absoluteMainPath; + if (path.isAbsolute(mainPath)) { + absoluteMainPath = mainPath; + } else { + absoluteMainPath = path.join(projectDir.absolute.path, mainPath); + } // start up the frontend server with configuration. final List arguments = [ @@ -145,14 +155,15 @@ class FlutterKernelBuilder implements Builder { if (incrementalCompilerByteStorePath != null) { arguments.add('--incremental'); } - final String generatedRoot = path.join(projectDir.absolute.path, '.dart_tool', 'build', 'generated', '$packageName'); + final String generatedRoot = path.join(projectDir.absolute.path, '.dart_tool', 'build', 'generated', '$packageName', 'lib'); + final String normalRoot = path.join(projectDir.absolute.path, 'lib'); arguments.addAll([ '--packages', packagesFile.path, '--output-dill', outputFile.path, '--filesystem-root', - projectDir.absolute.path, + normalRoot, '--filesystem-root', generatedRoot, '--filesystem-scheme', @@ -162,12 +173,12 @@ class FlutterKernelBuilder implements Builder { arguments.addAll(extraFrontEndOptions); } final Uri mainUri = _PackageUriMapper.findUri( - mainPath, + absoluteMainPath, packagesFile.path, multiRootScheme, - [projectDir.absolute.path, generatedRoot], + [normalRoot, generatedRoot], ); - arguments.add(mainUri.toString()); + arguments.add(mainUri?.toString() ?? absoluteMainPath); // Invoke the frontend server and copy the dill back to the output // directory. try { @@ -202,7 +213,6 @@ class _StdoutHandler { bool _suppressCompilerMessages; void handler(String message) { - log.info(message); const String kResultPrefix = 'result '; if (boundaryKey == null) { if (message.startsWith(kResultPrefix)) @@ -256,7 +266,7 @@ class _PackageUriMapper { if (fileSystemScheme != null && fileSystemRoots != null && prefix.contains(fileSystemScheme)) { _packageName = packageName; _uriPrefixes = fileSystemRoots - .map((String name) => Uri.file('$name/lib/', windows: Platform.isWindows).toString()) + .map((String name) => Uri.file(name, windows: Platform.isWindows).toString()) .toList(); return; } diff --git a/packages/flutter_build/pubspec.yaml b/packages/flutter_build/pubspec.yaml index eb88dadab89..7996be6ebdb 100644 --- a/packages/flutter_build/pubspec.yaml +++ b/packages/flutter_build/pubspec.yaml @@ -7,7 +7,7 @@ environment: dependencies: # To update these, use "flutter update-packages --force-upgrade". build: 1.1.1 - build_modules: 1.0.7 + build_modules: 1.0.7+2 package_config: 1.0.5 path: 1.6.2 @@ -50,4 +50,4 @@ dartdoc: # Exclude this package from the hosted API docs. nodoc: true -# PUBSPEC CHECKSUM: 0361 +# PUBSPEC CHECKSUM: dcbe diff --git a/packages/flutter_tools/BUILD.gn b/packages/flutter_tools/BUILD.gn index 1838e49eccc..6ba47615f11 100644 --- a/packages/flutter_tools/BUILD.gn +++ b/packages/flutter_tools/BUILD.gn @@ -18,6 +18,7 @@ dart_library("flutter_tools") { "//third_party/dart/third_party/pkg/linter", "//third_party/dart-pkg/pub/archive", "//third_party/dart-pkg/pub/args", + "//third_party/dart-pkg/pub/build_daemon", "//third_party/dart-pkg/pub/build_runner_core", "//third_party/dart-pkg/pub/collection", "//third_party/dart-pkg/pub/completion", diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart index 160cfc3219f..1415fa5f2c1 100644 --- a/packages/flutter_tools/lib/executable.dart +++ b/packages/flutter_tools/lib/executable.dart @@ -5,6 +5,12 @@ import 'dart:async'; import 'runner.dart' as runner; +import 'src/base/context.dart'; +// The build_runner code generation is provided here to make it easier to +// avoid introducing the dependency into google3. Not all build* packages +// are synced internally. +import 'src/build_runner/build_runner.dart'; +import 'src/codegen.dart'; import 'src/commands/analyze.dart'; import 'src/commands/attach.dart'; import 'src/commands/build.dart'; @@ -18,6 +24,7 @@ import 'src/commands/doctor.dart'; import 'src/commands/drive.dart'; import 'src/commands/emulators.dart'; import 'src/commands/format.dart'; +import 'src/commands/generate.dart'; import 'src/commands/ide_config.dart'; import 'src/commands/inject_plugins.dart'; import 'src/commands/install.dart'; @@ -63,6 +70,7 @@ Future main(List args) async { DriveCommand(), EmulatorsCommand(), FormatCommand(), + GenerateCommand(), IdeConfigCommand(hidden: !verboseHelp), InjectPluginsCommand(hidden: !verboseHelp), InstallCommand(), @@ -81,5 +89,10 @@ Future main(List args) async { VersionCommand(), ], verbose: verbose, muteCommandLogging: muteCommandLogging, - verboseHelp: verboseHelp); + verboseHelp: verboseHelp, + overrides: { + // The build runner instance is not supported in google3 because + // the build runner packages are not synced internally. + CodeGenerator: () => experimentalBuildEnabled ? const BuildRunner() : const UnsupportedCodeGenerator(), + }); } diff --git a/packages/flutter_tools/lib/src/base/build.dart b/packages/flutter_tools/lib/src/base/build.dart index a62a353203b..7be3b8c203b 100644 --- a/packages/flutter_tools/lib/src/base/build.dart +++ b/packages/flutter_tools/lib/src/base/build.dart @@ -302,7 +302,6 @@ class AOTSnapshotter { printTrace('Extra front-end options: $extraFrontEndOptions'); final String depfilePath = fs.path.join(outputPath, 'kernel_compile.d'); - final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(); final CompilerOutput compilerOutput = await kernelCompiler.compile( sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), mainPath: mainPath, diff --git a/packages/flutter_tools/lib/src/build_runner/build_kernel_compiler.dart b/packages/flutter_tools/lib/src/build_runner/build_kernel_compiler.dart deleted file mode 100644 index d60bc8cba65..00000000000 --- a/packages/flutter_tools/lib/src/build_runner/build_kernel_compiler.dart +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import '../base/file_system.dart'; -import '../compile.dart'; -import '../globals.dart'; -import 'build_runner.dart'; - -/// An implementation of the [KernelCompiler] which delegates to build_runner. -/// -/// Only a subset of the arguments provided to the [KernelCompiler] are -/// supported here. Using the build pipeline implies a fixed multiroot -/// filesystem and requires a pubspec. -/// -/// This is only safe to use if [experimentalBuildEnabled] is true. -class BuildKernelCompiler implements KernelCompiler { - const BuildKernelCompiler(); - - @override - Future compile({ - String mainPath, - String outputFilePath, - bool linkPlatformKernelIn = false, - bool aot = false, - bool trackWidgetCreation, - List extraFrontEndOptions, - String incrementalCompilerByteStorePath, - bool targetProductVm = false, - // These arguments are currently unused. - String sdkRoot, - String packagesPath, - List fileSystemRoots, - String fileSystemScheme, - String depFilePath, - TargetModel targetModel = TargetModel.flutter, - }) async { - if (fileSystemRoots != null || fileSystemScheme != null || depFilePath != null || targetModel != null || sdkRoot != null || packagesPath != null) { - printTrace('fileSystemRoots, fileSystemScheme, depFilePath, targetModel,' - 'sdkRoot, packagesPath are not supported when using the experimental ' - 'build* pipeline'); - } - final BuildRunner buildRunner = buildRunnerFactory.create(); - try { - final BuildResult buildResult = await buildRunner.build( - aot: aot, - linkPlatformKernelIn: linkPlatformKernelIn, - trackWidgetCreation: trackWidgetCreation, - mainPath: mainPath, - targetProductVm: targetProductVm, - extraFrontEndOptions: extraFrontEndOptions - ); - final File outputFile = fs.file(outputFilePath); - if (!await outputFile.exists()) { - await outputFile.create(); - } - await outputFile.writeAsBytes(await buildResult.dillFile.readAsBytes()); - return CompilerOutput(outputFilePath, 0); - } on Exception catch (err) { - printError('Compilation Failed: $err'); - return const CompilerOutput(null, 1); - } - } -} diff --git a/packages/flutter_tools/lib/src/build_runner/build_runner.dart b/packages/flutter_tools/lib/src/build_runner/build_runner.dart index 68591c079bd..90f660fcf09 100644 --- a/packages/flutter_tools/lib/src/build_runner/build_runner.dart +++ b/packages/flutter_tools/lib/src/build_runner/build_runner.dart @@ -4,69 +4,45 @@ import 'dart:async'; +import 'package:build_daemon/data/build_target.dart'; import 'package:build_runner_core/build_runner_core.dart'; +import 'package:build_daemon/data/server_log.dart'; +import 'package:build_daemon/data/build_status.dart' as build; +import 'package:build_daemon/client.dart'; import 'package:meta/meta.dart'; +import 'package:yaml/yaml.dart'; import '../artifacts.dart'; -import '../base/context.dart'; import '../base/file_system.dart'; import '../base/io.dart'; import '../base/logger.dart'; -import '../base/platform.dart'; import '../base/process_manager.dart'; import '../cache.dart'; +import '../codegen.dart'; import '../convert.dart'; import '../dart/pub.dart'; import '../globals.dart'; import '../project.dart'; +import '../resident_runner.dart'; import 'build_script_generator.dart'; -/// The [BuildRunnerFactory] instance. -BuildRunnerFactory get buildRunnerFactory => context[BuildRunnerFactory]; - -/// Whether to attempt to build a flutter project using build* libraries. -/// -/// This requires both an experimental opt in via the environment variable -/// 'FLUTTER_EXPERIMENTAL_BUILD' and that the project itself has a -/// dependency on the package 'flutter_build' and 'build_runner.' -bool get experimentalBuildEnabled { - return _experimentalBuildEnabled ??= platform.environment['FLUTTER_EXPERIMENTAL_BUILD']?.toLowerCase() == 'true'; -} -bool _experimentalBuildEnabled; - -@visibleForTesting -set experimentalBuildEnabled(bool value) { - _experimentalBuildEnabled = value; -} - -/// An injectable factory to create instances of [BuildRunner]. -class BuildRunnerFactory { - const BuildRunnerFactory(); - - /// Creates a new [BuildRunner] instance. - BuildRunner create() { - return BuildRunner(); - } -} - /// A wrapper for a build_runner process which delegates to a generated /// build script. /// /// This is only enabled if [experimentalBuildEnabled] is true, and only for /// external flutter users. -class BuildRunner { +class BuildRunner extends CodeGenerator { + const BuildRunner(); - /// Run a build_runner build and return the resulting .packages and dill file. - /// - /// The defines of the build command are the arguments required in the - /// flutter_build kernel builder. - Future build({ + @override + Future build({ + @required String mainPath, @required bool aot, @required bool linkPlatformKernelIn, @required bool trackWidgetCreation, @required bool targetProductVm, - @required String mainPath, - @required List extraFrontEndOptions, + List extraFrontEndOptions = const [], + bool disableKernelGeneration = false, }) async { await generateBuildScript(); final FlutterProject flutterProject = await FlutterProject.current(); @@ -95,7 +71,7 @@ class BuildRunner { '--packages=$scriptPackagesPath', buildScript, 'build', - '--define', 'flutter_build|kernel=disabled=false', + '--define', 'flutter_build|kernel=disabled=$disableKernelGeneration', '--define', 'flutter_build|kernel=aot=$aot', '--define', 'flutter_build|kernel=linkPlatformKernelIn=$linkPlatformKernelIn', '--define', 'flutter_build|kernel=trackWidgetCreation=$trackWidgetCreation', @@ -121,6 +97,9 @@ class BuildRunner { } finally { status.stop(); } + if (disableKernelGeneration) { + return const CodeGenerationResult(null, null); + } /// We don't check for this above because it might be generated for the /// first time by invoking the build. final Directory dartTool = flutterProject.dartTool; @@ -143,13 +122,10 @@ class BuildRunner { if (!packagesFile.existsSync() || !dillFile.existsSync()) { throw Exception('build_runner did not produce output at expected location: ${dillFile.path} missing'); } - return BuildResult(packagesFile, dillFile); + return CodeGenerationResult(packagesFile, dillFile); } - /// Invalidates a generated build script by deleting it. - /// - /// Must be called any time a pubspec file update triggers a corresponding change - /// in .packages. + @override Future invalidateBuildScript() async { final FlutterProject flutterProject = await FlutterProject.current(); final File buildScript = flutterProject.dartTool @@ -162,8 +138,7 @@ class BuildRunner { await buildScript.delete(); } - // Generates a synthetic package under .dart_tool/flutter_tool which is in turn - // used to generate a build script. + @override Future generateBuildScript() async { final FlutterProject flutterProject = await FlutterProject.current(); final String generatedDirectory = fs.path.join(flutterProject.dartTool.path, 'flutter_tool'); @@ -180,8 +155,10 @@ class BuildRunner { stringBuffer.writeln('name: synthetic_example'); stringBuffer.writeln('dependencies:'); - for (String builder in await flutterProject.builders) { - stringBuffer.writeln(' $builder: any'); + final YamlMap builders = await flutterProject.builders; + for (String name in builders.keys) { + final YamlNode node = builders[name]; + stringBuffer.writeln(' $name: $node'); } stringBuffer.writeln(' build_runner: any'); stringBuffer.writeln(' flutter_build:'); @@ -195,17 +172,92 @@ class BuildRunner { checkLastModified: false, ); final PackageGraph packageGraph = PackageGraph.forPath(syntheticPubspec.parent.path); - final BuildScriptGenerator buildScriptGenerator = buildScriptGeneratorFactory.create(flutterProject, packageGraph); + final BuildScriptGenerator buildScriptGenerator = const BuildScriptGeneratorFactory().create(flutterProject, packageGraph); await buildScriptGenerator.generateBuildScript(); } finally { status.stop(); } } + + @override + Future daemon({ + String mainPath, + bool linkPlatformKernelIn = false, + bool targetProductVm = false, + bool trackWidgetCreation = false, + List extraFrontEndOptions = const [], + }) async { + mainPath ??= findMainDartFile(); + await generateBuildScript(); + final FlutterProject flutterProject = await FlutterProject.current(); + final String frontendServerPath = artifacts.getArtifactPath( + Artifact.frontendServerSnapshotForEngineDartSdk + ); + final String sdkRoot = artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath); + final String engineDartBinaryPath = artifacts.getArtifactPath(Artifact.engineDartBinary); + final String packagesPath = flutterProject.packagesFile.absolute.path; + final String buildScript = flutterProject + .dartTool + .childDirectory('build') + .childDirectory('entrypoint') + .childFile('build.dart') + .path; + final String scriptPackagesPath = flutterProject + .dartTool + .childDirectory('flutter_tool') + .childFile('.packages') + .path; + final String dartPath = fs.path.join(Cache.flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', 'dart'); + final Status status = logger.startProgress('starting build daemon...', timeout: null); + BuildDaemonClient buildDaemonClient; + try { + final List command = [ + dartPath, + '--packages=$scriptPackagesPath', + buildScript, + 'daemon', + '--define', 'flutter_build|kernel=disabled=false', + '--define', 'flutter_build|kernel=aot=false', + '--define', 'flutter_build|kernel=linkPlatformKernelIn=$linkPlatformKernelIn', + '--define', 'flutter_build|kernel=trackWidgetCreation=$trackWidgetCreation', + '--define', 'flutter_build|kernel=targetProductVm=$targetProductVm', + '--define', 'flutter_build|kernel=mainPath=$mainPath', + '--define', 'flutter_build|kernel=packagesPath=$packagesPath', + '--define', 'flutter_build|kernel=sdkRoot=$sdkRoot', + '--define', 'flutter_build|kernel=frontendServerPath=$frontendServerPath', + '--define', 'flutter_build|kernel=engineDartBinaryPath=$engineDartBinaryPath', + '--define', 'flutter_build|kernel=extraFrontEndOptions=${extraFrontEndOptions ?? const []}', + ]; + buildDaemonClient = await BuildDaemonClient.connect(flutterProject.directory.path, command, logHandler: (ServerLog log) => printTrace(log.toString())); + } finally { + status.stop(); + } + buildDaemonClient.registerBuildTarget(DefaultBuildTarget((DefaultBuildTargetBuilder builder) { + builder.target = flutterProject.manifest.appName; + })); + final String relativeMain = fs.path.relative(mainPath, from: flutterProject.directory.path); + final File generatedPackagesFile = fs.file(fs.path.join(flutterProject.generated.path, fs.path.setExtension(relativeMain, '.packages'))); + final File generatedDillFile = fs.file(fs.path.join(flutterProject.generated.path, fs.path.setExtension(relativeMain, '.app.dill'))); + return _BuildRunnerCodegenDaemon(buildDaemonClient, generatedPackagesFile, generatedDillFile); + } } -class BuildResult { - const BuildResult(this.packagesFile, this.dillFile); +class _BuildRunnerCodegenDaemon implements CodegenDaemon { + _BuildRunnerCodegenDaemon(this.buildDaemonClient, this.packagesFile, this.dillFile); + final BuildDaemonClient buildDaemonClient; + @override final File packagesFile; + @override final File dillFile; + + @override + Stream get buildResults => buildDaemonClient.buildResults.map((build.BuildResults results) { + return results.results.first.status == build.BuildStatus.succeeded; + }); + + @override + void startBuild() { + buildDaemonClient.startBuild(); + } } diff --git a/packages/flutter_tools/lib/src/build_runner/build_script_generator.dart b/packages/flutter_tools/lib/src/build_runner/build_script_generator.dart index 1467872ace4..cb951ca8fb8 100644 --- a/packages/flutter_tools/lib/src/build_runner/build_script_generator.dart +++ b/packages/flutter_tools/lib/src/build_runner/build_script_generator.dart @@ -12,12 +12,9 @@ import 'package:dart_style/dart_style.dart'; import 'package:graphs/graphs.dart'; import '../base/common.dart'; -import '../base/context.dart'; import '../base/file_system.dart'; import '../project.dart'; -BuildScriptGeneratorFactory get buildScriptGeneratorFactory => context[BuildScriptGeneratorFactory]; - class BuildScriptGeneratorFactory { const BuildScriptGeneratorFactory(); @@ -227,14 +224,16 @@ class BuildScriptGenerator { return refer('toNoneByDefault', 'package:build_runner_core/build_runner_core.dart') .call([]); - case AutoApply.dependents: - return refer('toDependentsOf', - 'package:build_runner_core/build_runner_core.dart') - .call([literalString(definition.package)]); + // TODO(jonahwilliams): re-enabled when we have the builders strategy fleshed out. + // case AutoApply.dependents: + // return refer('toDependentsOf', + // 'package:build_runner_core/build_runner_core.dart') + // .call([literalString(definition.package)]); case AutoApply.allPackages: return refer('toAllPackages', 'package:build_runner_core/build_runner_core.dart') .call([]); + case AutoApply.dependents: case AutoApply.rootPackage: return refer('toRoot', 'package:build_runner_core/build_runner_core.dart') .call([]); diff --git a/packages/flutter_tools/lib/src/bundle.dart b/packages/flutter_tools/lib/src/bundle.dart index 856d2196d58..2d810a8f551 100644 --- a/packages/flutter_tools/lib/src/bundle.dart +++ b/packages/flutter_tools/lib/src/bundle.dart @@ -99,7 +99,6 @@ Future build({ if ((extraFrontEndOptions != null) && extraFrontEndOptions.isNotEmpty) printTrace('Extra front-end options: $extraFrontEndOptions'); ensureDirectoryExists(applicationKernelFilePath); - final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(); final CompilerOutput compilerOutput = await kernelCompiler.compile( sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), incrementalCompilerByteStorePath: compilationTraceFilePath != null ? null : diff --git a/packages/flutter_tools/lib/src/codegen.dart b/packages/flutter_tools/lib/src/codegen.dart new file mode 100644 index 00000000000..c0b32e62894 --- /dev/null +++ b/packages/flutter_tools/lib/src/codegen.dart @@ -0,0 +1,293 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:meta/meta.dart'; + +import 'artifacts.dart'; +import 'base/context.dart'; +import 'base/file_system.dart'; +import 'base/platform.dart'; +import 'compile.dart'; +import 'globals.dart'; +import 'project.dart'; + +const String _kMultiRootScheme = 'org-dartlang-app'; + +/// The [CodeGenerator] instance. +/// +/// If [experimentalBuildEnabled] is false, this will contain an unsupported +/// implementation. +CodeGenerator get codeGenerator => context[CodeGenerator]; + +/// Whether to attempt to build a flutter project using build* libraries. +/// +/// This requires both an experimental opt in via the environment variable +/// 'FLUTTER_EXPERIMENTAL_BUILD' and that the project itself has a +/// dependency on the package 'flutter_build' and 'build_runner.' +bool get experimentalBuildEnabled { + return _experimentalBuildEnabled ??= platform.environment['FLUTTER_EXPERIMENTAL_BUILD']?.toLowerCase() == 'true'; +} +bool _experimentalBuildEnabled; + +@visibleForTesting +set experimentalBuildEnabled(bool value) { + _experimentalBuildEnabled = value; +} + +/// A wrapper for a build_runner process which delegates to a generated +/// build script. +/// +/// This is only enabled if [experimentalBuildEnabled] is true, and only for +/// external flutter users. +abstract class CodeGenerator { + const CodeGenerator(); + + /// Run a partial build include code generators but not kernel. + Future generate({@required String mainPath}) async { + await build( + mainPath: mainPath, + aot: false, + linkPlatformKernelIn: false, + trackWidgetCreation: false, + targetProductVm: false, + disableKernelGeneration: true, + ); + } + + /// Run a full build and return the resulting .packages and dill file. + /// + /// The defines of the build command are the arguments required in the + /// flutter_build kernel builder. + Future build({ + @required String mainPath, + @required bool aot, + @required bool linkPlatformKernelIn, + @required bool trackWidgetCreation, + @required bool targetProductVm, + List extraFrontEndOptions = const [], + bool disableKernelGeneration = false, + }); + + /// Starts a persistent code generting daemon. + /// + /// The defines of the daemon command are the arguments required in the + /// flutter_build kernel builder. + Future daemon({ + @required String mainPath, + bool linkPlatformKernelIn = false, + bool targetProductVm = false, + bool trackWidgetCreation = false, + List extraFrontEndOptions = const [], + }); + + /// Invalidates a generated build script by deleting it. + /// + /// Must be called any time a pubspec file update triggers a corresponding change + /// in .packages. + Future invalidateBuildScript(); + + // Generates a synthetic package under .dart_tool/flutter_tool which is in turn + // used to generate a build script. + Future generateBuildScript(); +} + +class UnsupportedCodeGenerator extends CodeGenerator { + const UnsupportedCodeGenerator(); + + @override + Future build({ + String mainPath, + bool aot, + bool linkPlatformKernelIn, + bool trackWidgetCreation, + bool targetProductVm, + List extraFrontEndOptions = const [], + bool disableKernelGeneration = false, + }) { + throw UnsupportedError('build_runner is not currently supported.'); + } + + @override + Future generateBuildScript() { + throw UnsupportedError('build_runner is not currently supported.'); + } + + @override + Future invalidateBuildScript() { + throw UnsupportedError('build_runner is not currently supported.'); + } + + @override + Future daemon({ + String mainPath, + bool linkPlatformKernelIn = false, + bool targetProductVm = false, + bool trackWidgetCreation = false, + List extraFrontEndOptions = const [], + }) { + throw UnsupportedError('build_runner is not currently supported.'); + } +} + +abstract class CodegenDaemon { + /// Whether the previously enqueued build was successful. + Stream get buildResults; + + /// Starts a new build. + void startBuild(); + + File get packagesFile; + + File get dillFile; +} + +/// The result of running a build through a [CodeGenerator]. +/// +/// If no dill or packages file is generated, they will be null. +class CodeGenerationResult { + const CodeGenerationResult(this.packagesFile, this.dillFile); + + final File packagesFile; + final File dillFile; +} + +/// An implementation of the [KernelCompiler] which delegates to build_runner. +/// +/// Only a subset of the arguments provided to the [KernelCompiler] are +/// supported here. Using the build pipeline implies a fixed multiroot +/// filesystem and requires a pubspec. +/// +/// This is only safe to use if [experimentalBuildEnabled] is true. +class CodeGeneratingKernelCompiler implements KernelCompiler { + const CodeGeneratingKernelCompiler(); + + @override + Future compile({ + String mainPath, + String outputFilePath, + bool linkPlatformKernelIn = false, + bool aot = false, + bool trackWidgetCreation, + List extraFrontEndOptions, + String incrementalCompilerByteStorePath, + bool targetProductVm = false, + // These arguments are currently unused. + String sdkRoot, + String packagesPath, + List fileSystemRoots, + String fileSystemScheme, + String depFilePath, + TargetModel targetModel = TargetModel.flutter, + }) async { + if (fileSystemRoots != null || fileSystemScheme != null || depFilePath != null || targetModel != null || sdkRoot != null || packagesPath != null) { + printTrace('fileSystemRoots, fileSystemScheme, depFilePath, targetModel,' + 'sdkRoot, packagesPath are not supported when using the experimental ' + 'build* pipeline'); + } + try { + final CodeGenerationResult buildResult = await codeGenerator.build( + aot: aot, + linkPlatformKernelIn: linkPlatformKernelIn, + trackWidgetCreation: trackWidgetCreation, + mainPath: mainPath, + targetProductVm: targetProductVm, + extraFrontEndOptions: extraFrontEndOptions + ); + final File outputFile = fs.file(outputFilePath); + if (!await outputFile.exists()) { + await outputFile.create(); + } + await outputFile.writeAsBytes(await buildResult.dillFile.readAsBytes()); + return CompilerOutput(outputFilePath, 0); + } on Exception catch (err) { + printError('Compilation Failed: $err'); + return const CompilerOutput(null, 1); + } + } +} + +/// An implementation of a [ResidentCompiler] which runs a [BuildRunner] before +/// talking to the CFE. +class CodeGeneratingResidentCompiler implements ResidentCompiler { + CodeGeneratingResidentCompiler._(this._residentCompiler, this._codegenDaemon); + + /// Creates a new [ResidentCompiler] and configures a [BuildDaemonClient] to + /// run builds. + static Future create({ + @required String mainPath, + bool trackWidgetCreation = false, + CompilerMessageConsumer compilerMessageConsumer = printError, + bool unsafePackageSerialization = false, + }) async { + final FlutterProject flutterProject = await FlutterProject.current(); + final CodegenDaemon codegenDaemon = await codeGenerator.daemon( + extraFrontEndOptions: [], + linkPlatformKernelIn: false, + mainPath: mainPath, + targetProductVm: false, + trackWidgetCreation: trackWidgetCreation, + ); + codegenDaemon.startBuild(); + await codegenDaemon.buildResults.firstWhere((bool result) => result); + final ResidentCompiler residentCompiler = ResidentCompiler( + artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), + trackWidgetCreation: trackWidgetCreation, + packagesPath: codegenDaemon.packagesFile.path, + fileSystemRoots: [ + flutterProject.generated.absolute.path, + flutterProject.directory.path, + ], + fileSystemScheme: _kMultiRootScheme, + targetModel: TargetModel.flutter, + unsafePackageSerialization: unsafePackageSerialization, + ); + return CodeGeneratingResidentCompiler._(residentCompiler, codegenDaemon); + } + + final ResidentCompiler _residentCompiler; + final CodegenDaemon _codegenDaemon; + + @override + void accept() { + _residentCompiler.accept(); + } + + @override + Future compileExpression(String expression, List definitions, List typeDefinitions, String libraryUri, String klass, bool isStatic) { + return _residentCompiler.compileExpression(expression, definitions, typeDefinitions, libraryUri, klass, isStatic); + } + + @override + Future recompile(String mainPath, List invalidatedFiles, {String outputPath, String packagesFilePath}) async { + _codegenDaemon.startBuild(); + await _codegenDaemon.buildResults.first; + // Delete this file so that the frontend_server can handle multi-root. + // TODO(jonahwilliams): investigate frontend_server behavior in the presence + // of multi-root and initialize from dill. + if (await fs.file(outputPath).exists()) { + await fs.file(outputPath).delete(); + } + return _residentCompiler.recompile( + mainPath, + invalidatedFiles, + outputPath: outputPath, + packagesFilePath: _codegenDaemon.packagesFile.path, + ); + } + + @override + Future reject() { + return _residentCompiler.reject(); + } + + @override + void reset() { + _residentCompiler.reset(); + } + + @override + Future shutdown() { + return _residentCompiler.shutdown(); + } +} diff --git a/packages/flutter_tools/lib/src/commands/generate.dart b/packages/flutter_tools/lib/src/commands/generate.dart new file mode 100644 index 00000000000..71b0a8a5149 --- /dev/null +++ b/packages/flutter_tools/lib/src/commands/generate.dart @@ -0,0 +1,30 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import '../base/common.dart'; +import '../codegen.dart'; +import '../runner/flutter_command.dart'; + +class GenerateCommand extends FlutterCommand { + GenerateCommand() { + usesTargetOption(); + } + @override + String get description => 'run code generators.'; + + @override + String get name => 'generate'; + + @override + bool get hidden => true; + + @override + Future runCommand() async { + if (!experimentalBuildEnabled) { + throwToolExit('FLUTTER_EXPERIMENTAL_BUILD is not enabled, codegen is unsupported.'); + } + await codeGenerator.generate(mainPath: argResults['target']); + return null; + } +} \ No newline at end of file diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index 1a31645b093..6c59ab07645 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart @@ -10,6 +10,8 @@ import '../base/time.dart'; import '../base/utils.dart'; import '../build_info.dart'; import '../cache.dart'; +import '../codegen.dart'; +import '../compile.dart'; import '../device.dart'; import '../globals.dart'; import '../ios/mac.dart'; @@ -345,6 +347,10 @@ class RunCommand extends RunCommandBase { expFlags = argResults[FlutterOptions.kEnableExperiment]; } + ResidentCompiler residentCompiler; + if (experimentalBuildEnabled) { + residentCompiler = await CodeGeneratingResidentCompiler.create(mainPath: argResults['target']); + } final List flutterDevices = devices.map((Device device) { return FlutterDevice( device, @@ -354,6 +360,7 @@ class RunCommand extends RunCommandBase { fileSystemScheme: argResults['filesystem-scheme'], viewFilter: argResults['isolate-filter'], experimentalFlags: expFlags, + generator: residentCompiler, ); }).toList(); diff --git a/packages/flutter_tools/lib/src/commands/update_packages.dart b/packages/flutter_tools/lib/src/commands/update_packages.dart index 07aab13a7e1..5f7c5d87384 100644 --- a/packages/flutter_tools/lib/src/commands/update_packages.dart +++ b/packages/flutter_tools/lib/src/commands/update_packages.dart @@ -390,7 +390,7 @@ class _DependencyLink { /// "dependency_overrides" sections, as well as the "name" and "version" fields /// in the pubspec header bucketed into [header]. The others are all bucketed /// into [other]. -enum Section { header, dependencies, devDependencies, dependencyOverrides, other } +enum Section { header, dependencies, devDependencies, dependencyOverrides, builders, other } /// The various kinds of dependencies we know and care about. enum DependencyKind { @@ -504,6 +504,11 @@ class PubspecYaml { seenDev = true; } result.add(header); + } else if (section == Section.builders) { + // Do nothing. + // This line isn't a section header, and we're not in a section we care about. + // We just stick the line into the output unmodified. + result.add(PubspecLine(line)); } else if (section == Section.other) { if (line.contains(kDependencyChecksum)) { // This is the pubspec checksum. After computing it, we remove it from the output data @@ -878,6 +883,8 @@ class PubspecHeader extends PubspecLine { return PubspecHeader(line, Section.devDependencies); case 'dependency_overrides': return PubspecHeader(line, Section.dependencyOverrides); + case 'builders': + return PubspecHeader(line, Section.builders); case 'name': case 'version': return PubspecHeader(line, Section.header, name: sectionName, value: value); diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart index e2479696884..8fa4ed74aad 100644 --- a/packages/flutter_tools/lib/src/compile.dart +++ b/packages/flutter_tools/lib/src/compile.dart @@ -20,21 +20,10 @@ import 'convert.dart'; import 'dart/package_map.dart'; import 'globals.dart'; -KernelCompilerFactory get kernelCompilerFactory => context[KernelCompilerFactory]; +KernelCompiler get kernelCompiler => context[KernelCompiler]; typedef CompilerMessageConsumer = void Function(String message, {bool emphasis, TerminalColor color}); -/// Injectable factory to allow async construction of a [KernelCompiler]. -class KernelCompilerFactory { - const KernelCompilerFactory(); - - /// Return the correct [KernelCompiler] instance for the given project - /// configuration. - FutureOr create() async { - return const KernelCompiler(); - } -} - /// The target model describes the set of core libraries that are availible within /// the SDK. class TargetModel { diff --git a/packages/flutter_tools/lib/src/context_runner.dart b/packages/flutter_tools/lib/src/context_runner.dart index 7a4490baf11..af2976ddc23 100644 --- a/packages/flutter_tools/lib/src/context_runner.dart +++ b/packages/flutter_tools/lib/src/context_runner.dart @@ -22,6 +22,7 @@ import 'base/time.dart'; import 'base/user_messages.dart'; import 'base/utils.dart'; import 'cache.dart'; +import 'codegen.dart'; import 'compile.dart'; import 'devfs.dart'; import 'device.dart'; @@ -80,7 +81,7 @@ Future runInContext( IOSSimulatorUtils: () => IOSSimulatorUtils(), IOSWorkflow: () => const IOSWorkflow(), IOSValidator: () => const IOSValidator(), - KernelCompilerFactory: () => const KernelCompilerFactory(), + KernelCompiler: () => experimentalBuildEnabled ? const CodeGeneratingKernelCompiler() : const KernelCompiler(), LinuxWorkflow: () => const LinuxWorkflow(), Logger: () => platform.isWindows ? WindowsStdoutLogger() : StdoutLogger(), MacOSWorkflow: () => const MacOSWorkflow(), diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart index 04e47a81ba1..dc7587c1e43 100644 --- a/packages/flutter_tools/lib/src/project.dart +++ b/packages/flutter_tools/lib/src/project.dart @@ -107,6 +107,13 @@ class FlutterProject { /// The `.dart-tool` directory of this project. Directory get dartTool => directory.childDirectory('.dart_tool'); + /// The directory containing the generated code for this project. + Directory get generated => directory + .childDirectory('.dart_tool') + .childDirectory('build') + .childDirectory('generated') + .childDirectory(manifest.appName); + /// The example sub-project of this project. FlutterProject get example => FlutterProject( _exampleDirectory(directory), @@ -147,15 +154,9 @@ class FlutterProject { } /// Return the set of builders used by this package. - Future> get builders async { + Future get builders async { final YamlMap pubspec = loadYaml(await pubspecFile.readAsString()); - final YamlList builders = pubspec['builders']; - if (builders == null) { - return []; - } - return builders.map((Object node) { - return node.toString(); - }).toList(); + return pubspec['builders']; } } diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart index 788cdee24bf..576240d153a 100644 --- a/packages/flutter_tools/lib/src/resident_runner.dart +++ b/packages/flutter_tools/lib/src/resident_runner.dart @@ -16,6 +16,7 @@ import 'base/logger.dart'; import 'base/terminal.dart'; import 'base/utils.dart'; import 'build_info.dart'; +import 'codegen.dart'; import 'compile.dart'; import 'dart/dependencies.dart'; import 'dart/package_map.dart'; @@ -895,6 +896,11 @@ abstract class ResidentRunner { } bool hasDirtyDependencies(FlutterDevice device) { + /// When using the build system, dependency analysis is handled by build + /// runner instead. + if (experimentalBuildEnabled) { + return false; + } final DartDependencySetBuilder dartDependencySetBuilder = DartDependencySetBuilder(mainPath, packagesFilePath); final DependencyChecker dependencyChecker = diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart index 27541dd3075..116768ae682 100644 --- a/packages/flutter_tools/lib/src/run_hot.dart +++ b/packages/flutter_tools/lib/src/run_hot.dart @@ -15,6 +15,7 @@ import 'base/logger.dart'; import 'base/terminal.dart'; import 'base/utils.dart'; import 'build_info.dart'; +import 'codegen.dart'; import 'compile.dart'; import 'convert.dart'; import 'dart/dependencies.dart'; @@ -122,8 +123,12 @@ class HotRunner extends ResidentRunner { return false; } - final DartDependencySetBuilder dartDependencySetBuilder = - DartDependencySetBuilder(mainPath, packagesFilePath); + /// When using the build system, dependency analysis is handled by build + /// runner instead. + if (experimentalBuildEnabled) { + return true; + } + final DartDependencySetBuilder dartDependencySetBuilder = DartDependencySetBuilder(mainPath, packagesFilePath); try { _dartDependencies = Set.from(dartDependencySetBuilder.build()); } on DartDependencyException catch (error) { diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index e3cd1e881b3..35ec21c0cef 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -39,10 +39,6 @@ dependencies: flutter_goldens_client: path: ../flutter_goldens_client - # build_runner depenencies needed for codegen. - build: 1.1.1 - build_modules: 1.0.7 - # We depend on very specific internal implementation details of the # 'test' package, which change between versions, so when upgrading # this, make sure the tests are still running correctly. @@ -53,6 +49,9 @@ dependencies: build_runner_core: 2.0.3 dart_style: 1.2.3 code_builder: 3.2.0 + build: 1.1.1 + build_modules: 1.0.7+2 + build_daemon: 0.4.0 async: 2.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" bazel_worker: 0.1.20 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -85,9 +84,12 @@ dependencies: pub_semver: 1.4.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pubspec_parse: 0.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" scratch_space: 0.0.3+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + shelf: 0.7.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + shelf_web_socket: 0.2.2+4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_map_stack_trace: 1.1.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_maps: 0.10.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.5.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + stream_transform: 0.0.15 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" string_scanner: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" term_glyph: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" timing: 0.1.1+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -106,14 +108,12 @@ dev_dependencies: mime: 0.9.6+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" multi_server_socket: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 1.4.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - shelf: 0.7.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" shelf_packages_handler: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" shelf_static: 0.2.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - shelf_web_socket: 0.2.2+4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" test: 1.5.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" dartdoc: # Exclude this package from the hosted API docs. nodoc: true -# PUBSPEC CHECKSUM: c962 +# PUBSPEC CHECKSUM: 6c3e diff --git a/packages/flutter_tools/test/build_runner/build_runner_test.dart b/packages/flutter_tools/test/build_runner/build_runner_test.dart index a69ee5f3c51..fb7575a22a6 100644 --- a/packages/flutter_tools/test/build_runner/build_runner_test.dart +++ b/packages/flutter_tools/test/build_runner/build_runner_test.dart @@ -4,7 +4,7 @@ import 'package:flutter_tools/src/base/platform.dart'; -import 'package:flutter_tools/src/build_runner/build_runner.dart'; +import 'package:flutter_tools/src/codegen.dart'; import 'package:mockito/mockito.dart'; import '../src/common.dart'; diff --git a/packages/flutter_tools/test/build_runner/build_kernel_compiler_test.dart b/packages/flutter_tools/test/build_runner/code_generating_kernel_compiler.dart similarity index 80% rename from packages/flutter_tools/test/build_runner/build_kernel_compiler_test.dart rename to packages/flutter_tools/test/build_runner/code_generating_kernel_compiler.dart index f6ccad300c4..4dbd4d558fc 100644 --- a/packages/flutter_tools/test/build_runner/build_kernel_compiler_test.dart +++ b/packages/flutter_tools/test/build_runner/code_generating_kernel_compiler.dart @@ -3,8 +3,8 @@ // found in the LICENSE file. import 'package:flutter_tools/src/base/file_system.dart'; -import 'package:flutter_tools/src/build_runner/build_kernel_compiler.dart'; import 'package:flutter_tools/src/build_runner/build_runner.dart'; +import 'package:flutter_tools/src/codegen.dart'; import 'package:flutter_tools/src/compile.dart'; import 'package:mockito/mockito.dart'; @@ -12,8 +12,7 @@ import '../src/common.dart'; import '../src/context.dart'; void main() { - group(BuildKernelCompiler, () { - final MockBuildRunnerFactory mockBuildRunnerFactory = MockBuildRunnerFactory(); + group(CodeGeneratingKernelCompiler, () { final MockBuildRunner mockBuildRunner = MockBuildRunner(); final MockFileSystem mockFileSystem = MockFileSystem(); final MockFile packagesFile = MockFile(); @@ -26,11 +25,10 @@ void main() { when(packagesFile.exists()).thenAnswer((Invocation invocation) async => true); when(dillFile.exists()).thenAnswer((Invocation invocation) async => true); when(outputFile.exists()).thenAnswer((Invocation invocation) async => true); - when(mockBuildRunnerFactory.create()).thenReturn(mockBuildRunner); when(dillFile.readAsBytes()).thenAnswer((Invocation invocation) async => [0, 1, 2, 3]); testUsingContext('delegates to build_runner', () async { - const BuildKernelCompiler kernelCompiler = BuildKernelCompiler(); + const CodeGeneratingKernelCompiler kernelCompiler = CodeGeneratingKernelCompiler(); when(mockBuildRunner.build( aot: anyNamed('aot'), extraFrontEndOptions: anyNamed('extraFrontEndOptions'), @@ -39,7 +37,7 @@ void main() { targetProductVm: anyNamed('targetProductVm'), trackWidgetCreation: anyNamed('trackWidgetCreation') )).thenAnswer((Invocation invocation) async { - return BuildResult(fs.file('.packages'), fs.file('main.app.dill')); + return CodeGenerationResult(fs.file('.packages'), fs.file('main.app.dill')); }); final CompilerOutput buildResult = await kernelCompiler.compile( outputFilePath: 'output.app.dill', @@ -48,13 +46,12 @@ void main() { expect(buildResult.errorCount, 0); verify(outputFile.writeAsBytes([0, 1, 2, 3])).called(1); }, overrides: { - BuildRunnerFactory: () => mockBuildRunnerFactory, + CodeGenerator: () => mockBuildRunner, FileSystem: () => mockFileSystem, }); }); } -class MockBuildRunnerFactory extends Mock implements BuildRunnerFactory {} class MockBuildRunner extends Mock implements BuildRunner {} class MockFileSystem extends Mock implements FileSystem {} class MockFile extends Mock implements File {} diff --git a/packages/flutter_tools/test/compile_test.dart b/packages/flutter_tools/test/compile_test.dart index 79f9d267e02..ba7ae68a6c3 100644 --- a/packages/flutter_tools/test/compile_test.dart +++ b/packages/flutter_tools/test/compile_test.dart @@ -118,7 +118,6 @@ example:org-dartlang-app:/ 'result abc\nline1\nline2\nabc /path/to/main.dart.dill 0' )) )); - final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(); final CompilerOutput output = await kernelCompiler.compile(sdkRoot: '/path/to/sdkroot', mainPath: '/path/to/main.dart', trackWidgetCreation: false, @@ -142,7 +141,6 @@ example:org-dartlang-app:/ 'result abc\nline1\nline2\nabc' )) )); - final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(); final CompilerOutput output = await kernelCompiler.compile(sdkRoot: '/path/to/sdkroot', mainPath: '/path/to/main.dart', trackWidgetCreation: false, @@ -168,7 +166,6 @@ example:org-dartlang-app:/ 'result abc\nline1\nline2\nabc' )) )); - final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(); final CompilerOutput output = await kernelCompiler.compile( sdkRoot: '/path/to/sdkroot', mainPath: '/path/to/main.dart', diff --git a/packages/flutter_tools/test/forbidden_imports_test.dart b/packages/flutter_tools/test/forbidden_imports_test.dart index 3b3c3c2e239..4a6fa029ac2 100644 --- a/packages/flutter_tools/test/forbidden_imports_test.dart +++ b/packages/flutter_tools/test/forbidden_imports_test.dart @@ -78,6 +78,34 @@ void main() { } } }); + + test('no unauthorized imports of build_runner', () { + final List whitelistedPaths = [ + fs.path.join(flutterTools, 'test', 'src', 'build_runner'), + fs.path.join(flutterTools, 'lib', 'src', 'build_runner'), + fs.path.join(flutterTools, 'lib', 'executable.dart') + ]; + bool _isNotWhitelisted(FileSystemEntity entity) => whitelistedPaths.every((String path) => !entity.path.contains(path)); + + for (String dirName in ['lib']) { + final Iterable files = fs.directory(fs.path.join(flutterTools, dirName)) + .listSync(recursive: true) + .where(_isDartFile) + .where(_isNotWhitelisted) + .map(_asFile); + for (File file in files) { + for (String line in file.readAsLinesSync()) { + if (line.startsWith(RegExp(r'import.*package:build_runner_core/build_runner_core.dart')) || + line.startsWith(RegExp(r'import.*package:build_runner/build_runner.dart')) || + line.startsWith(RegExp(r'import.*package:build_config/build_config.dart')) || + line.startsWith(RegExp(r'import.*build_runner/.*.dart'))) { + final String relativePath = fs.path.relative(file.path, from:flutterTools); + fail('$relativePath imports a build_runner package'); + } + } + } + } + }); } bool _isDartFile(FileSystemEntity entity) => entity is File && entity.path.endsWith('.dart'); diff --git a/packages/flutter_tools/test/tester/flutter_tester_test.dart b/packages/flutter_tools/test/tester/flutter_tester_test.dart index 3d9961ed070..5b7aa26d8c5 100644 --- a/packages/flutter_tools/test/tester/flutter_tester_test.dart +++ b/packages/flutter_tools/test/tester/flutter_tester_test.dart @@ -102,7 +102,6 @@ void main() { MockArtifacts mockArtifacts; MockKernelCompiler mockKernelCompiler; MockProcessManager mockProcessManager; - MockKernelCompilerFactory mockKernelCompilerFactory; MockProcess mockProcess; final Map startOverrides = { @@ -110,7 +109,7 @@ void main() { FileSystem: () => fs, Cache: () => Cache(rootOverride: fs.directory(flutterRoot)), ProcessManager: () => mockProcessManager, - KernelCompilerFactory: () => mockKernelCompilerFactory, + KernelCompiler: () => mockKernelCompiler, Artifacts: () => mockArtifacts, }; @@ -136,10 +135,6 @@ void main() { when(mockArtifacts.getArtifactPath(any)).thenReturn(artifactPath); mockKernelCompiler = MockKernelCompiler(); - mockKernelCompilerFactory = MockKernelCompilerFactory(); - when(mockKernelCompilerFactory.create()).thenAnswer((Invocation invocation) async { - return mockKernelCompiler; - }); }); testUsingContext('not debug', () async { @@ -199,4 +194,3 @@ Hello! class MockArtifacts extends Mock implements Artifacts {} class MockKernelCompiler extends Mock implements KernelCompiler {} -class MockKernelCompilerFactory extends Mock implements KernelCompilerFactory {}