Unverified Commit 393f9276 authored by Stanislav Baranov's avatar Stanislav Baranov Committed by GitHub

Reland Flutter tool support for dynamic code #20543 (#20646)

parent bcae4717
...@@ -42,6 +42,8 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -42,6 +42,8 @@ class FlutterPlugin implements Plugin<Project> {
private File debugFlutterJar private File debugFlutterJar
private File profileFlutterJar private File profileFlutterJar
private File releaseFlutterJar private File releaseFlutterJar
private File dynamicProfileFlutterJar
private File dynamicReleaseFlutterJar
private Properties readPropertiesIfExist(File propertiesFile) { private Properties readPropertiesIfExist(File propertiesFile) {
Properties result = new Properties() Properties result = new Properties()
...@@ -70,7 +72,7 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -70,7 +72,7 @@ class FlutterPlugin implements Plugin<Project> {
@Override @Override
void apply(Project project) { void apply(Project project) {
// Add a 'profile' build type // Add custom build types
project.android.buildTypes { project.android.buildTypes {
profile { profile {
initWith debug initWith debug
...@@ -78,6 +80,18 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -78,6 +80,18 @@ class FlutterPlugin implements Plugin<Project> {
matchingFallbacks = ['debug', 'release'] matchingFallbacks = ['debug', 'release']
} }
} }
dynamicProfile {
initWith debug
if (it.hasProperty('matchingFallbacks')) {
matchingFallbacks = ['debug', 'release']
}
}
dynamicRelease {
initWith debug
if (it.hasProperty('matchingFallbacks')) {
matchingFallbacks = ['debug', 'release']
}
}
} }
String flutterRootPath = resolveProperty(project, "flutter.sdk", System.env.FLUTTER_ROOT) String flutterRootPath = resolveProperty(project, "flutter.sdk", System.env.FLUTTER_ROOT)
...@@ -123,6 +137,8 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -123,6 +137,8 @@ class FlutterPlugin implements Plugin<Project> {
debugFlutterJar = baseEnginePath.resolve("android-${targetArch}").resolve("flutter.jar").toFile() debugFlutterJar = baseEnginePath.resolve("android-${targetArch}").resolve("flutter.jar").toFile()
profileFlutterJar = baseEnginePath.resolve("android-${targetArch}-profile").resolve("flutter.jar").toFile() profileFlutterJar = baseEnginePath.resolve("android-${targetArch}-profile").resolve("flutter.jar").toFile()
releaseFlutterJar = baseEnginePath.resolve("android-${targetArch}-release").resolve("flutter.jar").toFile() releaseFlutterJar = baseEnginePath.resolve("android-${targetArch}-release").resolve("flutter.jar").toFile()
dynamicProfileFlutterJar = baseEnginePath.resolve("android-${targetArch}-dynamic-profile").resolve("flutter.jar").toFile()
dynamicReleaseFlutterJar = baseEnginePath.resolve("android-${targetArch}-dynamic-release").resolve("flutter.jar").toFile()
if (!debugFlutterJar.isFile()) { if (!debugFlutterJar.isFile()) {
project.exec { project.exec {
executable flutterExecutable.absolutePath executable flutterExecutable.absolutePath
...@@ -217,6 +233,10 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -217,6 +233,10 @@ class FlutterPlugin implements Plugin<Project> {
[flutterX86Jar, debugFlutterJar] [flutterX86Jar, debugFlutterJar]
} else if (buildMode == "profile") { } else if (buildMode == "profile") {
profileFlutterJar profileFlutterJar
} else if (buildMode == "dynamicProfile") {
dynamicProfileFlutterJar
} else if (buildMode == "dynamicRelease") {
dynamicReleaseFlutterJar
} else { } else {
releaseFlutterJar releaseFlutterJar
} }
...@@ -229,11 +249,15 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -229,11 +249,15 @@ class FlutterPlugin implements Plugin<Project> {
* *
* Note: The BuildType DSL type is not public, and is therefore omitted from the signature. * Note: The BuildType DSL type is not public, and is therefore omitted from the signature.
* *
* @return "debug", "profile", or "release" (fall-back). * @return "debug", "profile", "dynamicProfile", "dynamicRelease", or "release" (fall-back).
*/ */
private static String buildModeFor(buildType) { private static String buildModeFor(buildType) {
if (buildType.name == "profile") { if (buildType.name == "profile") {
return "profile" return "profile"
} else if (buildType.name == "dynamicProfile") {
return "dynamicProfile"
} else if (buildType.name == "dynamicRelease") {
return "dynamicRelease"
} else if (buildType.debuggable) { } else if (buildType.debuggable) {
return "debug" return "debug"
} }
...@@ -409,7 +433,7 @@ abstract class BaseFlutterTask extends DefaultTask { ...@@ -409,7 +433,7 @@ abstract class BaseFlutterTask extends DefaultTask {
intermediateDir.mkdirs() intermediateDir.mkdirs()
if (buildMode != "debug") { if (buildMode == "profile" || buildMode == "release") {
project.exec { project.exec {
executable flutterExecutable.absolutePath executable flutterExecutable.absolutePath
workingDir sourceDir workingDir sourceDir
...@@ -488,7 +512,7 @@ abstract class BaseFlutterTask extends DefaultTask { ...@@ -488,7 +512,7 @@ abstract class BaseFlutterTask extends DefaultTask {
if (targetPlatform != null) { if (targetPlatform != null) {
args "--target-platform", "${targetPlatform}" args "--target-platform", "${targetPlatform}"
} }
if (buildMode != "debug") { if (buildMode == "release" || buildMode == "profile") {
args "--precompiled" args "--precompiled"
} else { } else {
args "--depfile", "${intermediateDir}/snapshot_blob.bin.d" args "--depfile", "${intermediateDir}/snapshot_blob.bin.d"
...@@ -497,7 +521,18 @@ abstract class BaseFlutterTask extends DefaultTask { ...@@ -497,7 +521,18 @@ abstract class BaseFlutterTask extends DefaultTask {
} }
} }
args "--asset-dir", "${intermediateDir}/flutter_assets" args "--asset-dir", "${intermediateDir}/flutter_assets"
args "--${buildMode}" if (buildMode == "debug") {
args "--debug"
}
if (buildMode == "profile" || buildMode == "dynamicProfile") {
args "--profile"
}
if (buildMode == "release" || buildMode == "dynamicRelease") {
args "--release"
}
if (buildMode == "dynamicProfile" || buildMode == "dynamicRelease") {
args "--dynamic"
}
} }
} }
} }
......
...@@ -415,12 +415,13 @@ File _findApkFile(GradleProject project, BuildInfo buildInfo) { ...@@ -415,12 +415,13 @@ File _findApkFile(GradleProject project, BuildInfo buildInfo) {
File apkFile = fs.file(fs.path.join(project.apkDirectory.path, apkFileName)); File apkFile = fs.file(fs.path.join(project.apkDirectory.path, apkFileName));
if (apkFile.existsSync()) if (apkFile.existsSync())
return apkFile; return apkFile;
apkFile = fs.file(fs.path.join(project.apkDirectory.path, buildInfo.modeName, apkFileName)); final String modeName = camelCase(buildInfo.modeName);
apkFile = fs.file(fs.path.join(project.apkDirectory.path, modeName, apkFileName));
if (apkFile.existsSync()) if (apkFile.existsSync())
return apkFile; return apkFile;
if (buildInfo.flavor != null) { if (buildInfo.flavor != null) {
// Android Studio Gradle plugin v3 adds flavor to path. // Android Studio Gradle plugin v3 adds flavor to path.
apkFile = fs.file(fs.path.join(project.apkDirectory.path, buildInfo.flavor, buildInfo.modeName, apkFileName)); apkFile = fs.file(fs.path.join(project.apkDirectory.path, buildInfo.flavor, modeName, apkFileName));
if (apkFile.existsSync()) if (apkFile.existsSync())
return apkFile; return apkFile;
} }
...@@ -484,8 +485,9 @@ class GradleProject { ...@@ -484,8 +485,9 @@ class GradleProject {
final Directory apkDirectory; final Directory apkDirectory;
String _buildTypeFor(BuildInfo buildInfo) { String _buildTypeFor(BuildInfo buildInfo) {
if (buildTypes.contains(buildInfo.modeName)) final String modeName = camelCase(buildInfo.modeName);
return buildInfo.modeName; if (buildTypes.contains(modeName.toLowerCase()))
return modeName;
return null; return null;
} }
......
...@@ -8,6 +8,7 @@ import 'base/context.dart'; ...@@ -8,6 +8,7 @@ import 'base/context.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
import 'base/platform.dart'; import 'base/platform.dart';
import 'base/process_manager.dart'; import 'base/process_manager.dart';
import 'base/utils.dart';
import 'build_info.dart'; import 'build_info.dart';
import 'dart/sdk.dart'; import 'dart/sdk.dart';
import 'globals.dart'; import 'globals.dart';
...@@ -218,7 +219,7 @@ class CachedArtifacts extends Artifacts { ...@@ -218,7 +219,7 @@ class CachedArtifacts extends Artifacts {
case TargetPlatform.android_x64: case TargetPlatform.android_x64:
case TargetPlatform.android_x86: case TargetPlatform.android_x86:
assert(mode != null, 'Need to specify a build mode for platform $platform.'); assert(mode != null, 'Need to specify a build mode for platform $platform.');
final String suffix = mode != BuildMode.debug ? '-${getModeName(mode)}' : ''; final String suffix = mode != BuildMode.debug ? '-${snakeCase(getModeName(mode), '-')}' : '';
return fs.path.join(engineDir, platformName + suffix); return fs.path.join(engineDir, platformName + suffix);
} }
assert(false, 'Invalid platform $platform.'); assert(false, 'Invalid platform $platform.');
......
...@@ -75,6 +75,14 @@ String camelCase(String str) { ...@@ -75,6 +75,14 @@ String camelCase(String str) {
return str; return str;
} }
final RegExp _upperRegex = new RegExp(r'[A-Z]');
/// Convert `fooBar` to `foo_bar`.
String snakeCase(String str, [String sep = '_']) {
return str.replaceAllMapped(_upperRegex,
(Match m) => '${m.start == 0 ? '' : sep}${m[0].toLowerCase()}');
}
String toTitleCase(String str) { String toTitleCase(String str) {
if (str.isEmpty) if (str.isEmpty)
return str; return str;
......
...@@ -74,6 +74,8 @@ class BuildInfo { ...@@ -74,6 +74,8 @@ class BuildInfo {
static const BuildInfo debug = BuildInfo(BuildMode.debug, null); static const BuildInfo debug = BuildInfo(BuildMode.debug, null);
static const BuildInfo profile = BuildInfo(BuildMode.profile, null); static const BuildInfo profile = BuildInfo(BuildMode.profile, null);
static const BuildInfo release = BuildInfo(BuildMode.release, null); static const BuildInfo release = BuildInfo(BuildMode.release, null);
static const BuildInfo dynamicProfile = BuildInfo(BuildMode.dynamicProfile, null);
static const BuildInfo dynamicRelease = BuildInfo(BuildMode.dynamicRelease, null);
/// Returns whether a debug build is requested. /// Returns whether a debug build is requested.
/// ///
...@@ -83,12 +85,12 @@ class BuildInfo { ...@@ -83,12 +85,12 @@ class BuildInfo {
/// Returns whether a profile build is requested. /// Returns whether a profile build is requested.
/// ///
/// Exactly one of [isDebug], [isProfile], or [isRelease] is true. /// Exactly one of [isDebug], [isProfile], or [isRelease] is true.
bool get isProfile => mode == BuildMode.profile; bool get isProfile => mode == BuildMode.profile || mode == BuildMode.dynamicProfile;
/// Returns whether a release build is requested. /// Returns whether a release build is requested.
/// ///
/// Exactly one of [isDebug], [isProfile], or [isRelease] is true. /// Exactly one of [isDebug], [isProfile], or [isRelease] is true.
bool get isRelease => mode == BuildMode.release; bool get isRelease => mode == BuildMode.release || mode == BuildMode.dynamicRelease;
bool get usesAot => isAotBuildMode(mode); bool get usesAot => isAotBuildMode(mode);
bool get supportsEmulator => isEmulatorBuildMode(mode); bool get supportsEmulator => isEmulatorBuildMode(mode);
...@@ -106,25 +108,17 @@ class BuildInfo { ...@@ -106,25 +108,17 @@ class BuildInfo {
targetPlatform: targetPlatform); targetPlatform: targetPlatform);
} }
/// The type of build - `debug`, `profile`, or `release`. /// The type of build.
enum BuildMode { enum BuildMode {
debug, debug,
profile, profile,
release release,
dynamicProfile,
dynamicRelease
} }
String getModeName(BuildMode mode) => getEnumName(mode); String getModeName(BuildMode mode) => getEnumName(mode);
BuildMode getBuildModeForName(String mode) {
if (mode == 'debug')
return BuildMode.debug;
if (mode == 'profile')
return BuildMode.profile;
if (mode == 'release')
return BuildMode.release;
return null;
}
// Returns true if the selected build mode uses ahead-of-time compilation. // Returns true if the selected build mode uses ahead-of-time compilation.
bool isAotBuildMode(BuildMode mode) { bool isAotBuildMode(BuildMode mode) {
return mode == BuildMode.profile || mode == BuildMode.release; return mode == BuildMode.profile || mode == BuildMode.release;
......
...@@ -115,8 +115,7 @@ Future<void> build({ ...@@ -115,8 +115,7 @@ Future<void> build({
extraGenSnapshotOptions: extraGenSnapshotOptions, extraGenSnapshotOptions: extraGenSnapshotOptions,
); );
if (snapshotExitCode != 0) { if (snapshotExitCode != 0) {
printError('Snapshotting exited with non-zero exit code: $snapshotExitCode'); throwToolExit('Snapshotting exited with non-zero exit code: $snapshotExitCode');
return;
} }
} }
} }
......
...@@ -403,6 +403,10 @@ class FlutterEngine extends CachedArtifact { ...@@ -403,6 +403,10 @@ class FlutterEngine extends CachedArtifact {
<String>['android-arm-release/linux-x64', 'android-arm-release/linux-x64.zip'], <String>['android-arm-release/linux-x64', 'android-arm-release/linux-x64.zip'],
<String>['android-arm64-profile/linux-x64', 'android-arm64-profile/linux-x64.zip'], <String>['android-arm64-profile/linux-x64', 'android-arm64-profile/linux-x64.zip'],
<String>['android-arm64-release/linux-x64', 'android-arm64-release/linux-x64.zip'], <String>['android-arm64-release/linux-x64', 'android-arm64-release/linux-x64.zip'],
<String>['android-arm-dynamic-profile/linux-x64', 'android-arm-dynamic-profile/linux-x64.zip'],
<String>['android-arm-dynamic-release/linux-x64', 'android-arm-dynamic-release/linux-x64.zip'],
<String>['android-arm64-dynamic-profile/linux-x64', 'android-arm64-dynamic-profile/linux-x64.zip'],
<String>['android-arm64-dynamic-release/linux-x64', 'android-arm64-dynamic-release/linux-x64.zip'],
]; ];
List<List<String>> get _windowsBinaryDirs => <List<String>>[ List<List<String>> get _windowsBinaryDirs => <List<String>>[
...@@ -422,6 +426,10 @@ class FlutterEngine extends CachedArtifact { ...@@ -422,6 +426,10 @@ class FlutterEngine extends CachedArtifact {
<String>['android-arm64', 'android-arm64/artifacts.zip'], <String>['android-arm64', 'android-arm64/artifacts.zip'],
<String>['android-arm64-profile', 'android-arm64-profile/artifacts.zip'], <String>['android-arm64-profile', 'android-arm64-profile/artifacts.zip'],
<String>['android-arm64-release', 'android-arm64-release/artifacts.zip'], <String>['android-arm64-release', 'android-arm64-release/artifacts.zip'],
<String>['android-arm-dynamic-profile', 'android-arm-dynamic-profile/artifacts.zip'],
<String>['android-arm-dynamic-release', 'android-arm-dynamic-release/artifacts.zip'],
<String>['android-arm64-dynamic-profile', 'android-arm64-dynamic-profile/artifacts.zip'],
<String>['android-arm64-dynamic-release', 'android-arm64-dynamic-release/artifacts.zip'],
]; ];
List<List<String>> get _iosBinaryDirs => <List<String>>[ List<List<String>> get _iosBinaryDirs => <List<String>>[
......
...@@ -21,8 +21,8 @@ import 'daemon.dart'; ...@@ -21,8 +21,8 @@ import 'daemon.dart';
abstract class RunCommandBase extends FlutterCommand { abstract class RunCommandBase extends FlutterCommand {
// Used by run and drive commands. // Used by run and drive commands.
RunCommandBase() { RunCommandBase({ bool verboseHelp = false }) {
addBuildModeFlags(defaultToRelease: false); addBuildModeFlags(defaultToRelease: false, verboseHelp: verboseHelp);
usesFlavorOption(); usesFlavorOption();
argParser argParser
..addFlag('trace-startup', ..addFlag('trace-startup',
...@@ -78,7 +78,7 @@ class RunCommand extends RunCommandBase { ...@@ -78,7 +78,7 @@ class RunCommand extends RunCommandBase {
@override @override
final String description = 'Run your Flutter app on an attached device.'; final String description = 'Run your Flutter app on an attached device.';
RunCommand({ bool verboseHelp = false }) { RunCommand({ bool verboseHelp = false }) : super(verboseHelp: verboseHelp) {
requiresPubspecYaml(); requiresPubspecYaml();
argParser argParser
......
...@@ -101,6 +101,7 @@ class KernelCompiler { ...@@ -101,6 +101,7 @@ class KernelCompiler {
properties: <String, String>{ properties: <String, String>{
'entryPoint': mainPath, 'entryPoint': mainPath,
'trackWidgetCreation': trackWidgetCreation.toString(), 'trackWidgetCreation': trackWidgetCreation.toString(),
'linkPlatformKernelIn': linkPlatformKernelIn.toString(),
}, },
depfilePaths: <String>[depFilePath], depfilePaths: <String>[depFilePath],
pathFilter: (String path) => !path.startsWith('/b/build/slave/'), pathFilter: (String path) => !path.startsWith('/b/build/slave/'),
......
...@@ -141,7 +141,7 @@ abstract class FlutterCommand extends Command<Null> { ...@@ -141,7 +141,7 @@ abstract class FlutterCommand extends Command<Null> {
valueHelp: 'x.y.z'); valueHelp: 'x.y.z');
} }
void addBuildModeFlags({bool defaultToRelease = true}) { void addBuildModeFlags({bool defaultToRelease = true, bool verboseHelp = false}) {
defaultBuildMode = defaultToRelease ? BuildMode.release : BuildMode.debug; defaultBuildMode = defaultToRelease ? BuildMode.release : BuildMode.debug;
argParser.addFlag('debug', argParser.addFlag('debug',
...@@ -153,6 +153,11 @@ abstract class FlutterCommand extends Command<Null> { ...@@ -153,6 +153,11 @@ abstract class FlutterCommand extends Command<Null> {
argParser.addFlag('release', argParser.addFlag('release',
negatable: false, negatable: false,
help: 'Build a release version of your app${defaultToRelease ? ' (default mode)' : ''}.'); help: 'Build a release version of your app${defaultToRelease ? ' (default mode)' : ''}.');
argParser.addFlag('dynamic',
hide: !verboseHelp,
negatable: false,
help: 'Enable dynamic code. This flag is intended for use with\n'
'--release or --profile; --debug always has this enabled.');
} }
set defaultBuildMode(BuildMode value) { set defaultBuildMode(BuildMode value) {
...@@ -163,12 +168,15 @@ abstract class FlutterCommand extends Command<Null> { ...@@ -163,12 +168,15 @@ abstract class FlutterCommand extends Command<Null> {
final List<bool> modeFlags = <bool>[argResults['debug'], argResults['profile'], argResults['release']]; final List<bool> modeFlags = <bool>[argResults['debug'], argResults['profile'], argResults['release']];
if (modeFlags.where((bool flag) => flag).length > 1) if (modeFlags.where((bool flag) => flag).length > 1)
throw new UsageException('Only one of --debug, --profile, or --release can be specified.', null); throw new UsageException('Only one of --debug, --profile, or --release can be specified.', null);
final bool dynamicFlag = argParser.options.containsKey('dynamic')
? argResults['dynamic']
: false;
if (argResults['debug']) if (argResults['debug'])
return BuildMode.debug; return BuildMode.debug;
if (argResults['profile']) if (argResults['profile'])
return BuildMode.profile; return dynamicFlag ? BuildMode.dynamicProfile : BuildMode.profile;
if (argResults['release']) if (argResults['release'])
return BuildMode.release; return dynamicFlag ? BuildMode.dynamicRelease : BuildMode.release;
return _defaultBuildMode; return _defaultBuildMode;
} }
......
...@@ -166,4 +166,17 @@ baz=qux ...@@ -166,4 +166,17 @@ baz=qux
expect(duration, greaterThanOrEqualTo(new Duration(milliseconds: kShortDelay.inMilliseconds * 2))); expect(duration, greaterThanOrEqualTo(new Duration(milliseconds: kShortDelay.inMilliseconds * 2)));
}); });
}); });
group('Misc', () {
test('snakeCase', () async {
expect(snakeCase('abc'), equals('abc'));
expect(snakeCase('abC'), equals('ab_c'));
expect(snakeCase('aBc'), equals('a_bc'));
expect(snakeCase('aBC'), equals('a_b_c'));
expect(snakeCase('Abc'), equals('abc'));
expect(snakeCase('AbC'), equals('ab_c'));
expect(snakeCase('ABc'), equals('a_bc'));
expect(snakeCase('ABC'), equals('a_b_c'));
});
});
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment